index.jsView |
---|
4 | 4 … | var buffered = require('pull-buffered') |
5 | 5 … | var multicb = require('multicb') |
6 | 6 … | var crypto = require('crypto') |
7 | 7 … | var skipFooter = require('pull-skip-footer') |
| 8 … | +var packidx = require('pull-git-packidx-parser') |
| 9 … | +var blockFilter = require('pull-block-filter') |
8 | 10 … | |
9 | 11 … | function packHeader(numObjects) { |
10 | 12 … | var header = new Buffer(12) |
11 | 13 … | header.write('PACK') |
24 | 26 … | }) |
25 | 27 … | }) |
26 | 28 … | } |
27 | 29 … | |
28 | | -function reduceAsync(arr, fn, init, cb) { |
29 | | - var i = 0 |
30 | | - var acc = init |
31 | | - loop(function (next) { |
32 | | - if (i >= arr.length) return cb(null, acc) |
33 | | - fn(arr[i++], acc, function (err, data) { |
34 | | - if (err) return cb(err) |
35 | | - acc = data |
36 | | - next() |
37 | | - }) |
38 | | - }) |
39 | | -} |
40 | | - |
41 | 30 … | function skipHeader(len) { |
42 | 31 … | return function (read) { |
43 | 32 … | return function (end, cb) { |
44 | 33 … | if (end || len <= 0) read(end, cb) |
52 | 41 … | } |
53 | 42 … | } |
54 | 43 … | } |
55 | 44 … | |
56 | | -function readHeader(read, len, cb) { |
57 | | - var headerBufs = [] |
58 | | - var dataBuf |
59 | | - read(null, function next(end, data) { |
60 | | - if (end) return cb(end === true ? new Error('Missing header') : err) |
61 | | - if (data.length > len) { |
62 | | - |
63 | | - headerBufs.push(data.slice(0, len)) |
64 | | - var header = Buffer.concat(headerBufs) |
65 | | - headerBufs = null |
66 | | - dataBuf = data.slice(len) |
67 | | - cb(null, header, readRest) |
68 | | - } else if (data.length === len) { |
69 | | - |
70 | | - headerBufs.push(data) |
71 | | - var header = Buffer.concat(headerBufs) |
72 | | - headerBufs = null |
73 | | - cb(null, header, read) |
74 | | - } else { |
75 | | - len -= data.length |
76 | | - headerBufs.push(data) |
77 | | - read(null, next) |
78 | | - } |
79 | | - }) |
80 | | - function readRest(end, cb) { |
81 | | - var buf = dataBuf |
82 | | - if (end || buf == null) read(end, cb) |
83 | | - else dataBuf = null, cb(null, buf) |
84 | | - } |
| 45 … | +function compareByOffset(a, b) { |
| 46 … | + return a.offset - b.offset |
85 | 47 … | } |
86 | 48 … | |
87 | | -function getNumObjects(packs, cb) { |
88 | | - reduceAsync(packs, function (pack, num, cb) { |
89 | | - if (pack.numObjects != null) { |
90 | | - pack.read = pull(pack.read, skipHeader(12), skipFooter(20)) |
91 | | - cb(null, num + pack.numObjects) |
92 | | - } else { |
93 | | - readHeader(pack.read, 12, function (err, header, readRest) { |
94 | | - if (err === true) return cb(new Error('Missing header')) |
95 | | - if (err) return cb(err) |
96 | | - pack.numObjects = header.readUInt32BE(8) |
97 | | - pack.read = skipFooter(20)(readRest) |
98 | | - cb(null, num + pack.numObjects) |
99 | | - }) |
100 | | - } |
101 | | - }, 0, cb) |
| 49 … | +function dedupPacks(packs, cb) { |
| 50 … | + var seen = {} |
| 51 … | + var numObjects = 0 |
| 52 … | + forEachAsync(packs, function (pack, cb) { |
| 53 … | + return pull(pack.readIdx, packidx(function (err, idx) { |
| 54 … | + if (err) return cb(err) |
| 55 … | + var blocks = [] |
| 56 … | + var lastBlock |
| 57 … | + offset = 0 |
| 58 … | + var objs = idx.objects.sort(compareByOffset) |
| 59 … | + for (var i = 0; i < objs.length; i++) { |
| 60 … | + var obj = objs[i] |
| 61 … | + var id = obj.oid.toString('hex') |
| 62 … | + if (id === '00947e10295e018fc71cf9c264ea5f341260f9b2') throw 1 |
| 63 … | + if (seen[id]) continue |
| 64 … | + seen[id] = true |
| 65 … | + numObjects++ |
| 66 … | + if (obj.offset > offset) { |
| 67 … | + blocks.push(lastBlock = {skip: obj.offset - offset, length: 0}) |
| 68 … | + offset = obj.offset |
| 69 … | + } else if (obj.offset < offset) { |
| 70 … | + return cb(new Error('bad offset')) |
| 71 … | + } |
| 72 … | + var len = obj.next ? obj.next.offset - obj.offset : Infinity |
| 73 … | + lastBlock.length += len |
| 74 … | + offset += len |
| 75 … | + } |
| 76 … | + pack.read = pull( |
| 77 … | + pack.read, |
| 78 … | + skipFooter(20), |
| 79 … | + blockFilter(pull.values(blocks)) |
| 80 … | + ) |
| 81 … | + cb() |
| 82 … | + })) |
| 83 … | + }, function (err) { |
| 84 … | + cb(err, numObjects) |
| 85 … | + }) |
102 | 86 … | } |
103 | 87 … | |
|
104 | 88 … | function closePacks(packs, cb) { |
105 | 89 … | forEachAsync(packs, function (pack, cb) { |
107 | 91 … | }, cb) |
108 | 92 … | } |
109 | 93 … | |
110 | 94 … | module.exports = function concatPacks(packs) { |
111 | | - |
| 95 … | + |
112 | 96 … | if (packs.length === 1) return packs[0].read |
113 | 97 … | |
114 | 98 … | var checksum = crypto.createHash('sha1') |
115 | 99 … | var packI = 0 |
118 | 102 … | return function next(end, cb) { |
119 | 103 … | switch (state) { |
120 | 104 … | case 'begin': |
121 | 105 … | if (end) return closePacks(cb) |
122 | | - return getNumObjects(packs, function (err, numObjects) { |
| 106 … | + return dedupPacks(packs, function (err, numObjects) { |
123 | 107 … | if (err) return cb(err) |
124 | | - state = 'startpack' |
125 | 108 … | var header = packHeader(numObjects) |
126 | 109 … | checksum.update(header) |
| 110 … | + state = 'payload' |
127 | 111 … | cb(null, header) |
128 | 112 … | }) |
129 | 113 … | |
130 | | - case 'startpack': |
| 114 … | + case 'payload': |
131 | 115 … | if (end) return closePacks(cb) |
132 | 116 … | if (packI >= packs.length) { |
133 | 117 … | state = 'end' |
134 | 118 … | return cb(null, checksum.digest()) |