Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 'use strict';
7 module.metadata = {
8 'stability': 'experimental'
9 };
11 /*
12 * Encodings supported by TextEncoder/Decoder:
13 * utf-8, utf-16le, utf-16be
14 * http://encoding.spec.whatwg.org/#interface-textencoder
15 *
16 * Node however supports the following encodings:
17 * ascii, utf-8, utf-16le, usc2, base64, hex
18 */
20 const { Cu } = require('chrome');
21 const { isNumber } = require('sdk/lang/type');
22 const { TextEncoder, TextDecoder } = Cu.import('resource://gre/modules/commonjs/toolkit/loader.js', {});
24 exports.TextEncoder = TextEncoder;
25 exports.TextDecoder = TextDecoder;
27 /**
28 * Use WeakMaps to work around Bug 929146, which prevents us from adding
29 * getters or values to typed arrays
30 * https://bugzilla.mozilla.org/show_bug.cgi?id=929146
31 */
32 const parents = new WeakMap();
33 const views = new WeakMap();
35 function Buffer(subject, encoding /*, bufferLength */) {
37 // Allow invocation without `new` constructor
38 if (!(this instanceof Buffer))
39 return new Buffer(subject, encoding, arguments[2]);
41 var type = typeof(subject);
43 switch (type) {
44 case 'number':
45 // Create typed array of the given size if number.
46 try {
47 let buffer = new Uint8Array(subject > 0 ? Math.floor(subject) : 0);
48 return buffer;
49 } catch (e) {
50 if (/size and count too large/.test(e.message) ||
51 /invalid arguments/.test(e.message))
52 throw new RangeError('Could not instantiate buffer: size of buffer may be too large');
53 else
54 throw new Error('Could not instantiate buffer');
55 }
56 break;
57 case 'string':
58 // If string encode it and use buffer for the returned Uint8Array
59 // to create a local patched version that acts like node buffer.
60 encoding = encoding || 'utf8';
61 return new Uint8Array(new TextEncoder(encoding).encode(subject).buffer);
62 case 'object':
63 // This form of the constructor uses the form of
64 // new Uint8Array(buffer, offset, length);
65 // So we can instantiate a typed array within the constructor
66 // to inherit the appropriate properties, where both the
67 // `subject` and newly instantiated buffer share the same underlying
68 // data structure.
69 if (arguments.length === 3)
70 return new Uint8Array(subject, encoding, arguments[2]);
71 // If array or alike just make a copy with a local patched prototype.
72 else
73 return new Uint8Array(subject);
74 default:
75 throw new TypeError('must start with number, buffer, array or string');
76 }
77 }
78 exports.Buffer = Buffer;
80 // Tests if `value` is a Buffer.
81 Buffer.isBuffer = value => value instanceof Buffer
83 // Returns true if the encoding is a valid encoding argument & false otherwise
84 Buffer.isEncoding = function (encoding) {
85 if (!encoding) return false;
86 try {
87 new TextDecoder(encoding);
88 } catch(e) {
89 return false;
90 }
91 return true;
92 }
94 // Gives the actual byte length of a string. encoding defaults to 'utf8'.
95 // This is not the same as String.prototype.length since that returns the
96 // number of characters in a string.
97 Buffer.byteLength = (value, encoding = 'utf8') =>
98 new TextEncoder(encoding).encode(value).byteLength
100 // Direct copy of the nodejs's buffer implementation:
101 // https://github.com/joyent/node/blob/b255f4c10a80343f9ce1cee56d0288361429e214/lib/buffer.js#L146-L177
102 Buffer.concat = function(list, length) {
103 if (!Array.isArray(list))
104 throw new TypeError('Usage: Buffer.concat(list[, length])');
106 if (typeof length === 'undefined') {
107 length = 0;
108 for (var i = 0; i < list.length; i++)
109 length += list[i].length;
110 } else {
111 length = ~~length;
112 }
114 if (length < 0)
115 length = 0;
117 if (list.length === 0)
118 return new Buffer(0);
119 else if (list.length === 1)
120 return list[0];
122 if (length < 0)
123 throw new RangeError('length is not a positive number');
125 var buffer = new Buffer(length);
126 var pos = 0;
127 for (var i = 0; i < list.length; i++) {
128 var buf = list[i];
129 buf.copy(buffer, pos);
130 pos += buf.length;
131 }
133 return buffer;
134 };
136 // Node buffer is very much like Uint8Array although it has bunch of methods
137 // that typically can be used in combination with `DataView` while preserving
138 // access by index. Since in SDK each module has it's own set of bult-ins it
139 // ok to patch ours to make it nodejs Buffer compatible.
140 Buffer.prototype = Uint8Array.prototype;
141 Object.defineProperties(Buffer.prototype, {
142 parent: {
143 get: function() { return parents.get(this, undefined); }
144 },
145 view: {
146 get: function () {
147 let view = views.get(this, undefined);
148 if (view) return view;
149 view = new DataView(this.buffer);
150 views.set(this, view);
151 return view;
152 }
153 },
154 toString: {
155 value: function(encoding, start, end) {
156 encoding = !!encoding ? (encoding + '').toLowerCase() : 'utf8';
157 start = Math.max(0, ~~start);
158 end = Math.min(this.length, end === void(0) ? this.length : ~~end);
159 return new TextDecoder(encoding).decode(this.subarray(start, end));
160 }
161 },
162 toJSON: {
163 value: function() {
164 return { type: 'Buffer', data: Array.slice(this, 0) };
165 }
166 },
167 get: {
168 value: function(offset) {
169 return this[offset];
170 }
171 },
172 set: {
173 value: function(offset, value) { this[offset] = value; }
174 },
175 copy: {
176 value: function(target, offset, start, end) {
177 let length = this.length;
178 let targetLength = target.length;
179 offset = isNumber(offset) ? offset : 0;
180 start = isNumber(start) ? start : 0;
182 if (start < 0)
183 throw new RangeError('sourceStart is outside of valid range');
184 if (end < 0)
185 throw new RangeError('sourceEnd is outside of valid range');
187 // If sourceStart > sourceEnd, or targetStart > targetLength,
188 // zero bytes copied
189 if (start > end ||
190 offset > targetLength
191 )
192 return 0;
194 // If `end` is not defined, or if it is defined
195 // but would overflow `target`, redefine `end`
196 // so we can copy as much as we can
197 if (end - start > targetLength - offset ||
198 end == null) {
199 let remainingTarget = targetLength - offset;
200 let remainingSource = length - start;
201 if (remainingSource <= remainingTarget)
202 end = length;
203 else
204 end = start + remainingTarget;
205 }
207 Uint8Array.set(target, this.subarray(start, end), offset);
208 return end - start;
209 }
210 },
211 slice: {
212 value: function(start, end) {
213 let length = this.length;
214 start = ~~start;
215 end = end != null ? end : length;
217 if (start < 0) {
218 start += length;
219 if (start < 0) start = 0;
220 } else if (start > length)
221 start = length;
223 if (end < 0) {
224 end += length;
225 if (end < 0) end = 0;
226 } else if (end > length)
227 end = length;
229 if (end < start)
230 end = start;
232 // This instantiation uses the new Uint8Array(buffer, offset, length) version
233 // of construction to share the same underling data structure
234 let buffer = new Buffer(this.buffer, start, end - start);
236 // If buffer has a value, assign its parent value to the
237 // buffer it shares its underlying structure with. If a slice of
238 // a slice, then use the root structure
239 if (buffer.length > 0)
240 parents.set(buffer, this.parent || this);
242 return buffer;
243 }
244 },
245 write: {
246 value: function(string, offset, length, encoding = 'utf8') {
247 // write(string, encoding);
248 if (typeof(offset) === 'string' && Number.isNaN(parseInt(offset))) {
249 ([offset, length, encoding]) = [0, null, offset];
250 }
251 // write(string, offset, encoding);
252 else if (typeof(length) === 'string')
253 ([length, encoding]) = [null, length];
255 if (offset < 0 || offset > this.length)
256 throw new RangeError('offset is outside of valid range');
258 offset = ~~offset;
260 // Clamp length if it would overflow buffer, or if its
261 // undefined
262 if (length == null || length + offset > this.length)
263 length = this.length - offset;
265 let buffer = new TextEncoder(encoding).encode(string);
266 let result = Math.min(buffer.length, length);
267 if (buffer.length !== length)
268 buffer = buffer.subarray(0, length);
270 Uint8Array.set(this, buffer, offset);
271 return result;
272 }
273 },
274 fill: {
275 value: function fill(value, start, end) {
276 let length = this.length;
277 value = value || 0;
278 start = start || 0;
279 end = end || length;
281 if (typeof(value) === 'string')
282 value = value.charCodeAt(0);
283 if (typeof(value) !== 'number' || isNaN(value))
284 throw TypeError('value is not a number');
285 if (end < start)
286 throw new RangeError('end < start');
288 // Fill 0 bytes; we're done
289 if (end === start)
290 return 0;
291 if (length == 0)
292 return 0;
294 if (start < 0 || start >= length)
295 throw RangeError('start out of bounds');
297 if (end < 0 || end > length)
298 throw RangeError('end out of bounds');
300 let index = start;
301 while (index < end) this[index++] = value;
302 }
303 }
304 });
306 // Define nodejs Buffer's getter and setter functions that just proxy
307 // to internal DataView's equivalent methods.
309 // TODO do we need to check architecture to see if it's default big/little endian?
310 [['readUInt16LE', 'getUint16', true],
311 ['readUInt16BE', 'getUint16', false],
312 ['readInt16LE', 'getInt16', true],
313 ['readInt16BE', 'getInt16', false],
314 ['readUInt32LE', 'getUint32', true],
315 ['readUInt32BE', 'getUint32', false],
316 ['readInt32LE', 'getInt32', true],
317 ['readInt32BE', 'getInt32', false],
318 ['readFloatLE', 'getFloat32', true],
319 ['readFloatBE', 'getFloat32', false],
320 ['readDoubleLE', 'getFloat64', true],
321 ['readDoubleBE', 'getFloat64', false],
322 ['readUInt8', 'getUint8'],
323 ['readInt8', 'getInt8']].forEach(([alias, name, littleEndian]) => {
324 Object.defineProperty(Buffer.prototype, alias, {
325 value: function(offset) this.view[name](offset, littleEndian)
326 });
327 });
329 [['writeUInt16LE', 'setUint16', true],
330 ['writeUInt16BE', 'setUint16', false],
331 ['writeInt16LE', 'setInt16', true],
332 ['writeInt16BE', 'setInt16', false],
333 ['writeUInt32LE', 'setUint32', true],
334 ['writeUInt32BE', 'setUint32', false],
335 ['writeInt32LE', 'setInt32', true],
336 ['writeInt32BE', 'setInt32', false],
337 ['writeFloatLE', 'setFloat32', true],
338 ['writeFloatBE', 'setFloat32', false],
339 ['writeDoubleLE', 'setFloat64', true],
340 ['writeDoubleBE', 'setFloat64', false],
341 ['writeUInt8', 'setUint8'],
342 ['writeInt8', 'setInt8']].forEach(([alias, name, littleEndian]) => {
343 Object.defineProperty(Buffer.prototype, alias, {
344 value: function(value, offset) this.view[name](offset, value, littleEndian)
345 });
346 });