793b56f92b033251b1e5fdfc7bd50cdd5afbfb26
[aai/esr-gui.git] /
1 /**
2  * Module dependencies.
3  * @ignore
4  */
5
6 // Test if we're in Node via presence of "global" not absence of "window"
7 // to support hybrid environments like Electron
8 if(typeof global !== 'undefined') {
9   var Buffer = require('buffer').Buffer; // TODO just use global Buffer
10 }
11
12 /**
13  * A class representation of the BSON Binary type.
14  *
15  * Sub types
16  *  - **BSON.BSON_BINARY_SUBTYPE_DEFAULT**, default BSON type.
17  *  - **BSON.BSON_BINARY_SUBTYPE_FUNCTION**, BSON function type.
18  *  - **BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY**, BSON byte array type.
19  *  - **BSON.BSON_BINARY_SUBTYPE_UUID**, BSON uuid type.
20  *  - **BSON.BSON_BINARY_SUBTYPE_MD5**, BSON md5 type.
21  *  - **BSON.BSON_BINARY_SUBTYPE_USER_DEFINED**, BSON user defined type.
22  *
23  * @class
24  * @param {Buffer} buffer a buffer object containing the binary data.
25  * @param {Number} [subType] the option binary type.
26  * @return {Binary}
27  */
28 function Binary(buffer, subType) {
29   if(!(this instanceof Binary)) return new Binary(buffer, subType);
30
31   this._bsontype = 'Binary';
32
33   if(buffer instanceof Number) {
34     this.sub_type = buffer;
35     this.position = 0;
36   } else {
37     this.sub_type = subType == null ? BSON_BINARY_SUBTYPE_DEFAULT : subType;
38     this.position = 0;
39   }
40
41   if(buffer != null && !(buffer instanceof Number)) {
42     // Only accept Buffer, Uint8Array or Arrays
43     if(typeof buffer == 'string') {
44       // Different ways of writing the length of the string for the different types
45       if(typeof Buffer != 'undefined') {
46         this.buffer = new Buffer(buffer);
47       } else if(typeof Uint8Array != 'undefined' || (Object.prototype.toString.call(buffer) == '[object Array]')) {
48         this.buffer = writeStringToArray(buffer);
49       } else {
50         throw new Error("only String, Buffer, Uint8Array or Array accepted");
51       }
52     } else {
53       this.buffer = buffer;
54     }
55     this.position = buffer.length;
56   } else {
57     if(typeof Buffer != 'undefined') {
58       this.buffer =  new Buffer(Binary.BUFFER_SIZE);
59     } else if(typeof Uint8Array != 'undefined'){
60       this.buffer = new Uint8Array(new ArrayBuffer(Binary.BUFFER_SIZE));
61     } else {
62       this.buffer = new Array(Binary.BUFFER_SIZE);
63     }
64     // Set position to start of buffer
65     this.position = 0;
66   }
67 };
68
69 /**
70  * Updates this binary with byte_value.
71  *
72  * @method
73  * @param {string} byte_value a single byte we wish to write.
74  */
75 Binary.prototype.put = function put(byte_value) {
76   // If it's a string and a has more than one character throw an error
77   if(byte_value['length'] != null && typeof byte_value != 'number' && byte_value.length != 1) throw new Error("only accepts single character String, Uint8Array or Array");
78   if(typeof byte_value != 'number' && byte_value < 0 || byte_value > 255) throw new Error("only accepts number in a valid unsigned byte range 0-255");
79
80   // Decode the byte value once
81   var decoded_byte = null;
82   if(typeof byte_value == 'string') {
83     decoded_byte = byte_value.charCodeAt(0);
84   } else if(byte_value['length'] != null) {
85     decoded_byte = byte_value[0];
86   } else {
87     decoded_byte = byte_value;
88   }
89
90   if(this.buffer.length > this.position) {
91     this.buffer[this.position++] = decoded_byte;
92   } else {
93     if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) {
94       // Create additional overflow buffer
95       var buffer = new Buffer(Binary.BUFFER_SIZE + this.buffer.length);
96       // Combine the two buffers together
97       this.buffer.copy(buffer, 0, 0, this.buffer.length);
98       this.buffer = buffer;
99       this.buffer[this.position++] = decoded_byte;
100     } else {
101       var buffer = null;
102       // Create a new buffer (typed or normal array)
103       if(Object.prototype.toString.call(this.buffer) == '[object Uint8Array]') {
104         buffer = new Uint8Array(new ArrayBuffer(Binary.BUFFER_SIZE + this.buffer.length));
105       } else {
106         buffer = new Array(Binary.BUFFER_SIZE + this.buffer.length);
107       }
108
109       // We need to copy all the content to the new array
110       for(var i = 0; i < this.buffer.length; i++) {
111         buffer[i] = this.buffer[i];
112       }
113
114       // Reassign the buffer
115       this.buffer = buffer;
116       // Write the byte
117       this.buffer[this.position++] = decoded_byte;
118     }
119   }
120 };
121
122 /**
123  * Writes a buffer or string to the binary.
124  *
125  * @method
126  * @param {(Buffer|string)} string a string or buffer to be written to the Binary BSON object.
127  * @param {number} offset specify the binary of where to write the content.
128  * @return {null}
129  */
130 Binary.prototype.write = function write(string, offset) {
131   offset = typeof offset == 'number' ? offset : this.position;
132
133   // If the buffer is to small let's extend the buffer
134   if(this.buffer.length < offset + string.length) {
135     var buffer = null;
136     // If we are in node.js
137     if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) {
138       buffer = new Buffer(this.buffer.length + string.length);
139       this.buffer.copy(buffer, 0, 0, this.buffer.length);
140     } else if(Object.prototype.toString.call(this.buffer) == '[object Uint8Array]') {
141       // Create a new buffer
142       buffer = new Uint8Array(new ArrayBuffer(this.buffer.length + string.length))
143       // Copy the content
144       for(var i = 0; i < this.position; i++) {
145         buffer[i] = this.buffer[i];
146       }
147     }
148
149     // Assign the new buffer
150     this.buffer = buffer;
151   }
152
153   if(typeof Buffer != 'undefined' && Buffer.isBuffer(string) && Buffer.isBuffer(this.buffer)) {
154     string.copy(this.buffer, offset, 0, string.length);
155     this.position = (offset + string.length) > this.position ? (offset + string.length) : this.position;
156     // offset = string.length
157   } else if(typeof Buffer != 'undefined' && typeof string == 'string' && Buffer.isBuffer(this.buffer)) {
158     this.buffer.write(string, offset, 'binary');
159     this.position = (offset + string.length) > this.position ? (offset + string.length) : this.position;
160     // offset = string.length;
161   } else if(Object.prototype.toString.call(string) == '[object Uint8Array]'
162     || Object.prototype.toString.call(string) == '[object Array]' && typeof string != 'string') {
163     for(var i = 0; i < string.length; i++) {
164       this.buffer[offset++] = string[i];
165     }
166
167     this.position = offset > this.position ? offset : this.position;
168   } else if(typeof string == 'string') {
169     for(var i = 0; i < string.length; i++) {
170       this.buffer[offset++] = string.charCodeAt(i);
171     }
172
173     this.position = offset > this.position ? offset : this.position;
174   }
175 };
176
177 /**
178  * Reads **length** bytes starting at **position**.
179  *
180  * @method
181  * @param {number} position read from the given position in the Binary.
182  * @param {number} length the number of bytes to read.
183  * @return {Buffer}
184  */
185 Binary.prototype.read = function read(position, length) {
186   length = length && length > 0
187     ? length
188     : this.position;
189
190   // Let's return the data based on the type we have
191   if(this.buffer['slice']) {
192     return this.buffer.slice(position, position + length);
193   } else {
194     // Create a buffer to keep the result
195     var buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(length)) : new Array(length);
196     for(var i = 0; i < length; i++) {
197       buffer[i] = this.buffer[position++];
198     }
199   }
200   // Return the buffer
201   return buffer;
202 };
203
204 /**
205  * Returns the value of this binary as a string.
206  *
207  * @method
208  * @return {string}
209  */
210 Binary.prototype.value = function value(asRaw) {
211   asRaw = asRaw == null ? false : asRaw;
212
213   // Optimize to serialize for the situation where the data == size of buffer
214   if(asRaw && typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer) && this.buffer.length == this.position)
215     return this.buffer;
216
217   // If it's a node.js buffer object
218   if(typeof Buffer != 'undefined' && Buffer.isBuffer(this.buffer)) {
219     return asRaw ? this.buffer.slice(0, this.position) : this.buffer.toString('binary', 0, this.position);
220   } else {
221     if(asRaw) {
222       // we support the slice command use it
223       if(this.buffer['slice'] != null) {
224         return this.buffer.slice(0, this.position);
225       } else {
226         // Create a new buffer to copy content to
227         var newBuffer = Object.prototype.toString.call(this.buffer) == '[object Uint8Array]' ? new Uint8Array(new ArrayBuffer(this.position)) : new Array(this.position);
228         // Copy content
229         for(var i = 0; i < this.position; i++) {
230           newBuffer[i] = this.buffer[i];
231         }
232         // Return the buffer
233         return newBuffer;
234       }
235     } else {
236       return convertArraytoUtf8BinaryString(this.buffer, 0, this.position);
237     }
238   }
239 };
240
241 /**
242  * Length.
243  *
244  * @method
245  * @return {number} the length of the binary.
246  */
247 Binary.prototype.length = function length() {
248   return this.position;
249 };
250
251 /**
252  * @ignore
253  */
254 Binary.prototype.toJSON = function() {
255   return this.buffer != null ? this.buffer.toString('base64') : '';
256 }
257
258 /**
259  * @ignore
260  */
261 Binary.prototype.toString = function(format) {
262   return this.buffer != null ? this.buffer.slice(0, this.position).toString(format) : '';
263 }
264
265 /**
266  * Binary default subtype
267  * @ignore
268  */
269 var BSON_BINARY_SUBTYPE_DEFAULT = 0;
270
271 /**
272  * @ignore
273  */
274 var writeStringToArray = function(data) {
275   // Create a buffer
276   var buffer = typeof Uint8Array != 'undefined' ? new Uint8Array(new ArrayBuffer(data.length)) : new Array(data.length);
277   // Write the content to the buffer
278   for(var i = 0; i < data.length; i++) {
279     buffer[i] = data.charCodeAt(i);
280   }
281   // Write the string to the buffer
282   return buffer;
283 }
284
285 /**
286  * Convert Array ot Uint8Array to Binary String
287  *
288  * @ignore
289  */
290 var convertArraytoUtf8BinaryString = function(byteArray, startIndex, endIndex) {
291   var result = "";
292   for(var i = startIndex; i < endIndex; i++) {
293    result = result + String.fromCharCode(byteArray[i]);
294   }
295   return result;
296 };
297
298 Binary.BUFFER_SIZE = 256;
299
300 /**
301  * Default BSON type
302  *
303  * @classconstant SUBTYPE_DEFAULT
304  **/
305 Binary.SUBTYPE_DEFAULT = 0;
306 /**
307  * Function BSON type
308  *
309  * @classconstant SUBTYPE_DEFAULT
310  **/
311 Binary.SUBTYPE_FUNCTION = 1;
312 /**
313  * Byte Array BSON type
314  *
315  * @classconstant SUBTYPE_DEFAULT
316  **/
317 Binary.SUBTYPE_BYTE_ARRAY = 2;
318 /**
319  * OLD UUID BSON type
320  *
321  * @classconstant SUBTYPE_DEFAULT
322  **/
323 Binary.SUBTYPE_UUID_OLD = 3;
324 /**
325  * UUID BSON type
326  *
327  * @classconstant SUBTYPE_DEFAULT
328  **/
329 Binary.SUBTYPE_UUID = 4;
330 /**
331  * MD5 BSON type
332  *
333  * @classconstant SUBTYPE_DEFAULT
334  **/
335 Binary.SUBTYPE_MD5 = 5;
336 /**
337  * User BSON type
338  *
339  * @classconstant SUBTYPE_DEFAULT
340  **/
341 Binary.SUBTYPE_USER_DEFINED = 128;
342
343 /**
344  * Expose.
345  */
346 module.exports = Binary;
347 module.exports.Binary = Binary;