binary.js 10 KB

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