git ssb

0+

cel / pull-git-remote-helper



Tree: 7bc216a78caca4f4c7f04f98603edaa3617bb669

Files: 7bc216a78caca4f4c7f04f98603edaa3617bb669 / lib / pack.js

6872 bytesRaw
1var buffered = require('pull-buffered')
2var pull = require('pull-stream')
3var toPull = require('stream-to-pull-stream')
4var pako = require('pako')
5var createHash = require('./util').createHash
6var cat = require('pull-cat')
7
8exports.decode = decodePack
9exports.encode = encodePack
10
11var PACK_VERSION = 2
12
13var objectTypes = [
14 'none', 'commit', 'tree', 'blob',
15 'tag', 'unused', 'ofs-delta', 'ref-delta'
16]
17var objectTypeNums = {
18 commit: 1,
19 tree: 2,
20 blob: 3,
21 tag: 4
22}
23
24function error(cb) {
25 return function (err) {
26 cb(err || true)
27 }
28}
29
30function inflateBytes(read) {
31 var inflate = new pako.Inflate()
32 var ended, dataOut
33
34 inflate.onData = function (data) {
35 dataOut = new Buffer(data)
36 // console.error('inflated data', data.length)
37 }
38
39 inflate.onEnd = function (status) {
40 ended = (status === 0) ? true : new Error(inflate.msg)
41 // console.error('inflated end', status, ended)
42 }
43
44 return function (abort, cb) {
45 if (ended) return cb(ended)
46 read(abort, function next(end, data) {
47 if (end === true) {
48 end = null
49 data = []
50 }
51 if (ended = end) return cb(end)
52 if (data.length > 1) return cb(new Error('got more than one byte'))
53 dataOut = null
54 inflate.push(data, end === true)
55 if (dataOut)
56 cb(null, dataOut)
57 else if (ended)
58 cb(ended)
59 else
60 read(null, next)
61 })
62 }
63}
64
65function deflate(read) {
66 var def = new pako.Deflate()
67 var queue = []
68 var ended
69
70 def.onData = function (data) {
71 queue.push([null, new Buffer(data)])
72 }
73
74 def.onEnd = function (status) {
75 queue.push([(status === 0) ? true : new Error(def.msg)])
76 }
77
78 return function readOut(abort, cb) {
79 if (ended)
80 cb(ended)
81 else if (queue.length)
82 cb.apply(this, queue.shift())
83 else
84 read(abort, function next(end, data) {
85 if (end === true) def.push([], true)
86 else if (end) return cb(end)
87 else def.push(data)
88 readOut(null, cb)
89 })
90 }
91}
92
93function decodePack(onEnd, read) {
94 if (read === undefined)
95 return decodePack.bind(this, onEnd)
96
97 var ended
98 var numObjects = -1
99 var checksum = createHash('sha1')
100 var b = buffered(read)
101 // TODO: optimize to pass through buffers to checksum
102 var readByte = checksum(b.chunks(1))
103 var readWord = checksum(b.chunks(4))
104 var readChecksum = b.chunks(20)
105 var expectChecksum = true
106 var opts = {
107 verbosity: 0
108 }
109
110 function readHeader(cb) {
111 readWord(null, function (end, header) {
112 if (ended = end) return cb(end)
113 if (!header.equals(header, new Buffer('PACK')))
114 read(new Error('Invalid packfile header'), error(cb))
115 else
116 readVersion(cb)
117 })
118 }
119
120 function readVersion(cb) {
121 readWord(null, function (end, word) {
122 if (ended = end) return cb(end)
123 var version = word.readUInt32BE()
124 if (version < 2 || version > 3)
125 read(new Error('Invalid packfile version ' + version), error(cb))
126 else
127 readNumObjects(cb)
128 })
129 }
130
131 function readNumObjects(cb) {
132 readWord(null, function (end, word) {
133 if (ended = end) return cb(end)
134 numObjects = word.readUInt32BE()
135 if (opts.verbosity >= 1)
136 console.error(numObjects + ' objects')
137 readObject(null, cb)
138 })
139 }
140
141 function readVarInt(cb) {
142 var type, value, shift
143 // https://codewords.recurse.com/images/three/varint.svg
144 readByte(null, function (end, buf) {
145 if (ended = end) return cb(end)
146 var firstByte = buf[0]
147 type = objectTypes[(firstByte >> 4) & 7]
148 value = firstByte & 15
149 // console.error('byte1', firstByte, firstByte.toString(2))
150 shift = 4
151 checkByte(firstByte)
152 })
153
154 function checkByte(byte) {
155 if (byte & 0x80)
156 readByte(null, gotByte)
157 else
158 cb(null, type, value)
159 }
160
161 function gotByte(end, buf) {
162 if (ended = end) return cb(end)
163 var byte = buf[0]
164 value += (byte & 0x7f) << shift
165 shift += 7
166 // console.error('byte', byte, byte.toString(2))
167 checkByte(byte)
168 }
169 }
170
171 function getObject(cb) {
172 readVarInt(function (end, type, length) {
173 if (opts.verbosity >= 2)
174 console.error('read object header', end, type, length)
175 numObjects--
176 if (end === true && expectChecksum)
177 onEnd(new Error('Missing checksum'))
178 if (ended = end) return cb(end)
179 // TODO: verify that the inflated data is the correct length
180 cb(null, type, length, inflateBytes(readByte))
181 })
182 }
183
184 function readTrailer(cb) {
185 // read the checksum before it updates to include the trailer
186 var expected = checksum.digest()
187 readChecksum(null, function (end, value) {
188 cb(true)
189 if (end === true && expectChecksum)
190 onEnd(new Error('Missing checksum'))
191 if (!value.equals(expected)) {
192 onEnd(new Error('Checksum mismatch: ' +
193 expected.hexSlice() + ' != ' + value.hexSlice()))
194 } else {
195 if (opts.verbosity >= 2)
196 console.error('checksum ok', expected.hexSlice())
197 onEnd(null)
198 }
199 })
200 }
201
202 function readObject(abort, cb) {
203 if (ended) cb(ended)
204 else if (abort) read(abort)
205 else if (numObjects < 0) readHeader(cb)
206 else if (numObjects > 0) getObject(cb)
207 else if (expectChecksum) readTrailer(cb)
208 }
209
210 return readObject
211}
212
213function once(read) {
214 var done
215 return function (abort, cb) {
216 if (done) cb(done)
217 else done = true, read(abort, cb)
218 }
219}
220
221function encodeVarInt(typeStr, length, cb) {
222 var type = objectTypeNums[typeStr]
223 // console.error('TYPE', type, typeStr, 'len', length, typeof cb)
224 if (!type)
225 return cb(new Error("Bad object type " + typeStr))
226
227 var vals = []
228 var b = (type << 4) | (length & 15)
229 for (length >>= 4; length; length >>= 7) {
230 vals.push(b | 0x80)
231 b = length & 0x7f
232 }
233 vals.push(b)
234 /*
235 console.error('sending var int', vals, vals.map(function (n) {
236 return ('00000000' + Number(n).toString(2)).substr(-8)
237 }))
238 */
239 cb(null, new Buffer(vals))
240}
241
242function encodePack(numObjects, readObject) {
243 if (readObject === undefined)
244 return encodePack.bind(this, numObjects)
245
246 var header = new Buffer(12)
247 header.write('PACK')
248 header.writeUInt32BE(PACK_VERSION, 4)
249 header.writeUInt32BE(numObjects, 8)
250 var checksum = createHash('sha1')
251 var readData
252
253 return cat([
254 checksum(cat([
255 pull.once(header),
256 encodeObject
257 ])),
258 checksum.readDigest
259 ])
260
261 function encodeObject(abort, cb) {
262 if (readData)
263 readData(abort, function (end, data) {
264 if (end === true)
265 readObject(abort, nextObject)
266 else
267 cb(end, data)
268 })
269 else
270 readObject(abort, nextObject)
271
272 function nextObject(end, type, length, read) {
273 if (end) return cb(end)
274 readData = deflate(read)
275 encodeVarInt(type, length, cb)
276 }
277 }
278}
279

Built with git-ssb-web