%PDF- %PDF-
Direktori : /lib/node_modules/pm2/node_modules/js-git/lib/ |
Current File : //lib/node_modules/pm2/node_modules/js-git/lib/pack-codec.js |
var inflateStream = require('./inflate-stream.js'); var inflate = require('./inflate.js'); var deflate = require('./deflate.js'); var sha1 = require('git-sha1'); var bodec = require('bodec'); var typeToNum = { commit: 1, tree: 2, blob: 3, tag: 4, "ofs-delta": 6, "ref-delta": 7 }; var numToType = {}; for (var type in typeToNum) { var num = typeToNum[type]; numToType[num] = type; } exports.parseEntry = parseEntry; function parseEntry(chunk) { var offset = 0; var byte = chunk[offset++]; var type = numToType[(byte >> 4) & 0x7]; var size = byte & 0xf; var left = 4; while (byte & 0x80) { byte = chunk[offset++]; size |= (byte & 0x7f) << left; left += 7; } size = size >>> 0; var ref; if (type === "ref-delta") { ref = bodec.toHex(bodec.slice(chunk, offset, offset += 20)); } else if (type === "ofs-delta") { byte = chunk[offset++]; ref = byte & 0x7f; while (byte & 0x80) { byte = chunk[offset++]; ref = ((ref + 1) << 7) | (byte & 0x7f); } } var body = inflate(bodec.slice(chunk, offset)); if (body.length !== size) { throw new Error("Size mismatch"); } var result = { type: type, body: body }; if (typeof ref !== "undefined") { result.ref = ref; } return result; } exports.decodePack = decodePack; function decodePack(emit) { var state = $pack; var sha1sum = sha1(); var inf = inflateStream(); var offset = 0; var position = 0; var version = 0x4b434150; // PACK reversed var num = 0; var type = 0; var length = 0; var ref = null; var checksum = ""; var start = 0; var parts = []; return function (chunk) { if (chunk === undefined) { if (num || checksum.length < 40) throw new Error("Unexpected end of input stream"); return emit(); } for (var i = 0, l = chunk.length; i < l; i++) { // console.log([state, i, chunk[i].toString(16)]); if (!state) throw new Error("Unexpected extra bytes: " + bodec.slice(chunk, i)); state = state(chunk[i], i, chunk); position++; } if (!state) return; if (state !== $checksum) sha1sum.update(chunk); var buff = inf.flush(); if (buff.length) { parts.push(buff); } }; // The first four bytes in a packfile are the bytes 'PACK' function $pack(byte) { if ((version & 0xff) === byte) { version >>>= 8; return version ? $pack : $version; } throw new Error("Invalid packfile header"); } // The version is stored as an unsigned 32 integer in network byte order. // It must be version 2 or 3. function $version(byte) { version = (version << 8) | byte; if (++offset < 4) return $version; if (version >= 2 && version <= 3) { offset = 0; return $num; } throw new Error("Invalid version number " + num); } // The number of objects in this packfile is also stored as an unsigned 32 bit int. function $num(byte) { num = (num << 8) | byte; if (++offset < 4) return $num; offset = 0; emit({version: version, num: num}); return $header; } // n-byte type and length (3-bit type, (n-1)*7+4-bit length) // CTTTSSSS // C is continue bit, TTT is type, S+ is length function $header(byte) { if (start === 0) start = position; type = byte >> 4 & 0x07; length = byte & 0x0f; if (byte & 0x80) { offset = 4; return $header2; } return afterHeader(); } // Second state in the same header parsing. // CSSSSSSS* function $header2(byte) { length |= (byte & 0x7f) << offset; if (byte & 0x80) { offset += 7; return $header2; } return afterHeader(); } // Common helper for finishing tiny and normal headers. function afterHeader() { offset = 0; if (type === 6) { ref = 0; return $ofsDelta; } if (type === 7) { ref = ""; return $refDelta; } // console.log({type: type,length: length}) return $body; } // Big-endian modified base 128 number encoded ref offset function $ofsDelta(byte) { ref = byte & 0x7f; if (byte & 0x80) return $ofsDelta2; return $body; } function $ofsDelta2(byte) { ref = ((ref + 1) << 7) | (byte & 0x7f); if (byte & 0x80) return $ofsDelta2; return $body; } // 20 byte raw sha1 hash for ref function $refDelta(byte) { ref += toHex(byte); if (++offset < 20) return $refDelta; return $body; } // Common helper for generating 2-character hex numbers function toHex(num) { return num < 0x10 ? "0" + num.toString(16) : num.toString(16); } // Common helper for emitting all three object shapes function emitObject() { var body = bodec.join(parts); if (body.length !== length) { throw new Error("Body length mismatch"); } var item = { type: numToType[type], size: length, body: body, offset: start }; if (ref) item.ref = ref; parts.length = 0; start = 0; offset = 0; type = 0; length = 0; ref = null; emit(item); } // Feed the deflated code to the inflate engine function $body(byte, i, chunk) { if (inf.write(byte)) return $body; var buf = inf.flush(); if (buf.length !== length) throw new Error("Length mismatch, expected " + length + " got " + buf.length); inf.recycle(); if (buf.length) { parts.push(buf); } emitObject(); // If this was all the objects, start calculating the sha1sum if (--num) return $header; sha1sum.update(bodec.slice(chunk, 0, i + 1)); return $checksum; } // 20 byte checksum function $checksum(byte) { checksum += toHex(byte); if (++offset < 20) return $checksum; var actual = sha1sum.digest(); if (checksum !== actual) throw new Error("Checksum mismatch: " + actual + " != " + checksum); } } exports.encodePack = encodePack; function encodePack(emit) { var sha1sum = sha1(); var left; return function (item) { if (item === undefined) { if (left !== 0) throw new Error("Some items were missing"); return emit(); } if (typeof item.num === "number") { if (left !== undefined) throw new Error("Header already sent"); left = item.num; write(packHeader(item.num)); } else if (typeof item.type === "string" && bodec.isBinary(item.body)) { // The header must be sent before items. if (typeof left !== "number") throw new Error("Headers not sent yet"); // Make sure we haven't sent all the items already if (!left) throw new Error("All items already sent"); // Send the item in packstream format write(packFrame(item)); // Send the checksum after the last item if (!--left) { emit(bodec.fromHex(sha1sum.digest())); } } else { throw new Error("Invalid item"); } }; function write(chunk) { sha1sum.update(chunk); emit(chunk); } } function packHeader(length) { return bodec.fromArray([ 0x50, 0x41, 0x43, 0x4b, // PACK 0, 0, 0, 2, // version 2 length >> 24, // Num of objects (length >> 16) & 0xff, (length >> 8) & 0xff, length & 0xff ]); } function packFrame(item) { var length = item.body.length; // write TYPE_AND_BASE128_SIZE var head = [(typeToNum[item.type] << 4) | (length & 0xf)]; var i = 0; length >>= 4; while (length) { head[i++] |= 0x80; head[i] = length & 0x7f; length >>= 7; } if (typeof item.ref === "number") { // write BIG_ENDIAN_MODIFIED_BASE_128_NUMBER var offset = item.ref; // Calculate how many digits we need in base 128 and move the pointer i += Math.floor(Math.log(offset) / Math.log(0x80)) + 1; // Write the last digit head[i] = offset & 0x7f; // Then write the rest while (offset >>= 7) { head[--i] = 0x80 | (--offset & 0x7f); } } var parts = [bodec.fromArray(head)]; if (typeof item.ref === "string") { parts.push(bodec.fromHex(item.ref)); } parts.push(deflate(item.body)); return bodec.join(parts); }