1 | var stream = require('stream');
|
2 |
|
3 | var crc32 = require('buffer-crc32');
|
4 |
|
5 | var liner = new stream.Transform ({objectMode: true});
|
6 |
|
7 | function XZFormat (chunk) {
|
8 | this.buf = new Buffer (chunk, "binary");
|
9 | }
|
10 |
|
11 | if (!Buffer.prototype.equals)
|
12 | Buffer.prototype.equals = function (a) {
|
13 | if (!Buffer.isBuffer(a)) return undefined;
|
14 | if (a.length !== this.length) return false;
|
15 |
|
16 | for (var i = 0; i < a.length; i++) {
|
17 | if (a[i] !== this[i]) return false;
|
18 | }
|
19 |
|
20 | return true;
|
21 | };
|
22 |
|
23 | function encMultiInt (num) {
|
24 | var buf = new Buffer (9);
|
25 | if (num > Math.pow (2, 52))
|
26 | return null;
|
27 |
|
28 | var i = 0;
|
29 |
|
30 | while (num >= 0x80) {
|
31 | buf.writeUInt8 (i, num & 127);
|
32 | num >>= 7;
|
33 | i++;
|
34 | }
|
35 |
|
36 | buf.writeUInt8 (i, num);
|
37 |
|
38 | return buf.slice (0, i);
|
39 | }
|
40 |
|
41 | function decMultiInt (buf) {
|
42 |
|
43 | var i = 0;
|
44 | var num;
|
45 | var byte;
|
46 |
|
47 | while (i < buf.length) {
|
48 | byte = buf.readUInt8 (i);
|
49 | num |= byte << i * 7;
|
50 | if (byte | 127 === 0) {
|
51 | break;
|
52 | }
|
53 | }
|
54 |
|
55 | return num;
|
56 | }
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | XZFormat.prototype.headerMagic = new Buffer ("\xFD7zXZ\x00", "binary");
|
69 |
|
70 | XZFormat.prototype.readHeader = function () {
|
71 | if (this.buf.length < 12) {
|
72 | return;
|
73 | }
|
74 |
|
75 | console.log (this.buf);
|
76 |
|
77 | var magicOk = this.headerMagic.equals (this.buf.slice (0, 6));
|
78 | if (!magicOk) {
|
79 | throw "wrong magic!";
|
80 | }
|
81 |
|
82 | var nullByte = this.buf.readUInt8(6);
|
83 | var flags = this.buf.readUInt8(7);
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | var streamHasCRC32 = flags & 0x1 ? true: false;
|
95 | var streamHasCRC64 = flags & 0x4 ? true: false;
|
96 | var streamHasSHA256 = flags & 0xA ? true: false;
|
97 |
|
98 | var flagsB = new Buffer(2);
|
99 | flagsB.writeUInt8 (nullByte, 0);
|
100 | flagsB.writeUInt8 (flags, 1);
|
101 | var flagsCRC = crc32 (flagsB);
|
102 |
|
103 | var headerCRC = this.buf.slice (8, 12);
|
104 |
|
105 |
|
106 | console.log (magicOk, flags, {
|
107 | CRC32: streamHasCRC32,
|
108 | CRC64: streamHasCRC64,
|
109 | SHA256: streamHasSHA256,
|
110 | }, flagsCRC, headerCRC);
|
111 |
|
112 | this.buf = this.buf.slice (12);
|
113 |
|
114 | this.streamHeaderRead = true;
|
115 | }
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | XZFormat.prototype.footerMagic = new Buffer ("YZ", "binary");
|
122 |
|
123 | XZFormat.prototype.readFooter = function () {
|
124 |
|
125 | }
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | function XZBlock (buf) {
|
132 | this.buf = buf;
|
133 | }
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | XZBlock.prototype.read = function () {
|
148 | if (!this.headerRead && this.buf.length >= 20) {
|
149 | this.readHeader ()
|
150 | return;
|
151 | }
|
152 | }
|
153 |
|
154 | XZBlock.prototype.readHeader = function () {
|
155 | var blockHeaderSizeField = this.buf.readUInt8 (0)
|
156 | var blockHeaderSize = (blockHeaderSizeField + 1) * 4;
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 | var blockFlags = this.buf.readUInt8 (1);
|
163 | var blockFilters = blockFlags & 0x03;
|
164 | var blockReserved = blockFlags & 0x3c;
|
165 | var haveComprSize = blockFlags & 0x40;
|
166 | var haveUncomprSize = blockFlags & 0x80;
|
167 | var uncomprOffset = 0;
|
168 |
|
169 | var blockHeaderCRC = this.buf.slice (blockHeaderSize - 4, blockHeaderSize);
|
170 |
|
171 | if (blockReserved) {
|
172 | throw "reserved must be 0";
|
173 | }
|
174 |
|
175 | console.log (this.buf, blockHeaderSize, blockFlags, blockReserved, haveComprSize, haveUncomprSize);
|
176 |
|
177 |
|
178 |
|
179 | var comprSizeBuf = this.buf.slice (2, 11);
|
180 | console.log (comprSizeBuf);
|
181 | var comprSize = decMultiInt (comprSizeBuf);
|
182 | uncomprOffset = comprSizeBuf.length;
|
183 |
|
184 |
|
185 | console.log (uncomprOffset);
|
186 |
|
187 |
|
188 | var uncomprSizeBuf = this.buf.slice (2 + uncomprOffset, 11 + uncomprOffset);
|
189 | var uncomprSize = decMultiInt (uncomprSizeBuf);
|
190 |
|
191 |
|
192 | if (blockHeaderSizeField === 0) {
|
193 |
|
194 | throw "index not supported";
|
195 | }
|
196 |
|
197 | if (blockFilters) {
|
198 | throw "filters not supported";
|
199 | }
|
200 |
|
201 |
|
202 |
|
203 | console.log (
|
204 | "block header size:", blockHeaderSize,
|
205 | "compressed size:", comprSize,
|
206 | "uncompressed size:", uncomprSize,
|
207 | "header crc:", blockHeaderCRC
|
208 | );
|
209 |
|
210 | }
|
211 |
|
212 |
|
213 | XZFormat.prototype.append = function (chunk) {
|
214 | this.buf += chunk;
|
215 | }
|
216 |
|
217 | XZFormat.prototype.readBlock = function () {
|
218 | this.currentBlock = new XZBlock (this.buf);
|
219 |
|
220 | this.currentBlock.read();
|
221 | }
|
222 |
|
223 | liner._transform = function (chunk, encoding, done) {
|
224 |
|
225 | if (!this.xz) {
|
226 | this.xz = new XZFormat (chunk);
|
227 | } else {
|
228 | this.xz.append (chunk);
|
229 | }
|
230 |
|
231 | if (!this.xz.streamHeaderRead) {
|
232 | this.xz.readHeader();
|
233 | this.xz.readBlock();
|
234 | return;
|
235 | } else {
|
236 |
|
237 | }
|
238 |
|
239 | var data = chunk.toString();
|
240 | if (this._lastLineData) data = this._lastLineData + data;
|
241 |
|
242 | var lines = data.split('\n');
|
243 | this._lastLineData = lines.splice(lines.length-1,1)[0];
|
244 |
|
245 | lines.forEach(this.push.bind(this));
|
246 | done();
|
247 | }
|
248 |
|
249 | liner._flush = function (done) {
|
250 | if (this._lastLineData) this.push(this._lastLineData);
|
251 | this._lastLineData = null;
|
252 | done();
|
253 | }
|
254 |
|
255 | module.exports = liner;
|