xref: /php-src/ext/hash/hash_xxhash.c (revision 7463e70b)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Anatol Belski <ab@php.net>                                   |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "php_hash.h"
18 #include "php_hash_xxhash.h"
19 
20 static int php_hash_xxh32_unserialize(
21 		php_hashcontext_object *hash, zend_long magic, const zval *zv);
22 static int php_hash_xxh64_unserialize(
23 		php_hashcontext_object *hash, zend_long magic, const zval *zv);
24 
25 const php_hash_ops php_hash_xxh32_ops = {
26 	"xxh32",
27 	(php_hash_init_func_t) PHP_XXH32Init,
28 	(php_hash_update_func_t) PHP_XXH32Update,
29 	(php_hash_final_func_t) PHP_XXH32Final,
30 	(php_hash_copy_func_t) PHP_XXH32Copy,
31 	php_hash_serialize,
32 	php_hash_xxh32_unserialize,
33 	PHP_XXH32_SPEC,
34 	4,
35 	4,
36 	sizeof(PHP_XXH32_CTX),
37 	0
38 };
39 
PHP_XXH32Init(PHP_XXH32_CTX * ctx,HashTable * args)40 PHP_HASH_API void PHP_XXH32Init(PHP_XXH32_CTX *ctx, HashTable *args)
41 {
42 	/* XXH32_createState() is not used intentionally. */
43 	memset(&ctx->s, 0, sizeof ctx->s);
44 
45 	if (args) {
46 		zval *seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
47 		/* This might be a bit too restrictive, but thinking that a seed might be set
48 			once and for all, it should be done a clean way. */
49 		if (seed && IS_LONG == Z_TYPE_P(seed)) {
50 			XXH32_reset(&ctx->s, (XXH32_hash_t)Z_LVAL_P(seed));
51 		} else {
52 			XXH32_reset(&ctx->s, 0);
53 		}
54 	} else {
55 		XXH32_reset(&ctx->s, 0);
56 	}
57 }
58 
PHP_XXH32Update(PHP_XXH32_CTX * ctx,const unsigned char * in,size_t len)59 PHP_HASH_API void PHP_XXH32Update(PHP_XXH32_CTX *ctx, const unsigned char *in, size_t len)
60 {
61 	XXH32_update(&ctx->s, in, len);
62 }
63 
PHP_XXH32Final(unsigned char digest[4],PHP_XXH32_CTX * ctx)64 PHP_HASH_API void PHP_XXH32Final(unsigned char digest[4], PHP_XXH32_CTX *ctx)
65 {
66 	XXH32_canonicalFromHash((XXH32_canonical_t*)digest, XXH32_digest(&ctx->s));
67 }
68 
PHP_XXH32Copy(const php_hash_ops * ops,PHP_XXH32_CTX * orig_context,PHP_XXH32_CTX * copy_context)69 PHP_HASH_API int PHP_XXH32Copy(const php_hash_ops *ops, PHP_XXH32_CTX *orig_context, PHP_XXH32_CTX *copy_context)
70 {
71 	copy_context->s = orig_context->s;
72 	return SUCCESS;
73 }
74 
php_hash_xxh32_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv)75 static int php_hash_xxh32_unserialize(
76 		php_hashcontext_object *hash, zend_long magic, const zval *zv)
77 {
78 	PHP_XXH32_CTX *ctx = (PHP_XXH32_CTX *) hash->context;
79 	int r = FAILURE;
80 	if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
81 		&& (r = php_hash_unserialize_spec(hash, zv, PHP_XXH32_SPEC)) == SUCCESS
82 		&& ctx->s.memsize < 16) {
83 		return SUCCESS;
84 	} else {
85 		return r != SUCCESS ? r : -2000;
86 	}
87 }
88 
89 const php_hash_ops php_hash_xxh64_ops = {
90 	"xxh64",
91 	(php_hash_init_func_t) PHP_XXH64Init,
92 	(php_hash_update_func_t) PHP_XXH64Update,
93 	(php_hash_final_func_t) PHP_XXH64Final,
94 	(php_hash_copy_func_t) PHP_XXH64Copy,
95 	php_hash_serialize,
96 	php_hash_xxh64_unserialize,
97 	PHP_XXH64_SPEC,
98 	8,
99 	8,
100 	sizeof(PHP_XXH64_CTX),
101 	0
102 };
103 
PHP_XXH64Init(PHP_XXH64_CTX * ctx,HashTable * args)104 PHP_HASH_API void PHP_XXH64Init(PHP_XXH64_CTX *ctx, HashTable *args)
105 {
106 	/* XXH64_createState() is not used intentionally. */
107 	memset(&ctx->s, 0, sizeof ctx->s);
108 
109 	if (args) {
110 		zval *seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
111 		/* This might be a bit too restrictive, but thinking that a seed might be set
112 			once and for all, it should be done a clean way. */
113 		if (seed && IS_LONG == Z_TYPE_P(seed)) {
114 			XXH64_reset(&ctx->s, (XXH64_hash_t)Z_LVAL_P(seed));
115 		} else {
116 			XXH64_reset(&ctx->s, 0);
117 		}
118 	} else {
119 		XXH64_reset(&ctx->s, 0);
120 	}
121 }
122 
PHP_XXH64Update(PHP_XXH64_CTX * ctx,const unsigned char * in,size_t len)123 PHP_HASH_API void PHP_XXH64Update(PHP_XXH64_CTX *ctx, const unsigned char *in, size_t len)
124 {
125 	XXH64_update(&ctx->s, in, len);
126 }
127 
PHP_XXH64Final(unsigned char digest[8],PHP_XXH64_CTX * ctx)128 PHP_HASH_API void PHP_XXH64Final(unsigned char digest[8], PHP_XXH64_CTX *ctx)
129 {
130 	XXH64_canonicalFromHash((XXH64_canonical_t*)digest, XXH64_digest(&ctx->s));
131 }
132 
PHP_XXH64Copy(const php_hash_ops * ops,PHP_XXH64_CTX * orig_context,PHP_XXH64_CTX * copy_context)133 PHP_HASH_API int PHP_XXH64Copy(const php_hash_ops *ops, PHP_XXH64_CTX *orig_context, PHP_XXH64_CTX *copy_context)
134 {
135 	copy_context->s = orig_context->s;
136 	return SUCCESS;
137 }
138 
139 const php_hash_ops php_hash_xxh3_64_ops = {
140 	"xxh3",
141 	(php_hash_init_func_t) PHP_XXH3_64_Init,
142 	(php_hash_update_func_t) PHP_XXH3_64_Update,
143 	(php_hash_final_func_t) PHP_XXH3_64_Final,
144 	(php_hash_copy_func_t) PHP_XXH3_64_Copy,
145 	php_hash_serialize,
146 	php_hash_unserialize,
147 	NULL,
148 	8,
149 	8,
150 	sizeof(PHP_XXH3_64_CTX),
151 	0
152 };
153 
154 typedef XXH_errorcode (*xxh3_reset_with_secret_func_t)(XXH3_state_t*, const void*, size_t);
155 typedef XXH_errorcode (*xxh3_reset_with_seed_func_t)(XXH3_state_t*, XXH64_hash_t);
156 
_PHP_XXH3_Init(PHP_XXH3_64_CTX * ctx,HashTable * args,xxh3_reset_with_seed_func_t func_init_seed,xxh3_reset_with_secret_func_t func_init_secret,const char * algo_name)157 zend_always_inline static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args,
158 		xxh3_reset_with_seed_func_t func_init_seed, xxh3_reset_with_secret_func_t func_init_secret, const char* algo_name)
159 {
160 	memset(&ctx->s, 0, sizeof ctx->s);
161 
162 	if (args) {
163 		zval *_seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
164 		zval *_secret = zend_hash_str_find_deref(args, "secret", sizeof("secret") - 1);
165 
166 		if (_seed && _secret) {
167 			zend_throw_error(NULL, "%s: Only one of seed or secret is to be passed for initialization", algo_name);
168 			return;
169 		}
170 
171 		if (_seed && IS_LONG == Z_TYPE_P(_seed)) {
172 			/* This might be a bit too restrictive, but thinking that a seed might be set
173 				once and for all, it should be done a clean way. */
174 			func_init_seed(&ctx->s, (XXH64_hash_t)Z_LVAL_P(_seed));
175 			return;
176 		} else if (_secret) {
177 			if (!try_convert_to_string(_secret)) {
178 				return;
179 			}
180 			size_t len = Z_STRLEN_P(_secret);
181 			if (len < PHP_XXH3_SECRET_SIZE_MIN) {
182 				zend_throw_error(NULL, "%s: Secret length must be >= %u bytes, %zu bytes passed", algo_name, XXH3_SECRET_SIZE_MIN, len);
183 				return;
184 			}
185 			if (len > sizeof(ctx->secret)) {
186 				len = sizeof(ctx->secret);
187 				php_error_docref(NULL, E_WARNING, "%s: Secret content exceeding %zu bytes discarded", algo_name, sizeof(ctx->secret));
188 			}
189 			memcpy((unsigned char *)ctx->secret, Z_STRVAL_P(_secret), len);
190 			func_init_secret(&ctx->s, ctx->secret, len);
191 			return;
192 		}
193 	}
194 
195 	func_init_seed(&ctx->s, 0);
196 }
197 
PHP_XXH3_64_Init(PHP_XXH3_64_CTX * ctx,HashTable * args)198 PHP_HASH_API void PHP_XXH3_64_Init(PHP_XXH3_64_CTX *ctx, HashTable *args)
199 {
200 	_PHP_XXH3_Init(ctx, args, XXH3_64bits_reset_withSeed, XXH3_64bits_reset_withSecret, "xxh3");
201 }
202 
PHP_XXH3_64_Update(PHP_XXH3_64_CTX * ctx,const unsigned char * in,size_t len)203 PHP_HASH_API void PHP_XXH3_64_Update(PHP_XXH3_64_CTX *ctx, const unsigned char *in, size_t len)
204 {
205 	XXH3_64bits_update(&ctx->s, in, len);
206 }
207 
PHP_XXH3_64_Final(unsigned char digest[8],PHP_XXH3_64_CTX * ctx)208 PHP_HASH_API void PHP_XXH3_64_Final(unsigned char digest[8], PHP_XXH3_64_CTX *ctx)
209 {
210 	XXH64_canonicalFromHash((XXH64_canonical_t*)digest, XXH3_64bits_digest(&ctx->s));
211 }
212 
PHP_XXH3_64_Copy(const php_hash_ops * ops,PHP_XXH3_64_CTX * orig_context,PHP_XXH3_64_CTX * copy_context)213 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)
214 {
215 	copy_context->s = orig_context->s;
216 	return SUCCESS;
217 }
218 
php_hash_xxh64_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv)219 static int php_hash_xxh64_unserialize(
220 		php_hashcontext_object *hash, zend_long magic, const zval *zv)
221 {
222 	PHP_XXH64_CTX *ctx = (PHP_XXH64_CTX *) hash->context;
223 	int r = FAILURE;
224 	if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
225 		&& (r = php_hash_unserialize_spec(hash, zv, PHP_XXH64_SPEC)) == SUCCESS
226 		&& ctx->s.memsize < 32) {
227 		return SUCCESS;
228 	} else {
229 		return r != SUCCESS ? r : -2000;
230 	}
231 }
232 
233 const php_hash_ops php_hash_xxh3_128_ops = {
234 	"xxh128",
235 	(php_hash_init_func_t) PHP_XXH3_128_Init,
236 	(php_hash_update_func_t) PHP_XXH3_128_Update,
237 	(php_hash_final_func_t) PHP_XXH3_128_Final,
238 	(php_hash_copy_func_t) PHP_XXH3_128_Copy,
239 	php_hash_serialize,
240 	php_hash_unserialize,
241 	NULL,
242 	16,
243 	8,
244 	sizeof(PHP_XXH3_128_CTX),
245 	0
246 };
247 
PHP_XXH3_128_Init(PHP_XXH3_128_CTX * ctx,HashTable * args)248 PHP_HASH_API void PHP_XXH3_128_Init(PHP_XXH3_128_CTX *ctx, HashTable *args)
249 {
250 	_PHP_XXH3_Init(ctx, args, XXH3_128bits_reset_withSeed, XXH3_128bits_reset_withSecret, "xxh128");
251 }
252 
PHP_XXH3_128_Update(PHP_XXH3_128_CTX * ctx,const unsigned char * in,size_t len)253 PHP_HASH_API void PHP_XXH3_128_Update(PHP_XXH3_128_CTX *ctx, const unsigned char *in, size_t len)
254 {
255 	XXH3_128bits_update(&ctx->s, in, len);
256 }
257 
PHP_XXH3_128_Final(unsigned char digest[16],PHP_XXH3_128_CTX * ctx)258 PHP_HASH_API void PHP_XXH3_128_Final(unsigned char digest[16], PHP_XXH3_128_CTX *ctx)
259 {
260 	XXH128_canonicalFromHash((XXH128_canonical_t*)digest, XXH3_128bits_digest(&ctx->s));
261 }
262 
PHP_XXH3_128_Copy(const php_hash_ops * ops,PHP_XXH3_128_CTX * orig_context,PHP_XXH3_128_CTX * copy_context)263 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)
264 {
265 	copy_context->s = orig_context->s;
266 	return SUCCESS;
267 }
268 
269