Skip to content

hash: Implement xxHash #6524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions ext/hash/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,17 @@ else
fi

PHP_ADD_BUILD_DIR(ext/hash/murmur, 1)
PHP_HASH_CFLAGS="$PHP_HASH_CFLAGS -I@ext_srcdir@/xxhash"

EXT_HASH_SOURCES="hash.c hash_md.c hash_sha.c hash_ripemd.c hash_haval.c \
hash_tiger.c hash_gost.c hash_snefru.c hash_whirlpool.c hash_adler32.c \
hash_crc32.c hash_fnv.c hash_joaat.c $EXT_HASH_SHA3_SOURCES
murmur/PMurHash.c murmur/PMurHash128.c hash_murmur.c"
murmur/PMurHash.c murmur/PMurHash128.c hash_murmur.c hash_xxhash.c"
EXT_HASH_HEADERS="php_hash.h php_hash_md.h php_hash_sha.h php_hash_ripemd.h \
php_hash_haval.h php_hash_tiger.h php_hash_gost.h php_hash_snefru.h \
php_hash_whirlpool.h php_hash_adler32.h php_hash_crc32.h \
php_hash_fnv.h php_hash_joaat.h php_hash_sha3.h php_hash_murmur.h"
php_hash_fnv.h php_hash_joaat.h php_hash_sha3.h php_hash_murmur.h \
php_hash_xxhash.h"

PHP_NEW_EXTENSION(hash, $EXT_HASH_SOURCES, 0,,$PHP_HASH_CFLAGS)
PHP_INSTALL_HEADERS(ext/hash, $EXT_HASH_HEADERS)
9 changes: 7 additions & 2 deletions ext/hash/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PHP_HASH = 'yes';
EXTENSION('hash', 'hash.c hash_md.c hash_sha.c hash_ripemd.c hash_haval.c ' +
'hash_tiger.c hash_gost.c hash_snefru.c hash_whirlpool.c ' +
'hash_adler32.c hash_crc32.c hash_joaat.c hash_fnv.c ' +
'hash_sha3.c hash_murmur.c', false);
'hash_sha3.c hash_murmur.c hash_xxhash.c', false);

var hash_sha3_dir = 'ext/hash/sha3/generic' + (X64 ? '64' : '32') + 'lc';

Expand All @@ -34,8 +34,13 @@ if (!CHECK_HEADER_ADD_INCLUDE('PMurHash.h', 'CFLAGS_HASH', hash_murmur_dir)) {
}
ADD_SOURCES(hash_murmur_dir, 'PMurHash.c PMurHash128.c', 'hash');

var hash_xxhash_dir = 'ext/hash/xxhash';
if (!CHECK_HEADER_ADD_INCLUDE('xxhash.h', 'CFLAGS_HASH', hash_xxhash_dir)) {
ERROR('Unable to locate xxhash headers');
}

PHP_INSTALL_HEADERS('ext/hash/', 'php_hash.h php_hash_md.h php_hash_sha.h ' +
'php_hash_ripemd.h php_hash_haval.h php_hash_tiger.h ' +
'php_hash_gost.h php_hash_snefru.h php_hash_whirlpool.h ' +
'php_hash_adler32.h php_hash_crc32.h php_hash_sha3.h ' +
'php_hash_murmur.h');
'php_hash_murmur.h php_hash_xxhash.h');
10 changes: 9 additions & 1 deletion ext/hash/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct mhash_bc_entry {
int value;
};

#define MHASH_NUM_ALGOS 38
#define MHASH_NUM_ALGOS 42

