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 | | - }) |
| 30 … | +function compareByOffset(a, b) { |
| 31 … | + return a.offset - b.offset |
39 | 32 … | } |
40 | 33 … | |
41 | | -function skipHeader(len) { |
42 | | - return function (read) { |
43 | | - return function (end, cb) { |
44 | | - if (end || len <= 0) read(end, cb) |
45 | | - else read(null, function next(end, data) { |
46 | | - if (end) return cb(end) |
47 | | - var _len = len |
48 | | - len -= data.length |
49 | | - if (len > 0) read(null, next) |
50 | | - else cb(null, data.slice(_len)) |
51 | | - }) |
52 | | - } |
53 | | - } |
54 | | -} |
55 | | - |
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 | | - } |
| 34 … | +function dedupPacks(packs, cb) { |
| 35 … | + var seen = {} |
| 36 … | + var numObjects = 0 |
| 37 … | + forEachAsync(packs, function (pack, cb) { |
| 38 … | + return pull(pack.readIdx, packidx(function (err, idx) { |
| 39 … | + if (err) return cb(err) |
| 40 … | + var blocks = [] |
| 41 … | + var lastBlock |
| 42 … | + offset = 0 |
| 43 … | + var objs = idx.objects.sort(compareByOffset) |
|
| 44 … | + for (var i = 0; i < objs.length; i++) { |
| 45 … | + var obj = objs[i] |
| 46 … | + var id = obj.oid.toString('hex') |
| 47 … | + if (seen[id]) continue |
| 48 … | + seen[id] = true |
| 49 … | + numObjects++ |
| 50 … | + if (obj.offset > offset) { |
| 51 … | + blocks.push(lastBlock = {skip: obj.offset - offset, length: 0}) |
| 52 … | + offset = obj.offset |
| 53 … | + } else if (obj.offset < offset) { |
| 54 … | + return cb(new Error('bad offset')) |
| 55 … | + } |
| 56 … | + var len = obj.next ? obj.next.offset - obj.offset : Infinity |
| 57 … | + lastBlock.length += len |
| 58 … | + offset += len |
| 59 … | + } |
| 60 … | + pack.read = pull( |
| 61 … | + pack.read, |
| 62 … | + skipFooter(20), |
| 63 … | + blockFilter(pull.values(blocks)) |
| 64 … | + ) |
| 65 … | + cb() |
| 66 … | + })) |
| 67 … | + }, function (err) { |
| 68 … | + cb(err, numObjects) |
79 | 69 … | }) |
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 | | - } |
85 | 70 … | } |
86 | 71 … | |
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) |
102 | | -} |
103 | | - |
104 | 72 … | function closePacks(packs, cb) { |
105 | 73 … | forEachAsync(packs, function (pack, cb) { |
106 | 74 … | pack.read(true, cb) |
107 | 75 … | }, cb) |
108 | 76 … | } |
109 | 77 … | |
110 | 78 … | module.exports = function concatPacks(packs) { |
111 | | - |
| 79 … | + |
112 | 80 … | if (packs.length === 1) return packs[0].read |
113 | 81 … | |
114 | 82 … | var checksum = crypto.createHash('sha1') |
115 | 83 … | var packI = 0 |
118 | 86 … | return function next(end, cb) { |
119 | 87 … | switch (state) { |
120 | 88 … | case 'begin': |
121 | 89 … | if (end) return closePacks(cb) |
122 | | - return getNumObjects(packs, function (err, numObjects) { |
| 90 … | + return dedupPacks(packs, function (err, numObjects) { |
123 | 91 … | if (err) return cb(err) |
124 | | - state = 'startpack' |
125 | 92 … | var header = packHeader(numObjects) |
126 | 93 … | checksum.update(header) |
| 94 … | + state = 'payload' |
127 | 95 … | cb(null, header) |
128 | 96 … | }) |
129 | 97 … | |
130 | | - case 'startpack': |
| 98 … | + case 'payload': |
131 | 99 … | if (end) return closePacks(cb) |
132 | 100 … | if (packI >= packs.length) { |
133 | 101 … | state = 'end' |
134 | 102 … | return cb(null, checksum.digest()) |