Current File : //usr/local/share/.config/yarn/global/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);
}