static struct mhash_bc_entry mhash_to_hash[MHASH_NUM_ALGOS] = {
{"CRC32", "crc32", 0}, /* used by bzip */
Expand Down Expand Up @@ -93,6 +93,10 @@ static struct mhash_bc_entry mhash_to_hash[MHASH_NUM_ALGOS] = {
{"MURMUR3A", "murmur3a", 35},
{"MURMUR3C", "murmur3c", 36},
{"MURMUR3F", "murmur3f", 37},
{"XXH32", "xxh32", 38},
{"XXH64", "xxh64", 39},
{"XXH3", "xxh3", 40},
{"XXH128", "xxh128", 41},
};
#endif

Expand Down Expand Up @@ -1598,6 +1602,10 @@ PHP_MINIT_FUNCTION(hash)
php_hash_register_algo("murmur3a", &php_hash_murmur3a_ops);
php_hash_register_algo("murmur3c", &php_hash_murmur3c_ops);
php_hash_register_algo("murmur3f", &php_hash_murmur3f_ops);
php_hash_register_algo("xxh32", &php_hash_xxh32_ops);
php_hash_register_algo("xxh64", &php_hash_xxh64_ops);
php_hash_register_algo("xxh3", &php_hash_xxh3_64_ops);
php_hash_register_algo("xxh128", &php_hash_xxh3_128_ops);

PHP_HASH_HAVAL_REGISTER(3,128);
PHP_HASH_HAVAL_REGISTER(3,160);
Expand Down
255 changes: 255 additions & 0 deletions ext/hash/hash_xxhash.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Anatol Belski <ab@php.net> |
+----------------------------------------------------------------------+
*/

#include "php_hash.h"
#include "php_hash_xxhash.h"

const php_hash_ops php_hash_xxh32_ops = {
"xxh32",
(php_hash_init_func_t) PHP_XXH32Init,
(php_hash_update_func_t) PHP_XXH32Update,
(php_hash_final_func_t) PHP_XXH32Final,
(php_hash_copy_func_t) PHP_XXH32Copy,
php_hash_serialize,
php_hash_unserialize,
PHP_XXH32_SPEC,
4,
4,
sizeof(PHP_XXH32_CTX),
0
};

PHP_HASH_API void PHP_XXH32Init(PHP_XXH32_CTX *ctx, HashTable *args)
{
/* XXH32_createState() is not used intentionally. */
memset(&ctx->s, 0, sizeof ctx->s);

if (args) {
zval *seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
/* This might be a bit too restrictive, but thinking that a seed might be set
once and for all, it should be done a clean way. */
if (seed && IS_LONG == Z_TYPE_P(seed)) {
XXH32_reset(&ctx->s, (XXH32_hash_t)Z_LVAL_P(seed));
} else {
XXH32_reset(&ctx->s, 0);
}
} else {
XXH32_reset(&ctx->s, 0);
}
}

PHP_HASH_API void PHP_XXH32Update(PHP_XXH32_CTX *ctx, const unsigned char *in, size_t len)
{
XXH32_update(&ctx->s, in, len);
}

PHP_HASH_API void PHP_XXH32Final(unsigned char digest[4], PHP_XXH32_CTX *ctx)
{
XXH32_hash_t const h = XXH32_digest(&ctx->s);

digest[0] = (unsigned char)((h >> 24) & 0xff);
digest[1] = (unsigned char)((h >> 16) & 0xff);
digest[2] = (unsigned char)((h >> 8) & 0xff);
digest[3] = (unsigned char)(h & 0xff);
}

PHP_HASH_API int PHP_XXH32Copy(const php_hash_ops *ops, PHP_XXH32_CTX *orig_context, PHP_XXH32_CTX *copy_context)
{
copy_context->s = orig_context->s;
return SUCCESS;
}

const php_hash_ops php_hash_xxh64_ops = {
"xxh64",
(php_hash_init_func_t) PHP_XXH64Init,
(php_hash_update_func_t) PHP_XXH64Update,
(php_hash_final_func_t) PHP_XXH64Final,
(php_hash_copy_func_t) PHP_XXH64Copy,
php_hash_serialize,
php_hash_unserialize,
PHP_XXH64_SPEC,
8,
8,
sizeof(PHP_XXH64_CTX),
0
};

PHP_HASH_API void PHP_XXH64Init(PHP_XXH64_CTX *ctx, HashTable *args)
{
/* XXH64_createState() is not used intentionally. */
memset(&ctx->s, 0, sizeof ctx->s);

if (args) {
zval *seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
/* This might be a bit too restrictive, but thinking that a seed might be set
once and for all, it should be done a clean way. */
if (seed && IS_LONG == Z_TYPE_P(seed)) {
XXH64_reset(&ctx->s, (XXH64_hash_t)Z_LVAL_P(seed));
} else {
XXH64_reset(&ctx->s, 0);
}
} else {
XXH64_reset(&ctx->s, 0);
}
}

PHP_HASH_API void PHP_XXH64Update(PHP_XXH64_CTX *ctx, const unsigned char *in, size_t len)
{
XXH64_update(&ctx->s, in, len);
}

PHP_HASH_API void PHP_XXH64Final(unsigned char digest[8], PHP_XXH64_CTX *ctx)
{
XXH64_hash_t const h = XXH64_digest(&ctx->s);

digest[0] = (unsigned char)((h >> 56) & 0xff);
digest[1] = (unsigned char)((h >> 48) & 0xff);
digest[2] = (unsigned char)((h >> 40) & 0xff);
digest[3] = (unsigned char)((h >> 32) & 0xff);
digest[4] = (unsigned char)((h >> 24) & 0xff);
digest[5] = (unsigned char)((h >> 16) & 0xff);
digest[6] = (unsigned char)((h >> 8) & 0xff);
digest[7] = (unsigned char)(h & 0xff);
}

PHP_HASH_API int PHP_XXH64Copy(const php_hash_ops *ops, PHP_XXH64_CTX *orig_context, PHP_XXH64_CTX *copy_context)
{
copy_context->s = orig_context->s;
return SUCCESS;
}

const php_hash_ops php_hash_xxh3_64_ops = {
"xxh3",
(php_hash_init_func_t) PHP_XXH3_64_Init,
(php_hash_update_func_t) PHP_XXH3_64_Update,
(php_hash_final_func_t) PHP_XXH3_64_Final,
(php_hash_copy_func_t) PHP_XXH3_64_Copy,
php_hash_serialize,
php_hash_unserialize,
NULL,
8,
8,
sizeof(PHP_XXH3_64_CTX),
0
};

PHP_HASH_API void PHP_XXH3_64_Init(PHP_XXH3_64_CTX *ctx, HashTable *args)
{
/* TODO integrate also XXH3_64bits_reset_withSecret(). */
XXH64_hash_t seed = 0;

if (args) {
zval *_seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
/* This might be a bit too restrictive, but thinking that a seed might be set
once and for all, it should be done a clean way. */
if (_seed && IS_LONG == Z_TYPE_P(_seed)) {
seed = (XXH64_hash_t)Z_LVAL_P(_seed);
}
}

XXH3_64bits_reset_withSeed(&ctx->s, seed);
}

PHP_HASH_API void PHP_XXH3_64_Update(PHP_XXH3_64_CTX *ctx, const unsigned char *in, size_t len)
{
XXH3_64bits_update(&ctx->s, in, len);
}

PHP_HASH_API void PHP_XXH3_64_Final(unsigned char digest[8], PHP_XXH3_64_CTX *ctx)
{
XXH64_hash_t const h = XXH3_64bits_digest(&ctx->s);

digest[0] = (unsigned char)((h >> 56) & 0xff);
digest[1] = (unsigned char)((h >> 48) & 0xff);
digest[2] = (unsigned char)((h >> 40) & 0xff);
digest[3] = (unsigned char)((h >> 32) & 0xff);
digest[4] = (unsigned char)((h >> 24) & 0xff);
digest[5] = (unsigned char)((h >> 16) & 0xff);
digest[6] = (unsigned char)((h >> 8) & 0xff);
digest[7] = (unsigned char)(h & 0xff);
}

PHP_HASH_API int PHP_XXH3_64_Copy(const php_hash_ops *ops, PHP_XXH3_64_CTX *orig_context, PHP_XXH3_64_CTX *copy_context)
{
copy_context->s = orig_context->s;
return SUCCESS;
}

const php_hash_ops php_hash_xxh3_128_ops = {
"xxh128",
(php_hash_init_func_t) PHP_XXH3_128_Init,
(php_hash_update_func_t) PHP_XXH3_128_Update,
(php_hash_final_func_t) PHP_XXH3_128_Final,
(php_hash_copy_func_t) PHP_XXH3_128_Copy,
php_hash_serialize,
php_hash_unserialize,
NULL,
16,
8,
sizeof(PHP_XXH3_128_CTX),
0
};

PHP_HASH_API void PHP_XXH3_128_Init(PHP_XXH3_128_CTX *ctx, HashTable *args)
{
/* TODO integrate also XXH3_128__64bits_reset_withSecret(). */
XXH64_hash_t seed = 0;

if (args) {
zval *_seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
/* This might be a bit too restrictive, but thinking that a seed might be set
once and for all, it should be done a clean way. */
if (_seed && IS_LONG == Z_TYPE_P(_seed)) {
seed = (XXH64_hash_t)Z_LVAL_P(_seed);
}
}

XXH3_128bits_reset_withSeed(&ctx->s, seed);
}

PHP_HASH_API void PHP_XXH3_128_Update(PHP_XXH3_128_CTX *ctx, const unsigned char *in, size_t len)
{
XXH3_128bits_update(&ctx->s, in, len);
}

PHP_HASH_API void PHP_XXH3_128_Final(unsigned char digest[16], PHP_XXH3_128_CTX *ctx)
{
XXH128_hash_t const h = XXH3_128bits_digest(&ctx->s);

digest[0] = (unsigned char)((h.high64 >> 56) & 0xff);
digest[1] = (unsigned char)((h.high64 >> 48) & 0xff);
digest[2] = (unsigned char)((h.high64 >> 40) & 0xff);
digest[3] = (unsigned char)((h.high64 >> 32) & 0xff);
digest[4] = (unsigned char)((h.high64 >> 24) & 0xff);
digest[5] = (unsigned char)((h.high64 >> 16) & 0xff);
digest[6] = (unsigned char)((h.high64 >> 8) & 0xff);
digest[7] = (unsigned char)(h.high64 & 0xff);
digest[8] = (unsigned char)((h.low64 >> 56) & 0xff);
digest[9] = (unsigned char)((h.low64 >> 48) & 0xff);
digest[10] = (unsigned char)((h.low64 >> 40) & 0xff);
digest[11] = (unsigned char)((h.low64 >> 32) & 0xff);
digest[12] = (unsigned char)((h.low64 >> 24) & 0xff);
digest[13] = (unsigned char)((h.low64 >> 16) & 0xff);
digest[14] = (unsigned char)((h.low64 >> 8) & 0xff);
digest[15] = (unsigned char)(h.low64 & 0xff);
}

PHP_HASH_API int PHP_XXH3_128_Copy(const php_hash_ops *ops, PHP_XXH3_128_CTX *orig_context, PHP_XXH3_128_CTX *copy_context)
{
copy_context->s = orig_context->s;
return SUCCESS;
}

4 changes: 4 additions & 0 deletions ext/hash/php_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ extern const php_hash_ops php_hash_joaat_ops;
extern const php_hash_ops php_hash_murmur3a_ops;
extern const php_hash_ops php_hash_murmur3c_ops;
extern const php_hash_ops php_hash_murmur3f_ops;
extern const php_hash_ops php_hash_xxh32_ops;
extern const php_hash_ops php_hash_xxh64_ops;
extern const php_hash_ops php_hash_xxh3_64_ops;
extern const php_hash_ops php_hash_xxh3_128_ops;

#define PHP_HASH_HAVAL_OPS(p,b) extern const php_hash_ops php_hash_##p##haval##b##_ops;

Expand Down
Loading