Skip to content

Instantly share code, notes, and snippets.

@ademidoff
Forked from lahmatiy/png.inject.js
Created April 16, 2018 13:19
Show Gist options
  • Select an option

  • Save ademidoff/660e8ff4928ef17a08fac5cb4a851d09 to your computer and use it in GitHub Desktop.

Select an option

Save ademidoff/660e8ff4928ef17a08fac5cb4a851d09 to your computer and use it in GitHub Desktop.

Revisions

  1. @lahmatiy lahmatiy revised this gist Dec 11, 2017. No changes.
  2. @lahmatiy lahmatiy created this gist Nov 29, 2017.
    94 changes: 94 additions & 0 deletions png.inject.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    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
    };