|
| 1 | +// @ts-nocheck |
| 2 | +/** |
| 3 | + * Copyright (c) 2019 Jason Dent |
| 4 | + * https://github.com/Jason3S/xxhash |
| 5 | + */ |
| 6 | +const PRIME32_1 = 2654435761; |
| 7 | +const PRIME32_2 = 2246822519; |
| 8 | +const PRIME32_3 = 3266489917; |
| 9 | +const PRIME32_4 = 668265263; |
| 10 | +const PRIME32_5 = 374761393; |
| 11 | + |
| 12 | +function toUtf8(text) { |
| 13 | + const bytes = []; |
| 14 | + for (let i = 0, n = text.length; i < n; ++i) { |
| 15 | + const c = text.charCodeAt(i); |
| 16 | + if (c < 0x80) { |
| 17 | + bytes.push(c); |
| 18 | + } else if (c < 0x800) { |
| 19 | + bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f)); |
| 20 | + } else if (c < 0xd800 || c >= 0xe000) { |
| 21 | + bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f)); |
| 22 | + } else { |
| 23 | + const cp = |
| 24 | + 0x10000 + (((c & 0x3ff) << 10) | (text.charCodeAt(++i) & 0x3ff)); |
| 25 | + bytes.push( |
| 26 | + 0xf0 | ((cp >> 18) & 0x7), |
| 27 | + 0x80 | ((cp >> 12) & 0x3f), |
| 28 | + 0x80 | ((cp >> 6) & 0x3f), |
| 29 | + 0x80 | (cp & 0x3f), |
| 30 | + ); |
| 31 | + } |
| 32 | + } |
| 33 | + return new Uint8Array(bytes); |
| 34 | +} |
| 35 | +/** |
| 36 | + * |
| 37 | + * @param buffer - byte array or string |
| 38 | + * @param seed - optional seed (32-bit unsigned); |
| 39 | + */ |
| 40 | +export default function xxHash32( |
| 41 | + buffer, |
| 42 | + seed = 0, |
| 43 | +) { |
| 44 | + buffer = typeof buffer === 'string' ? toUtf8(buffer) : buffer; |
| 45 | + const b = buffer; |
| 46 | + |
| 47 | + /* |
| 48 | + Step 1. Initialize internal accumulators |
| 49 | + Each accumulator gets an initial value based on optional seed input. |
| 50 | + Since the seed is optional, it can be 0. |
| 51 | + ``` |
| 52 | + u32 acc1 = seed + PRIME32_1 + PRIME32_2; |
| 53 | + u32 acc2 = seed + PRIME32_2; |
| 54 | + u32 acc3 = seed + 0; |
| 55 | + u32 acc4 = seed - PRIME32_1; |
| 56 | + ``` |
| 57 | + Special case : input is less than 16 bytes |
| 58 | + When input is too small (< 16 bytes), the algorithm will not process any stripe. |
| 59 | + Consequently, it will not make use of parallel accumulators. |
| 60 | + In which case, a simplified initialization is performed, using a single accumulator : |
| 61 | + u32 acc = seed + PRIME32_5; |
| 62 | + The algorithm then proceeds directly to step 4. |
| 63 | + */ |
| 64 | + |
| 65 | + let acc = (seed + PRIME32_5) & 0xffffffff; |
| 66 | + let offset = 0; |
| 67 | + |
| 68 | + if (b.length >= 16) { |
| 69 | + const accN = [ |
| 70 | + (seed + PRIME32_1 + PRIME32_2) & 0xffffffff, |
| 71 | + (seed + PRIME32_2) & 0xffffffff, |
| 72 | + (seed + 0) & 0xffffffff, |
| 73 | + (seed - PRIME32_1) & 0xffffffff, |
| 74 | + ]; |
| 75 | + |
| 76 | + /* |
| 77 | + Step 2. Process stripes |
| 78 | + A stripe is a contiguous segment of 16 bytes. It is evenly divided into 4 lanes, |
| 79 | + of 4 bytes each. The first lane is used to update accumulator 1, the second lane |
| 80 | + is used to update accumulator 2, and so on. Each lane read its associated 32-bit |
| 81 | + value using little-endian convention. For each {lane, accumulator}, the update |
| 82 | + process is called a round, and applies the following formula : |
| 83 | + ``` |
| 84 | + accN = accN + (laneN * PRIME32_2); |
| 85 | + accN = accN <<< 13; |
| 86 | + accN = accN * PRIME32_1; |
| 87 | + ``` |
| 88 | + This shuffles the bits so that any bit from input lane impacts several bits in |
| 89 | + output accumulator. All operations are performed modulo 2^32. |
| 90 | + Input is consumed one full stripe at a time. Step 2 is looped as many times as |
| 91 | + necessary to consume the whole input, except the last remaining bytes which cannot |
| 92 | + form a stripe (< 16 bytes). When that happens, move to step 3. |
| 93 | + */ |
| 94 | + |
| 95 | + const b = buffer; |
| 96 | + const limit = b.length - 16; |
| 97 | + let lane = 0; |
| 98 | + for (offset = 0; (offset & 0xfffffff0) <= limit; offset += 4) { |
| 99 | + const i = offset; |
| 100 | + const laneN0 = b[i + 0] + (b[i + 1] << 8); |
| 101 | + const laneN1 = b[i + 2] + (b[i + 3] << 8); |
| 102 | + const laneNP = laneN0 * PRIME32_2 + ((laneN1 * PRIME32_2) << 16); |
| 103 | + let acc = (accN[lane] + laneNP) & 0xffffffff; |
| 104 | + acc = (acc << 13) | (acc >>> 19); |
| 105 | + const acc0 = acc & 0xffff; |
| 106 | + const acc1 = acc >>> 16; |
| 107 | + accN[lane] = (acc0 * PRIME32_1 + ((acc1 * PRIME32_1) << 16)) & 0xffffffff; |
| 108 | + lane = (lane + 1) & 0x3; |
| 109 | + } |
| 110 | + |
| 111 | + /* |
| 112 | + Step 3. Accumulator convergence |
| 113 | + All 4 lane accumulators from previous steps are merged to produce a |
| 114 | + single remaining accumulator |
| 115 | + of same width (32-bit). The associated formula is as follows : |
| 116 | + ``` |
| 117 | + acc = (acc1 <<< 1) + (acc2 <<< 7) + (acc3 <<< 12) + (acc4 <<< 18); |
| 118 | + ``` |
| 119 | + */ |
| 120 | + acc = |
| 121 | + (((accN[0] << 1) | (accN[0] >>> 31)) + |
| 122 | + ((accN[1] << 7) | (accN[1] >>> 25)) + |
| 123 | + ((accN[2] << 12) | (accN[2] >>> 20)) + |
| 124 | + ((accN[3] << 18) | (accN[3] >>> 14))) & |
| 125 | + 0xffffffff; |
| 126 | + } |
| 127 | + |
| 128 | + /* |
| 129 | + Step 4. Add input length |
| 130 | + The input total length is presumed known at this stage. |
| 131 | + This step is just about adding the length to |
| 132 | + accumulator, so that it participates to final mixing. |
| 133 | + ``` |
| 134 | + acc = acc + (u32)inputLength; |
| 135 | + ``` |
| 136 | + */ |
| 137 | + acc = (acc + buffer.length) & 0xffffffff; |
| 138 | + |
| 139 | + /* |
| 140 | + Step 5. Consume remaining input |
| 141 | + There may be up to 15 bytes remaining to consume from the input. |
| 142 | + The final stage will digest them according |
| 143 | + to following pseudo-code : |
| 144 | + ``` |
| 145 | + while (remainingLength >= 4) { |
| 146 | + lane = read_32bit_little_endian(input_ptr); |
| 147 | + acc = acc + lane * PRIME32_3; |
| 148 | + acc = (acc <<< 17) * PRIME32_4; |
| 149 | + input_ptr += 4; remainingLength -= 4; |
| 150 | + } |
| 151 | + ``` |
| 152 | + This process ensures that all input bytes are present in the final mix. |
| 153 | + */ |
| 154 | + |
| 155 | + const limit = buffer.length - 4; |
| 156 | + for (; offset <= limit; offset += 4) { |
| 157 | + const i = offset; |
| 158 | + const laneN0 = b[i + 0] + (b[i + 1] << 8); |
| 159 | + const laneN1 = b[i + 2] + (b[i + 3] << 8); |
| 160 | + const laneP = laneN0 * PRIME32_3 + ((laneN1 * PRIME32_3) << 16); |
| 161 | + acc = (acc + laneP) & 0xffffffff; |
| 162 | + acc = (acc << 17) | (acc >>> 15); |
| 163 | + acc = |
| 164 | + ((acc & 0xffff) * PRIME32_4 + (((acc >>> 16) * PRIME32_4) << 16)) & |
| 165 | + 0xffffffff; |
| 166 | + } |
| 167 | + |
| 168 | + /* |
| 169 | + ``` |
| 170 | + while (remainingLength >= 1) { |
| 171 | + lane = read_byte(input_ptr); |
| 172 | + acc = acc + lane * PRIME32_5; |
| 173 | + acc = (acc <<< 11) * PRIME32_1; |
| 174 | + input_ptr += 1; remainingLength -= 1; |
| 175 | + } |
| 176 | + ``` |
| 177 | + */ |
| 178 | + |
| 179 | + for (; offset < b.length; ++offset) { |
| 180 | + const lane = b[offset]; |
| 181 | + acc += lane * PRIME32_5; |
| 182 | + acc = (acc << 11) | (acc >>> 21); |
| 183 | + acc = |
| 184 | + ((acc & 0xffff) * PRIME32_1 + (((acc >>> 16) * PRIME32_1) << 16)) & |
| 185 | + 0xffffffff; |
| 186 | + } |
| 187 | + |
| 188 | + /* |
| 189 | + Step 6. Final mix (avalanche) |
| 190 | + The final mix ensures that all input bits have a chance to impact any bit in |
| 191 | + the output digest, resulting in an unbiased distribution. This is also called |
| 192 | + avalanche effect. |
| 193 | + ``` |
| 194 | + acc = acc xor (acc >> 15); |
| 195 | + acc = acc * PRIME32_2; |
| 196 | + acc = acc xor (acc >> 13); |
| 197 | + acc = acc * PRIME32_3; |
| 198 | + acc = acc xor (acc >> 16); |
| 199 | + ``` |
| 200 | + */ |
| 201 | + |
| 202 | + acc ^= acc >>> 15; |
| 203 | + acc = |
| 204 | + (((acc & 0xffff) * PRIME32_2) & 0xffffffff) + |
| 205 | + (((acc >>> 16) * PRIME32_2) << 16); |
| 206 | + acc ^= acc >>> 13; |
| 207 | + acc = |
| 208 | + (((acc & 0xffff) * PRIME32_3) & 0xffffffff) + |
| 209 | + (((acc >>> 16) * PRIME32_3) << 16); |
| 210 | + acc ^= acc >>> 16; |
| 211 | + |
| 212 | + // turn any negatives back into a positive number; |
| 213 | + return acc < 0 ? acc + 4294967296 : acc; |
| 214 | +} |
0 commit comments