(pack: Uint8Array, options?: ParsePackOptions)
| 424 | |
| 425 | // Parse a full packfile and resolve all deltas. Returns objects keyed by sha. |
| 426 | export async function parsePack(pack: Uint8Array, options?: ParsePackOptions): Promise<ParsedPack> { |
| 427 | const limits = limitsFor(options); |
| 428 | requireAvailable(pack, 0, 12, "pack header"); |
| 429 | if (td.decode(pack.subarray(0, 4)) !== "PACK") throw new PackParseError("not a packfile"); |
| 430 | const version = new DataView(pack.buffer, pack.byteOffset + 4, 4).getUint32(0); |
| 431 | if (version !== 2 && version !== 3) { |
| 432 | throw new PackParseError(`unsupported pack version: ${version}`); |
| 433 | } |
| 434 | const count = new DataView(pack.buffer, pack.byteOffset + 8, 4).getUint32(0); |
| 435 | let pos = 12; |
| 436 | let expandedBytes = 0; |
| 437 | |
| 438 | const accountExpanded = (size: number): void => { |
| 439 | expandedBytes = checkedAdd(expandedBytes, size, "expanded pack size"); |
| 440 | if (expandedBytes > limits.maxExpandedBytes) { |
| 441 | throw repositoryTooLarge(`expanded objects exceed ${limits.maxExpandedBytes} bytes`); |
| 442 | } |
| 443 | }; |
| 444 | |
| 445 | const raws: RawObj[] = []; |
| 446 | for (let i = 0; i < count; i++) { |
| 447 | const start = pos; |
| 448 | const { type, size, pos: p1 } = readVarintSize(pack, pos); |
| 449 | if (size > limits.maxObjectBytes && type !== OBJ_OFS_DELTA && type !== OBJ_REF_DELTA) { |
| 450 | throw repositoryTooLarge(`object declares ${size} bytes`); |
| 451 | } |
| 452 | pos = p1; |
| 453 | if (type === OBJ_OFS_DELTA) { |
| 454 | // negative offset varint |
| 455 | requireAvailable(pack, pos, 1, "ofs-delta base offset"); |
| 456 | let c = pack[pos++]!; |
| 457 | let ofs = c & 0x7f; |
| 458 | let bytes = 1; |
| 459 | while (c & 0x80) { |
| 460 | if (bytes >= 7) throw new PackParseError("ofs-delta offset varint is too long"); |
| 461 | requireAvailable(pack, pos, 1, "ofs-delta base offset"); |
| 462 | c = pack[pos++]!; |
| 463 | ofs = checkedAdd( |
| 464 | checkedMul(ofs + 1, 128, "ofs-delta offset"), |
| 465 | c & 0x7f, |
| 466 | "ofs-delta offset", |
| 467 | ); |
| 468 | bytes += 1; |
| 469 | } |
| 470 | const baseOfs = start - ofs; |
| 471 | if (baseOfs < 12 || baseOfs >= start) { |
| 472 | throw new PackParseError("ofs-delta base offset is invalid"); |
| 473 | } |
| 474 | const inf = inflateZlib(pack, pos, limits); |
| 475 | pos = inf.endPos; |
| 476 | accountExpanded(inf.out.length); |
| 477 | raws.push({ type, baseOfs, delta: inf.out, offset: start }); |
| 478 | } else if (type === OBJ_REF_DELTA) { |
| 479 | requireAvailable(pack, pos, 20, "ref-delta base sha"); |
| 480 | let hex = ""; |
| 481 | for (let j = 0; j < 20; j++) hex += pack[pos + j].toString(16).padStart(2, "0"); |
| 482 | pos += 20; |
| 483 | const inf = inflateZlib(pack, pos, limits); |
no test coverage detected