git ssb

0+

cel / pull-git-remote-helper



Tree: 20c102b7036f7fee19f151e269d33cd7da837a3b

Files: 20c102b7036f7fee19f151e269d33cd7da837a3b / pack.js

5387 bytesRaw
1var buffered = require('pull-buffered')
2var pull = require('pull-stream')
3var toPull = require('stream-to-pull-stream')
4var Inflate = require('pako/lib/inflate').Inflate
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]
17
18function error(cb) {
19 return function (err) {
20 cb(err || true)
21 }
22}
23
24function inflateBytes(read) {
25 var inflate = new Inflate()
26 var ended, dataOut
27
28 inflate.onData = function (data) {
29 dataOut = new Buffer(data)
30 // console.error('inflated data', data.length)
31 }
32
33 inflate.onEnd = function (status) {
34 ended = (status === 0) ? true : new Error(inflate.msg)
35 // console.error('inflated end', status, ended)
36 }
37
38 return function (abort, cb) {
39 if (ended) return cb(ended)
40 read(abort, function next(end, data) {
41 if (end === true) {
42 end = null
43 data = []
44 }
45 if (ended = end) return cb(end)
46 if (data.length > 1) return cb(new Error('got more than one byte'))
47 dataOut = null
48 inflate.push(data, end === true)
49 if (dataOut)
50 cb(null, dataOut)
51 else if (ended)
52 cb(ended)
53 else
54 read(null, next)
55 })
56 }
57}
58
59function decodePack(onEnd, read) {
60 if (read === undefined)
61 return decodePack.bind(this, onEnd)
62
63 var ended
64 var numObjects = -1
65 var checksum = createHash('sha1')
66 var b = buffered(read)
67 // TODO: optimize to pass through buffers to checksum
68 var readByte = checksum(b.chunks(1))
69 var readWord = checksum(b.chunks(4))
70 var readChecksum = b.chunks(20)
71 var expectChecksum = true
72 var opts = {
73 verbosity: 2
74 }
75
76 function readHeader(cb) {
77 readWord(null, function (end, header) {
78 if (ended = end) return cb(end)
79 if (!header.equals(header, new Buffer('PACK')))
80 read(new Error('Invalid packfile header'), error(cb))
81 else
82 readVersion(cb)
83 })
84 }
85
86 function readVersion(cb) {
87 readWord(null, function (end, word) {
88 if (ended = end) return cb(end)
89 var version = word.readUInt32BE()
90 if (version < 2 || version > 3)
91 read(new Error('Invalid packfile version ' + version), error(cb))
92 else
93 readNumObjects(cb)
94 })
95 }
96
97 function readNumObjects(cb) {
98 readWord(null, function (end, word) {
99 if (ended = end) return cb(end)
100 numObjects = word.readUInt32BE()
101 if (opts.verbosity >= 1)
102 console.error(numObjects + ' objects')
103 readObject(null, cb)
104 })
105 }
106
107 function readVarInt(cb) {
108 var type, value, shift
109 // https://codewords.recurse.com/images/three/varint.svg
110 readByte(null, function (end, buf) {
111 if (ended = end) return cb(end)
112 var firstByte = buf[0]
113 type = objectTypes[(firstByte >> 4) & 7]
114 value = firstByte & 15
115 // console.error('byte1', firstByte, firstByte.toString(2), value, value.toString(2))
116 shift = 4
117 checkByte(firstByte)
118 })
119
120 function checkByte(byte) {
121 if (byte & 0x80)
122 readByte(null, gotByte)
123 else
124 cb(null, type, value)
125 }
126
127 function gotByte(end, buf) {
128 if (ended = end) return cb(end)
129 var byte = buf[0]
130 value += (byte & 0x7f) << shift
131 shift += 7
132 // console.error('byte', byte, byte.toString(2), value, value.toString(2))
133 checkByte(byte)
134 }
135 }
136
137 function getObject(cb) {
138 readVarInt(function (end, type, length) {
139 if (opts.verbosity >= 2)
140 console.error('read var int', end, type, length)
141 numObjects--
142 if (end === true && expectChecksum)
143 onEnd(new Error('Missing checksum'))
144 if (ended = end) return cb(end)
145 // TODO: verify that the inflated data is the correct length
146 cb(null, type, length, inflateBytes(readByte))
147 })
148 }
149
150 function readTrailer(cb) {
151 // read the checksum before it updates to include the trailer
152 var expected = checksum.digest()
153 readChecksum(null, function (end, value) {
154 cb(true)
155 if (end === true && expectChecksum)
156 onEnd(new Error('Missing checksum'))
157 if (!value.equals(expected)) {
158 onEnd(new Error('Checksum mismatch: ' +
159 expected.hexSlice() + ' != ' + value.hexSlice()))
160 } else {
161 if (opts.verbosity >= 2)
162 console.error('checksum ok', expected.hexSlice())
163 onEnd(null)
164 }
165 })
166 }
167
168 function readObject(abort, cb) {
169 if (ended) cb(ended)
170 else if (abort) read(abort)
171 else if (numObjects < 0) readHeader(cb)
172 else if (numObjects > 0) getObject(cb)
173 else if (expectChecksum) readTrailer(cb)
174 }
175
176 return readObject
177}
178
179function encodePack(numObjects, readObject) {
180 var ended
181 var header = new Buffer(12)
182 header.write('PACK')
183 header.writeUInt32BE(PACK_VERSION, 4)
184 header.writeUInt32BE(numObjects, 8)
185 var checksum = createHash('sha1')
186
187 return pull.through(function (data) {
188 console.error('> ' + data.length, data.toString())
189 })(cat([
190 checksum(cat([
191 pull.once(header),
192 function (abort, cb) {
193 if (ended) return cb(ended)
194 readObject(abort, function (end, type, length, read) {
195 if (ended = end) return cb(end)
196 console.error('TODO: encode object')
197 })
198 }
199 ])),
200 checksum.readDigest
201 ]))
202}
203

Built with git-ssb-web