UNPKG

git-cat-file

Version:

Pure JavaScript `git cat-file -p` for node.js

140 lines (139 loc) 5.14 kB
"use strict"; /** * https://github.com/kawanet/git-cat-file * * @see https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt */ Object.defineProperty(exports, "__esModule", { value: true }); exports.readPackedObject = void 0; const zlib_1 = require("zlib"); const typeNames = [null, "commit", "tree", "blob", "tag"]; const deltaTypes = { 6: "OBJ_OFS_DELTA", 7: "OBJ_REF_DELTA" }; // const toHex = (buf: Buffer) => buf.toString("hex").replace(/(\w.)(?=\w)/g, "$1 "); async function readPackedObject(fh, start, store) { const buffer = Buffer.alloc(28); await fh.read({ buffer, position: start }); // console.warn(`read: ${start} (${toHex(buffer)})`); let offset = 0; let c = buffer[offset++]; const typeBit = (c & 0x70) >> 4; const type = typeNames[typeBit]; const deltaType = deltaTypes[typeBit]; // console.warn(`type: ${typeBit} (${deltaType || type})`); if (!deltaType && !type) throw new TypeError(`Invalid type: ${typeBit}`); let size = c & 0x0F; { let shift = 4; while (c & 0x80) { c = buffer[offset++]; size += ((c & 0x7F) << shift); shift += 7; } } // console.warn(`size: ${size} bytes`); if (typeBit === 6 /* TypeBit.OBJ_OFS_DELTA */) { let c = buffer[offset++]; let baseOffset = c & 0x7F; while (c & 0x80) { baseOffset += 1; // see unpack-objects.c c = buffer[offset++]; baseOffset = (baseOffset << 7) + (c & 0x7F); } if (start < baseOffset) { throw new TypeError(`offset value out of bound: ${start} < ${baseOffset}`); } // console.warn(`delta: ${start} + ${offset}`); const delta = await readData(fh, start + offset, size); // console.warn(`base: ${start} - ${baseOffset}`); const base = await readPackedObject(fh, start - baseOffset, store); const data = applyDelta(base.data, delta); return { type: base.type, data }; } if (typeBit === 7 /* TypeBit.OBJ_REF_DELTA */) { const end = offset + 20; const oid = buffer.slice(offset, end).toString("hex"); offset = end; // console.warn(`delta: ${start} + ${offset}`); const delta = await readData(fh, start + offset, size); // console.warn(`base: ${oid}`); const base = await store.getObject(oid); const data = applyDelta(base.data, delta); return { type: base.type, data }; } if (!size) { const data = Buffer.alloc(0); return { type, data }; } // console.warn(`position: ${start} + ${offset}`); const data = await readData(fh, start + offset, size); return { type, data }; } exports.readPackedObject = readPackedObject; async function readData(fh, position, size) { const bufSize = Math.ceil(size * 17 / 16 / 512) * 512; const buffer = Buffer.alloc(bufSize); await fh.read({ buffer, position }); return (0, zlib_1.inflateSync)(buffer, { maxOutputLength: size }); } function applyDelta(baseData, deltaData) { let deltaPos = 0; let dstPos = 0; // console.warn(`delta: ${toHex(deltaData.slice(0, 32))}`); const srcSize = readSize(); if (!srcSize) throw new TypeError(`Invalid source size: ${srcSize}`); const dstSize = readSize(); if (!dstSize) throw new TypeError(`Invalid dest size: ${dstSize}`); const dstData = Buffer.alloc(dstSize); const deltaEnd = deltaData.length; while (deltaPos < deltaEnd) { const inst = deltaData[deltaPos++]; if (inst & 0x80) { let offset = 0; let size = 0; if (inst & 0x01) offset += deltaData[deltaPos++]; if (inst & 0x02) offset += (deltaData[deltaPos++] << 8); if (inst & 0x04) offset += (deltaData[deltaPos++] << 16); if (inst & 0x08) offset += (deltaData[deltaPos++] << 24); if (inst & 0x10) size += deltaData[deltaPos++]; if (inst & 0x20) size += (deltaData[deltaPos++] << 8); if (inst & 0x40) size += (deltaData[deltaPos++] << 16); if (!size) size = 0x10000; // console.warn(`copy: ${inst.toString(2)} offset=${offset} size=${size}`); baseData.copy(dstData, dstPos, offset, offset + size); dstPos += size; } else if (inst) { const end = deltaPos + inst; // console.warn(`add: ${inst}`); deltaData.copy(dstData, dstPos, deltaPos, end); deltaPos = end; dstPos += inst; } else { throw TypeError(`unexpected delta opcode: ${inst}`); } } return dstData; function readSize() { let c = deltaData[deltaPos++]; let shift = 7; let size = c & 0x7F; while (c & 0x80) { c = deltaData[deltaPos++]; size += ((c & 0x7F) << shift); shift += 7; } return size; } }