UNPKG

10.5 kBJavaScriptView Raw
1// encode.js
2
3exports.Encoder = Encoder;
4
5var BufferLite = require("./buffer-lite");
6var extencoders = require("./ext").encoders;
7var ExtBuffer = require("./ext-buffer").ExtBuffer;
8
9var BUFFER_LENGTH = 2048;
10var NO_ASSERT = true;
11var IS_BUFFER_SHIM = ("TYPED_ARRAY_SUPPORT" in Buffer);
12var NO_TYPED_ARRAY = IS_BUFFER_SHIM && !Buffer.TYPED_ARRAY_SUPPORT;
13var FUNCTION_HAS_NAME = Encoder.name;
14var IS_ARRAY = Array.isArray || isArray;
15
16function Encoder(opts) {
17 if (!(this instanceof Encoder)) return new Encoder(opts);
18 if (opts && opts.push) this.push = opts.push.bind(opts);
19}
20
21Encoder.prototype.encode = function(chunk, encoding, callback) {
22 if (!this.buffer) this.flush();
23 encode(this, chunk);
24 this.flush();
25 if (callback) callback();
26};
27
28Encoder.prototype.flush = function(length) {
29 if (this.offset) {
30 this.push(this.buffer.slice(0, this.offset));
31 this.buffer = null;
32 }
33 if (length < BUFFER_LENGTH) length = 0;
34 if (!this.buffer || length) {
35 this.buffer = new Buffer(length || BUFFER_LENGTH);
36 }
37 this.offset = 0;
38};
39
40Encoder.prototype.write = function(buffer) {
41 var end = this.offset + buffer.length;
42 if (end >= BUFFER_LENGTH) {
43 this.flush();
44 this.push(buffer);
45 } else {
46 buffer.copy(this.buffer, this.offset);
47 this.offset = end;
48 }
49};
50
51var constant = new Array(256);
52var token = new Array(256);
53init();
54
55function init() {
56 // positive fixint -- 0x00 - 0x7f
57 // nil -- 0xc0
58 // false -- 0xc2
59 // true -- 0xc3
60 // negative fixint -- 0xe0 - 0xff
61 for (var i = 0x00; i <= 0xFF; i++) {
62 token[i] = constant[i] = write0(i);
63 }
64
65 if (NO_TYPED_ARRAY) {
66 init_compatible(); // old browsers
67 } else {
68 init_optimized(); // Node.js and browsers with TypedArray
69 }
70}
71
72function init_optimized() {
73 // bin 8 -- 0xc4
74 // bin 16 -- 0xc5
75 // bin 32 -- 0xc6
76 token[0xc4] = write1(0xc4);
77 token[0xc5] = write2(0xc5);
78 token[0xc6] = write4(0xc6);
79
80 // ext 8 -- 0xc7
81 // ext 16 -- 0xc8
82 // ext 32 -- 0xc9
83 token[0xc7] = write1(0xc7);
84 token[0xc8] = write2(0xc8);
85 token[0xc9] = write4(0xc9);
86
87 // float 32 -- 0xca
88 // float 64 -- 0xcb
89 token[0xca] = writeN(0xca, 4, Buffer.prototype.writeFloatBE);
90 token[0xcb] = writeN(0xcb, 8, Buffer.prototype.writeDoubleBE);
91
92 // uint 8 -- 0xcc
93 // uint 16 -- 0xcd
94 // uint 32 -- 0xce
95 // uint 64 -- 0xcf
96 token[0xcc] = write1(0xcc);
97 token[0xcd] = write2(0xcd);
98 token[0xce] = write4(0xce);
99 token[0xcf] = writeN(0xcf, 8, BufferLite.writeUint64BE);
100
101 // int 8 -- 0xd0
102 // int 16 -- 0xd1
103 // int 32 -- 0xd2
104 // int 64 -- 0xd3
105 token[0xd0] = write1(0xd0);
106 token[0xd1] = write2(0xd1);
107 token[0xd2] = write4(0xd2);
108 token[0xd3] = writeN(0xd3, 8, BufferLite.writeUint64BE);
109
110 // str 8 -- 0xd9
111 // str 16 -- 0xda
112 // str 32 -- 0xdb
113 // array 16 -- 0xdc
114 // array 32 -- 0xdd
115 // map 16 -- 0xde
116 // map 32 -- 0xdf
117 token[0xd9] = write1(0xd9);
118 token[0xda] = write2(0xda);
119 token[0xdb] = write4(0xdb);
120 token[0xdc] = write2(0xdc);
121 token[0xdd] = write4(0xdd);
122 token[0xde] = write2(0xde);
123 token[0xdf] = write4(0xdf);
124}
125
126function init_compatible() {
127 token[0xc4] = writeN(0xc4, 1, Buffer.prototype.writeUInt8);
128 token[0xc5] = writeN(0xc5, 2, Buffer.prototype.writeUInt16BE);
129 token[0xc6] = writeN(0xc6, 4, Buffer.prototype.writeUInt32BE);
130 token[0xc7] = writeN(0xc7, 1, Buffer.prototype.writeUInt8);
131 token[0xc8] = writeN(0xc8, 2, Buffer.prototype.writeUInt16BE);
132 token[0xc9] = writeN(0xc9, 4, Buffer.prototype.writeUInt32BE);
133 token[0xca] = writeN(0xca, 4, Buffer.prototype.writeFloatBE);
134 token[0xcb] = writeN(0xcb, 8, Buffer.prototype.writeDoubleBE);
135 token[0xcc] = writeN(0xcc, 1, Buffer.prototype.writeUInt8);
136 token[0xcd] = writeN(0xcd, 2, Buffer.prototype.writeUInt16BE);
137 token[0xce] = writeN(0xce, 4, Buffer.prototype.writeUInt32BE);
138 token[0xcf] = writeN(0xcf, 8, BufferLite.writeUint64BE);
139 token[0xd0] = writeN(0xd0, 1, Buffer.prototype.writeInt8);
140 token[0xd1] = writeN(0xd1, 2, Buffer.prototype.writeInt16BE);
141 token[0xd2] = writeN(0xd2, 4, Buffer.prototype.writeInt32BE);
142 token[0xd3] = writeN(0xd3, 8, BufferLite.writeUint64BE);
143 token[0xd9] = writeN(0xd9, 1, Buffer.prototype.writeUInt8);
144 token[0xda] = writeN(0xda, 2, Buffer.prototype.writeUInt16BE);
145 token[0xdb] = writeN(0xdb, 4, Buffer.prototype.writeUInt32BE);
146 token[0xdc] = writeN(0xdc, 2, Buffer.prototype.writeUInt16BE);
147 token[0xdd] = writeN(0xdd, 4, Buffer.prototype.writeUInt32BE);
148 token[0xde] = writeN(0xde, 2, Buffer.prototype.writeUInt16BE);
149 token[0xdf] = writeN(0xdf, 4, Buffer.prototype.writeUInt32BE);
150}
151
152function write0(type) {
153 return function(encoder) {
154 var end = encoder.offset + 1;
155 if (end >= BUFFER_LENGTH) encoder.flush();
156 encoder.buffer[encoder.offset++] = type;
157 };
158}
159
160function write1(type) {
161 return function(encoder, value) {
162 var end = encoder.offset + 2;
163 if (end >= BUFFER_LENGTH) encoder.flush();
164 var buffer = encoder.buffer;
165 var offset = encoder.offset;
166 buffer[offset++] = type;
167 buffer[offset++] = value;
168 encoder.offset = offset;
169 };
170}
171
172function write2(type) {
173 return function(encoder, value) {
174 var end = encoder.offset + 3;
175 if (end >= BUFFER_LENGTH) encoder.flush();
176 var buffer = encoder.buffer;
177 var offset = encoder.offset;
178 buffer[offset++] = type;
179 buffer[offset++] = value >>> 8;
180 buffer[offset++] = value;
181 encoder.offset = offset;
182 };
183}
184
185function write4(type) {
186 return function(encoder, value) {
187 var end = encoder.offset + 5;
188 if (end >= BUFFER_LENGTH) encoder.flush();
189 var buffer = encoder.buffer;
190 var offset = encoder.offset;
191 buffer[offset++] = type;
192 buffer[offset++] = value >>> 24;
193 buffer[offset++] = value >>> 16;
194 buffer[offset++] = value >>> 8;
195 buffer[offset++] = value;
196 encoder.offset = offset;
197 };
198}
199
200function writeN(type, len, method) {
201 return function(encoder, value) {
202 var end = encoder.offset + 1 + len;
203 if (end >= BUFFER_LENGTH) encoder.flush();
204 encoder.buffer[encoder.offset++] = type;
205 method.call(encoder.buffer, value, encoder.offset, NO_ASSERT);
206 encoder.offset += len;
207 };
208}
209
210var types = {
211 "string": string,
212 "number": number,
213 "object": object,
214 "boolean": bool,
215 "undefined": token[0xc0]
216};
217
218function encode(encoder, value) {
219 var func = types[typeof value] || NA;
220 func(encoder, value);
221}
222
223function NA(encoder, value) {
224 throw new Error("Unknown supported type \"" + (typeof value) + "\": " + value);
225}
226
227function bool(encoder, value) {
228 // false -- 0xc2
229 // true -- 0xc3
230 var type = value ? 0xc3 : 0xc2;
231 token[type](encoder, value);
232}
233
234function number(encoder, value) {
235 var ivalue = value | 0;
236 var type;
237 if (value !== ivalue) {
238 // float 64 -- 0xcb
239 type = 0xcb;
240 token[type](encoder, value);
241 return;
242 } else if (-0x20 <= ivalue && ivalue <= 0x7F) {
243 // positive fixint -- 0x00 - 0x7f
244 // negative fixint -- 0xe0 - 0xff
245 type = ivalue & 0xFF;
246 } else if (0 <= ivalue) {
247 // uint 8 -- 0xcc
248 // uint 16 -- 0xcd
249 // uint 32 -- 0xce
250 // uint 64 -- 0xcf
251 type = (ivalue <= 0xFF) ? 0xcc : (ivalue <= 0xFFFF) ? 0xcd : 0xce;
252 } else {
253 // int 8 -- 0xd0
254 // int 16 -- 0xd1
255 // int 32 -- 0xd2
256 // int 64 -- 0xd3
257 type = (-0x80 <= ivalue) ? 0xd0 : (-0x8000 <= ivalue) ? 0xd1 : 0xd2;
258 }
259 token[type](encoder, ivalue);
260}
261
262function string(encoder, value) {
263 // str 8 -- 0xd9
264 // str 16 -- 0xda
265 // str 32 -- 0xdb
266 // fixstr -- 0xa0 - 0xbf
267
268 // prepare buffer
269 var length = value.length;
270 var maxsize = 5 + length * 3;
271 if (encoder.offset + maxsize > BUFFER_LENGTH) {
272 encoder.flush(maxsize);
273 }
274
275 // expected header size
276 var expected = (length < 32) ? 1 : (length <= 0xFF) ? 2 : (length <= 0xFFFF) ? 3 : 5;
277
278 // expected start point
279 var start = encoder.offset + expected;
280
281 // write string
282 length = BufferLite.writeString.call(encoder.buffer, value, start);
283
284 // actual header size
285 var actual = (length < 32) ? 1 : (length <= 0xFF) ? 2 : (length <= 0xFFFF) ? 3 : 5;
286
287 // move content when needed
288 if (expected !== actual) {
289 var targetStart = encoder.offset + actual;
290 var end = start + length;
291 if (IS_BUFFER_SHIM) {
292 BufferLite.copy.call(encoder.buffer, encoder.buffer, targetStart, start, end);
293 } else {
294 encoder.buffer.copy(encoder.buffer, targetStart, start, end);
295 }
296 }
297
298 // write header
299 var type = (actual === 1) ? (0xa0 + length) : (actual <= 3) ? 0xd7 + actual : 0xdb;
300 token[type](encoder, length);
301
302 // move cursor
303 encoder.offset += length;
304}
305
306var extmap = [];
307extmap[1] = 0xd4;
308extmap[2] = 0xd5;
309extmap[4] = 0xd6;
310extmap[8] = 0xd7;
311extmap[16] = 0xd8;
312
313function object(encoder, value) {
314 var type;
315 var length;
316 if (value === null) {
317 // nil -- 0xc0
318 type = 0xc0;
319 token[type](encoder, value);
320 } else if (IS_ARRAY(value)) {
321 // fixarray -- 0x90 - 0x9f
322 // array 16 -- 0xdc
323 // array 32 -- 0xdd
324 length = value.length;
325 type = (length < 16) ? (0x90 + length) : (length <= 0xFFFF) ? 0xdc : 0xdd;
326 token[type](encoder, length);
327 for (var i = 0; i < length; i++) {
328 encode(encoder, value[i]);
329 }
330 } else if (Buffer.isBuffer(value)) {
331 // bin 8 -- 0xc4
332 // bin 16 -- 0xc5
333 // bin 32 -- 0xc6
334 length = value.length;
335 type = (length < 0xFF) ? 0xc4 : (length <= 0xFFFF) ? 0xc5 : 0xc6;
336 token[type](encoder, length);
337 encoder.write(value);
338 } else {
339 var n = value.constructor && value.constructor.name;
340 if (!n && !FUNCTION_HAS_NAME && value instanceof ExtBuffer) n = "ExtBuffer";
341 var e = extencoders[n];
342 if (e) {
343 // fixext 1 -- 0xd4
344 // fixext 2 -- 0xd5
345 // fixext 4 -- 0xd6
346 // fixext 8 -- 0xd7
347 // fixext 16 -- 0xd8
348 // ext 8 -- 0xc7
349 // ext 16 -- 0xc8
350 // ext 32 -- 0xc9
351 var etype = e.type(value);
352 var buf = e.encode(value);
353 length = buf.length;
354 type = extmap[length] || ((length < 0xFF) ? 0xc7 : (length <= 0xFFFF) ? 0xc8 : 0xc9);
355 token[type](encoder, length);
356 constant[etype](encoder);
357 encoder.write(buf);
358 return;
359 }
360
361 // fixmap -- 0x80 - 0x8f
362 // map 16 -- 0xde
363 // map 32 -- 0xdf
364 var keys = Object.keys(value);
365 length = keys.length;
366 type = (length < 16) ? (0x80 + length) : (length <= 0xFFFF) ? 0xde : 0xdf;
367 token[type](encoder, length);
368 keys.forEach(function(key) {
369 encode(encoder, key);
370 encode(encoder, value[key]);
371 });
372 }
373}
374
375function isArray(array) {
376 return "[object Array]" === Object.prototype.toString.call(array);
377}