Skip to content

Commit e015dbc

Browse files
committed
hash: Add xxHash serialize backward compatibility test
Signed-off-by: Anatol Belski <ab@php.net>
1 parent b325d01 commit e015dbc

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
--TEST--
2+
xxHash: serialize/unserialize backward compatibility after xxHash library upgrade
3+
--DESCRIPTION--
4+
Ensures that serialized xxh32/xxh64 HashContext objects remain deserializable
5+
and produce correct results across xxHash library versions (0.8.2 -> 0.8.3).
6+
The struct field renames (v->acc, mem32->buffer, memsize->bufferedSize) in 0.8.3
7+
do not change the binary layout, so the SPEC-based serialization must remain
8+
backward compatible.
9+
10+
The serialized strings below were generated by hashing "I can't remember anything"
11+
with xxh32/xxh64 (with and without seed). They are hardcoded to ensure that
12+
a future library upgrade does not break deserialization of previously-stored
13+
HashContext objects.
14+
--FILE--
15+
<?php
16+
17+
// Hardcoded serialized HashContext blobs (base64-encoded).
18+
// These represent a mid-stream xxh32/xxh64 context after hashing
19+
// "I can't remember anything", and must remain deserializable
20+
// across xxHash library upgrades that don't change the struct layout.
21+
22+
$tests = [
23+
// xxh32, no seed
24+
[
25+
'algo' => 'xxh32',
26+
'serialized' => 'TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjU6Inh4aDMyIjtpOjE7aTowO2k6MjthOjEyOntpOjA7aToyNTtpOjE7aToxO2k6MjtpOjUyODE4NjE2NjtpOjM7aTotNDc5MjcxNTg0O2k6NDtpOjIwMTQxODQ2NzM7aTo1O2k6LTQzMjc1MjM5NjtpOjY7aToyMDM3Mjc2OTYwO2k6NztpOjE4NTI0MDE3ODA7aTo4O2k6MTAzO2k6OTtpOjA7aToxMDtpOjk7aToxMTtpOjA7fWk6MztpOjI7aTo0O2E6MDp7fX0=',
27+
'expected_final' => 'eee74423',
28+
'continue_data' => "Can't tell if this is true or dream",
29+
'expected_continued' => '336076ba',
30+
],
31+
// xxh32, seed=1234
32+
[
33+
'algo' => 'xxh32',
34+
'seed' => 1234,
35+
'serialized' => 'TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjU6Inh4aDMyIjtpOjE7aTowO2k6MjthOjEyOntpOjA7aToyNTtpOjE7aToxO2k6MjtpOjg0NTI2NTcxODtpOjM7aTotMTYyMTkyMDMyO2k6NDtpOi0xOTYzNzAzMDcxO2k6NTtpOi0xMTU2NzI4NDQ7aTo2O2k6MjAzNzI3Njk2MDtpOjc7aToxODUyNDAxNzgwO2k6ODtpOjEwMztpOjk7aTowO2k6MTA7aTo5O2k6MTE7aTowO31pOjM7aToyO2k6NDthOjA6e319',
36+
'expected_final' => 'db5f448b',
37+
'continue_data' => "Can't tell if this is true or dream",
38+
'expected_continued' => '7da6974b',
39+
],
40+
// xxh64, no seed
41+
[
42+
'algo' => 'xxh64',
43+
'serialized' => 'TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjU6Inh4aDY0IjtpOjE7aTowO2k6MjthOjIyOntpOjA7aToyNTtpOjE7aTowO2k6MjtpOi0xMzc5ODc5NDY2O2k6MztpOjE2MjU5NTgzODI7aTo0O2k6NjY4MjY1Mjk1O2k6NTtpOi0xMDI4NDc3Mzc5O2k6NjtpOjA7aTo3O2k6MDtpOjg7aToyMDQ4MTQ0NzYxO2k6OTtpOjE2NDA1MzE1MzQ7aToxMDtpOjE2MzM4ODYyODE7aToxMTtpOjU0NDQ4MzE4MjtpOjEyO2k6MTcwMTY2ODIxMDtpOjEzO2k6MTkxOTI0Njk1NztpOjE0O2k6MjAzNzI3Njk2MDtpOjE1O2k6MTg1MjQwMTc4MDtpOjE2O2k6MTAzO2k6MTc7aTowO2k6MTg7aToyNTtpOjE5O2k6MDtpOjIwO2k6MDtpOjIxO2k6MDt9aTozO2k6MjtpOjQ7YTowOnt9fQ==',
44+
'expected_final' => '9d6ab4708056a619',
45+
'continue_data' => "Can't tell if this is true or dream",
46+
'expected_continued' => 'a0ede520213aaf63',
47+
],
48+
// xxh64, seed=1234
49+
[
50+
'algo' => 'xxh64',
51+
'seed' => 1234,
52+
'serialized' => 'TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjU6Inh4aDY0IjtpOjE7aTowO2k6MjthOjIyOntpOjA7aToyNTtpOjE7aTowO2k6MjtpOi0xMzc5ODc4MjMyO2k6MztpOjE2MjU5NTgzODI7aTo0O2k6NjY4MjY2NTI5O2k6NTtpOi0xMDI4NDc3Mzc5O2k6NjtpOjEyMzQ7aTo3O2k6MDtpOjg7aToyMDQ4MTQ1OTk1O2k6OTtpOjE2NDA1MzE1MzQ7aToxMDtpOjE2MzM4ODYyODE7aToxMTtpOjU0NDQ4MzE4MjtpOjEyO2k6MTcwMTY2ODIxMDtpOjEzO2k6MTkxOTI0Njk1NztpOjE0O2k6MjAzNzI3Njk2MDtpOjE1O2k6MTg1MjQwMTc4MDtpOjE2O2k6MTAzO2k6MTc7aTowO2k6MTg7aToyNTtpOjE5O2k6MDtpOjIwO2k6MDtpOjIxO2k6MDt9aTozO2k6MjtpOjQ7YTowOnt9fQ==',
53+
'expected_final' => 'be97930f372bf6e7',
54+
'continue_data' => "Can't tell if this is true or dream",
55+
'expected_continued' => 'a0434a21f6820fe4',
56+
],
57+
];
58+
59+
foreach ($tests as $test) {
60+
$algo = $test['algo'];
61+
$ctx = unserialize(base64_decode($test['serialized']));
62+
63+
// 1. Finalizing the deserialized context produces the expected hash
64+
$ctx_final = clone $ctx;
65+
$digest = hash_final($ctx_final);
66+
if ($digest !== $test['expected_final']) {
67+
echo "FAIL: {$algo} final expected {$test['expected_final']}, got {$digest}\n";
68+
} else {
69+
echo "OK: {$algo} unserialize+final\n";
70+
}
71+
72+
// 2. Continuing to hash after unserialize produces the correct result
73+
hash_update($ctx, $test['continue_data']);
74+
$digest = hash_final($ctx);
75+
if ($digest !== $test['expected_continued']) {
76+
echo "FAIL: {$algo} continued expected {$test['expected_continued']}, got {$digest}\n";
77+
} else {
78+
echo "OK: {$algo} unserialize+continue+final\n";
79+
}
80+
81+
// 3. Round-trip: serialize now, unserialize, and ensure same result
82+
$seed = $test['seed'] ?? null;
83+
$ctx2 = $seed !== null ? hash_init($algo, options: ["seed" => $seed]) : hash_init($algo);
84+
hash_update($ctx2, "I can't remember anything");
85+
$reserialized = serialize($ctx2);
86+
$ctx3 = unserialize($reserialized);
87+
hash_update($ctx3, $test['continue_data']);
88+
$digest = hash_final($ctx3);
89+
if ($digest !== $test['expected_continued']) {
90+
echo "FAIL: {$algo} round-trip expected {$test['expected_continued']}, got {$digest}\n";
91+
} else {
92+
echo "OK: {$algo} fresh serialize round-trip\n";
93+
}
94+
}
95+
96+
echo "Done\n";
97+
?>
98+
--EXPECT--
99+
OK: xxh32 unserialize+final
100+
OK: xxh32 unserialize+continue+final
101+
OK: xxh32 fresh serialize round-trip
102+
OK: xxh32 unserialize+final
103+
OK: xxh32 unserialize+continue+final
104+
OK: xxh32 fresh serialize round-trip
105+
OK: xxh64 unserialize+final
106+
OK: xxh64 unserialize+continue+final
107+
OK: xxh64 fresh serialize round-trip
108+
OK: xxh64 unserialize+final
109+
OK: xxh64 unserialize+continue+final
110+
OK: xxh64 fresh serialize round-trip
111+
Done

0 commit comments

Comments
 (0)