我试图在编译时使用宏计算常量C字符串的哈希。这是我的示例代码:
#include <stddef.h>
#include <stdint.h>
typedef uint32_t hash_t;
#define hash_cstr(s) ({
typeof(sizeof(s)) i = 0;
hash_t h = 5381;
for (; i < sizeof(s) - 1; )
h = h * 33 + s[i++];
h;
})
/* tests */
#include <stdio.h>
int main() {
#define test(s) printf("The djb2 hash of " #s " is a %un", hash_cstr(#s))
test(POST);
test(/path/to/file);
test(Content-Length);
}
现在我运行GCC来显示列表:
arm-none-eabi-gcc-4.8 -S -O2 -funroll-loops -o hash_test.S hash_test.c
结果正如预期的那样:所有字符串都被删除,取而代之的是它的散列。但通常我使用-Os来编译嵌入式应用程序的代码。当我尝试这样做时,我只为少于四个字符的字符串提供哈希。我还尝试设置参数max-unroll-times
并使用GCC 4.9:
arm-none-eabi-gcc-4.9 -S -Os -funroll-loops
--param max-unroll-times=128 -o hash_test.S hash_test.c
我无法理解这种行为的原因,也无法理解如何扩展四个字符的限制。
我建议将相关代码放在一个单独的文件中,并使用-O2
(而不是-Os
)编译该文件。或者放置一个特定于函数的杂注,如
#pragma GCC optimize ("-O2")
在函数之前,或者使用类似__attribute__((optimize("02")))
的函数属性(并且pure
属性可能也相关)
你可能对__builtin_constant_p
感兴趣。
我会让你的哈希代码成为一些static inline
函数(可能带有always_inline
函数属性),例如
static inline hash_t hashfun(const char*s) {
hash_t h = 5381;
for (const char* p = s; *p; p++)
h = h * 33 + *p;
return h;
}
一个更可移植(也不那么脆弱)的替代方案是更改构建过程,以生成一些包含之类内容的C文件(例如,使用简单的awk
或python
脚本,甚至是特定的C程序)
const char str1[]="POST";
hash_t hash1=2089437419; // the hash code of str1
不要忘记.c
或.h
文件可以由其他东西生成(您只需要在Makefile
中添加一些规则即可生成它们);如果你的老板对此感到不安,那就给他看元编程维基页面。
我似乎找到了一个受长度限制的变通方法。它看起来像是一个肮脏的破解,但与任何GCC工具链都能正常工作。
#define _hash_cstr_4(s, o)
for (; i < ((o + 4) < sizeof(s) - 1 ?
(o + 4) : sizeof(s) - 1); )
h = h * 33 + s[i++]
#define _hash_cstr_16(s, o)
_hash_cstr_4(s, o);
_hash_cstr_4(s, o + 4);
_hash_cstr_4(s, o + 8);
_hash_cstr_4(s, o + 12)
#define _hash_cstr_64(s, o)
_hash_cstr_16(s, o);
_hash_cstr_16(s, o + 16);
_hash_cstr_16(s, o + 32);
_hash_cstr_16(s, o + 48)
#define _hash_cstr_256(s, o)
_hash_cstr_64(s, o);
_hash_cstr_64(s, o + 64);
_hash_cstr_64(s, o + 128);
_hash_cstr_64(s, o + 192)
#define hash_cstr(s) ({
typeof(sizeof(s)) i = 0;
hash_t h = 5381;
if (sizeof(s) - 1 < 256) {
_hash_cstr_256(s, 0);
} else
for (; i < sizeof(s) - 1; )
h = h * 33 + s[i++];
h;
})
当散列字符串的长度小于256个字符时,它在编译时计算散列,否则在运行时计算散列。
此解决方案不需要对编译器进行额外的调整。它也适用于-Os和-O1。
如果C++被降级,那么给模板函数一个通道,比如:
template<int I>
hash_t hash_rec(const char* str, hash_t h) {
if( I > 0 ) {
return hash_rec<I-1>(str, h * 33 + str[I-1]);
} else {
return h;
}
}
#define hash(str) hash_rec<sizeof(str)>(str, 5381)
h = hash(str);