const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]); const hashKey = 'react-snapshot-hash'; const crcTable = []; const initialCrc = 0xffffffff; for (let n = 0; n < 256; n++) { let c = n; for (let k = 0; k < 8; k++) { if (c & 1) { c = 0xedb88320 ^ (c >>> 1); } else { c = c >>> 1; } } crcTable[n] = c; } function updateCrc(crc, data, length) { let c = crc; for (let n = 0; n < length; n++) { c = crcTable[(c ^ data[n]) & 0xff] ^ (c >>> 8); } return c; } function crc(data, length) { return (updateCrc(initialCrc, data, length) ^ initialCrc) >>> 0; } function fetchRequestHash(data) { const view = new DataView(data.buffer); if (pngSignature.compare(data, 0, pngSignature.length) !== 0) { return false; } // fast png scan for (let offset = pngSignature.length; offset < data.length;) { const len = view.getUint32(offset); const type = data.toString('ascii', offset + 4, offset + 8); // search for text chunk with `hashKey` as a key if (type === 'tEXt') { const keyEnd = data.indexOf(0, offset + 8); if (keyEnd !== -1) { const key = data.toString('ascii', offset + 8, keyEnd); if (key === hashKey) { return data.toString('utf8', keyEnd + 1, offset + 8 + len); } } } offset += len + 12; // len + type + crc } return false; } function buildRequestHashChunk(hash) { const data = Buffer.from([ 'tEXt', // type hashKey, // key '\0', // key terminator hash // data ].join('')); const res = Buffer.alloc(4 + data.length + 4); const view = new DataView(res.buffer); view.setUint32(0, data.length - 4); view.setUint32(4 + data.length, crc(data, data.length)); data.copy(res, 4); return res; } function injectRequestHash(buffer, hash) { return Buffer.concat([ buffer.slice(0, buffer.length - 12), buildRequestHashChunk(hash), buffer.slice(buffer.length - 12) ]); } module.exports = { crc, fetchRequestHash, injectRequestHash };