Last active
April 16, 2018 13:19
-
-
Save lahmatiy/0332b6dc3319190f9493831492dbf036 to your computer and use it in GitHub Desktop.
Solution to inject/fetch a custom data to/from a PNG image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment