git ssb

0+

cel / pull-git-remote-helper



Tree: b57e8b80419ea2fb7cf7d3b5281d371a2b3976c0

Files: b57e8b80419ea2fb7cf7d3b5281d371a2b3976c0 / pack.js

7609 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 /*
80 function _cb(end, data) {
81 console.error('sending deflated', end,
82 data && JSON.stringify(data.toString()))
83 cb(end, data)
84 }
85 */
86 if (ended)
87 cb(ended)
88 else if (queue.length)
89 cb.apply(this, queue.shift())
90 else
91 read(abort, function next(end, data) {
92 if (data)
93 console.error('read into deflat', data.length, JSON.stringify(data))
94 if (end === true) def.push([], true)
95 else if (end) return cb(end)
96 else def.push(data)
97 readOut(null, cb)
98 })
99 }
100}
101
102function decodePack(onEnd, read) {
103 if (read === undefined)
104 return decodePack.bind(this, onEnd)
105
106 var ended
107 var numObjects = -1
108 var checksum = createHash('sha1')
109 var b = buffered(read)
110 // TODO: optimize to pass through buffers to checksum
111 var readByte = checksum(b.chunks(1))
112 var readWord = checksum(b.chunks(4))
113 var readChecksum = b.chunks(20)
114 var expectChecksum = true
115 var opts = {
116 verbosity: 2
117 }
118
119 function readHeader(cb) {
120 readWord(null, function (end, header) {
121 if (ended = end) return cb(end)
122 if (!header.equals(header, new Buffer('PACK')))
123 read(new Error('Invalid packfile header'), error(cb))
124 else
125 readVersion(cb)
126 })
127 }
128
129 function readVersion(cb) {
130 readWord(null, function (end, word) {
131 if (ended = end) return cb(end)
132 var version = word.readUInt32BE()
133 if (version < 2 || version > 3)
134 read(new Error('Invalid packfile version ' + version), error(cb))
135 else
136 readNumObjects(cb)
137 })
138 }
139
140 function readNumObjects(cb) {
141 readWord(null, function (end, word) {
142 if (ended = end) return cb(end)
143 numObjects = word.readUInt32BE()
144 if (opts.verbosity >= 1)
145 console.error(numObjects + ' objects')
146 readObject(null, cb)
147 })
148 }
149
150 function readVarInt(cb) {
151 var type, value, shift
152 // https://codewords.recurse.com/images/three/varint.svg
153 readByte(null, function (end, buf) {
154 if (ended = end) return cb(end)
155 var firstByte = buf[0]
156 type = objectTypes[(firstByte >> 4) & 7]
157 value = firstByte & 15
158 console.error('byte1', firstByte, firstByte.toString(2), value, value.toString(2))
159 shift = 4
160 checkByte(firstByte)
161 })
162
163 function checkByte(byte) {
164 if (byte & 0x80)
165 readByte(null, gotByte)
166 else
167 cb(null, type, value)
168 }
169
170 function gotByte(end, buf) {
171 if (ended = end) return cb(end)
172 var byte = buf[0]
173 value += (byte & 0x7f) << shift
174 shift += 7
175 console.error('byte', byte, byte.toString(2), value, value.toString(2))
176 checkByte(byte)
177 }
178 }
179
180 function getObject(cb) {
181 readVarInt(function (end, type, length) {
182 if (opts.verbosity >= 2)
183 console.error('read var int', end, type, length)
184 numObjects--
185 if (end === true && expectChecksum)
186 onEnd(new Error('Missing checksum'))
187 if (ended = end) return cb(end)
188 // TODO: verify that the inflated data is the correct length
189 cb(null, type, length, inflateBytes(readByte))
190 })
191 }
192
193 function readTrailer(cb) {
194 // read the checksum before it updates to include the trailer
195 var expected = checksum.digest()
196 readChecksum(null, function (end, value) {
197 cb(true)
198 if (end === true && expectChecksum)
199 onEnd(new Error('Missing checksum'))
200 if (!value.equals(expected)) {
201 onEnd(new Error('Checksum mismatch: ' +
202 expected.hexSlice() + ' != ' + value.hexSlice()))
203 } else {
204 if (opts.verbosity >= 2)
205 console.error('checksum ok', expected.hexSlice())
206 onEnd(null)
207 }
208 })
209 }
210
211 function readObject(abort, cb) {
212 if (ended) cb(ended)
213 else if (abort) read(abort)
214 else if (numObjects < 0) readHeader(cb)
215 else if (numObjects > 0) getObject(cb)
216 else if (expectChecksum) readTrailer(cb)
217 }
218
219 return readObject
220}
221
222function once(read) {
223 var done
224 return function (abort, cb) {
225 if (done) cb(done)
226 else done = true, read(abort, cb)
227 }
228}
229
230function encodeVarInt(typeStr, length, cb) {
231 var type = objectTypeNums[typeStr]
232 // console.error('TYPE', type, typeStr, 'len', length, typeof cb)
233 if (!type)
234 return cb(new Error("Bad object type " + typeStr))
235
236 var vals = []
237 var b = (type << 4) | (length & 15)
238 for (length >>= 4; length; length >>= 7) {
239 vals.push(b | 0x80)
240 b = length & 0x7f
241 }
242 vals.push(b)
243 console.error('sending var int', vals, vals.map(function (n) {
244 return ('00000000' + Number(n).toString(2)).substr(-8)
245 }))
246 cb(null, new Buffer(vals))
247}
248
249/*
250function flow(read) {
251 return function (abort, cb) {
252 read(abort, cb, function nextCb(newRead, onEnd) {
253 read = newRead
254 return function (end, data) {
255 cb(end, data)
256 if (end) onEnd(end === true ? null : end)
257 }
258 })
259 }
260}
261*/
262
263function encodePack(numObjects, readObject) {
264 // var ended
265 var header = new Buffer(12)
266 header.write('PACK')
267 header.writeUInt32BE(PACK_VERSION, 4)
268 header.writeUInt32BE(numObjects, 8)
269 var checksum = createHash('sha1')
270 var readData
271
272 /*
273 return pull.through(function (data) {
274 console.error('> ' + data.length, JSON.stringify(data.toString('ascii')))
275 })(cat([
276 */
277 return cat([
278 checksum(cat([
279 pull.once(header),
280 encodeObject
281 ])),
282 checksum.readDigest
283 ])
284 // )
285
286 function encodeObject(abort, cb) {
287 if (readData)
288 readData(abort, function (end, data) {
289 if (end === true)
290 readObject(abort, nextObject)
291 else
292 cb(end, data)
293 })
294 else
295 readObject(abort, nextObject)
296
297 function nextObject(end, type, length, read) {
298 // console.error('got obj', end, type, length)
299 if (end) return cb(end)
300 readData = deflate(read)
301 encodeVarInt(type, length, cb) // nextCb(deflate(read), encodeObject))
302 }
303 }
304}
305

Built with git-ssb-web