buffer.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*!
  2. * Module dependencies.
  3. */
  4. 'use strict';
  5. const Binary = require('../driver').get().Binary;
  6. const utils = require('../utils');
  7. /**
  8. * Mongoose Buffer constructor.
  9. *
  10. * Values always have to be passed to the constructor to initialize.
  11. *
  12. * @param {Buffer} value
  13. * @param {String} encode
  14. * @param {Number} offset
  15. * @api private
  16. * @inherits Buffer
  17. * @see https://bit.ly/f6CnZU
  18. */
  19. function MongooseBuffer(value, encode, offset) {
  20. let val = value;
  21. if (value == null) {
  22. val = 0;
  23. }
  24. let encoding;
  25. let path;
  26. let doc;
  27. if (Array.isArray(encode)) {
  28. // internal casting
  29. path = encode[0];
  30. doc = encode[1];
  31. } else {
  32. encoding = encode;
  33. }
  34. let buf;
  35. if (typeof val === 'number' || val instanceof Number) {
  36. buf = Buffer.alloc(val);
  37. } else { // string, array or object { type: 'Buffer', data: [...] }
  38. buf = Buffer.from(val, encoding, offset);
  39. }
  40. utils.decorate(buf, MongooseBuffer.mixin);
  41. buf.isMongooseBuffer = true;
  42. // make sure these internal props don't show up in Object.keys()
  43. buf[MongooseBuffer.pathSymbol] = path;
  44. buf[parentSymbol] = doc;
  45. buf._subtype = 0;
  46. return buf;
  47. }
  48. const pathSymbol = Symbol.for('mongoose#Buffer#_path');
  49. const parentSymbol = Symbol.for('mongoose#Buffer#_parent');
  50. MongooseBuffer.pathSymbol = pathSymbol;
  51. /*!
  52. * Inherit from Buffer.
  53. */
  54. MongooseBuffer.mixin = {
  55. /**
  56. * Default subtype for the Binary representing this Buffer
  57. *
  58. * @api private
  59. * @property _subtype
  60. * @receiver MongooseBuffer
  61. */
  62. _subtype: undefined,
  63. /**
  64. * Marks this buffer as modified.
  65. *
  66. * @api private
  67. * @method _markModified
  68. * @receiver MongooseBuffer
  69. */
  70. _markModified: function() {
  71. const parent = this[parentSymbol];
  72. if (parent) {
  73. parent.markModified(this[MongooseBuffer.pathSymbol]);
  74. }
  75. return this;
  76. },
  77. /**
  78. * Writes the buffer.
  79. *
  80. * @api public
  81. * @method write
  82. * @receiver MongooseBuffer
  83. */
  84. write: function() {
  85. const written = Buffer.prototype.write.apply(this, arguments);
  86. if (written > 0) {
  87. this._markModified();
  88. }
  89. return written;
  90. },
  91. /**
  92. * Copies the buffer.
  93. *
  94. * #### Note:
  95. *
  96. * `Buffer#copy` does not mark `target` as modified so you must copy from a `MongooseBuffer` for it to work as expected. This is a work around since `copy` modifies the target, not this.
  97. *
  98. * @return {Number} The number of bytes copied.
  99. * @param {Buffer} target
  100. * @method copy
  101. * @receiver MongooseBuffer
  102. */
  103. copy: function(target) {
  104. const ret = Buffer.prototype.copy.apply(this, arguments);
  105. if (target && target.isMongooseBuffer) {
  106. target._markModified();
  107. }
  108. return ret;
  109. }
  110. };
  111. /*!
  112. * Compile other Buffer methods marking this buffer as modified.
  113. */
  114. (
  115. // node < 0.5
  116. ('writeUInt8 writeUInt16 writeUInt32 writeInt8 writeInt16 writeInt32 ' +
  117. 'writeFloat writeDouble fill ' +
  118. 'utf8Write binaryWrite asciiWrite set ' +
  119. // node >= 0.5
  120. 'writeUInt16LE writeUInt16BE writeUInt32LE writeUInt32BE ' +
  121. 'writeInt16LE writeInt16BE writeInt32LE writeInt32BE ' + 'writeFloatLE writeFloatBE writeDoubleLE writeDoubleBE')
  122. ).split(' ').forEach(function(method) {
  123. if (!Buffer.prototype[method]) {
  124. return;
  125. }
  126. MongooseBuffer.mixin[method] = function() {
  127. const ret = Buffer.prototype[method].apply(this, arguments);
  128. this._markModified();
  129. return ret;
  130. };
  131. });
  132. /**
  133. * Converts this buffer to its Binary type representation.
  134. *
  135. * ####SubTypes:
  136. *
  137. * const bson = require('bson')
  138. * bson.BSON_BINARY_SUBTYPE_DEFAULT
  139. * bson.BSON_BINARY_SUBTYPE_FUNCTION
  140. * bson.BSON_BINARY_SUBTYPE_BYTE_ARRAY
  141. * bson.BSON_BINARY_SUBTYPE_UUID
  142. * bson.BSON_BINARY_SUBTYPE_MD5
  143. * bson.BSON_BINARY_SUBTYPE_USER_DEFINED
  144. *
  145. * doc.buffer.toObject(bson.BSON_BINARY_SUBTYPE_USER_DEFINED);
  146. *
  147. * @see https://bsonspec.org/#/specification
  148. * @param {Hex} [subtype]
  149. * @return {Binary}
  150. * @api public
  151. * @method toObject
  152. * @receiver MongooseBuffer
  153. */
  154. MongooseBuffer.mixin.toObject = function(options) {
  155. const subtype = typeof options === 'number'
  156. ? options
  157. : (this._subtype || 0);
  158. return new Binary(Buffer.from(this), subtype);
  159. };
  160. MongooseBuffer.mixin.$toObject = MongooseBuffer.mixin.toObject;
  161. /**
  162. * Converts this buffer for storage in MongoDB, including subtype
  163. *
  164. * @return {Binary}
  165. * @api public
  166. * @method toBSON
  167. * @receiver MongooseBuffer
  168. */
  169. MongooseBuffer.mixin.toBSON = function() {
  170. return new Binary(this, this._subtype || 0);
  171. };
  172. /**
  173. * Determines if this buffer is equals to `other` buffer
  174. *
  175. * @param {Buffer} other
  176. * @return {Boolean}
  177. * @method equals
  178. * @receiver MongooseBuffer
  179. */
  180. MongooseBuffer.mixin.equals = function(other) {
  181. if (!Buffer.isBuffer(other)) {
  182. return false;
  183. }
  184. if (this.length !== other.length) {
  185. return false;
  186. }
  187. for (let i = 0; i < this.length; ++i) {
  188. if (this[i] !== other[i]) {
  189. return false;
  190. }
  191. }
  192. return true;
  193. };
  194. /**
  195. * Sets the subtype option and marks the buffer modified.
  196. *
  197. * ####SubTypes:
  198. *
  199. * const bson = require('bson')
  200. * bson.BSON_BINARY_SUBTYPE_DEFAULT
  201. * bson.BSON_BINARY_SUBTYPE_FUNCTION
  202. * bson.BSON_BINARY_SUBTYPE_BYTE_ARRAY
  203. * bson.BSON_BINARY_SUBTYPE_UUID
  204. * bson.BSON_BINARY_SUBTYPE_MD5
  205. * bson.BSON_BINARY_SUBTYPE_USER_DEFINED
  206. *
  207. * doc.buffer.subtype(bson.BSON_BINARY_SUBTYPE_UUID);
  208. *
  209. * @see https://bsonspec.org/#/specification
  210. * @param {Hex} subtype
  211. * @api public
  212. * @method subtype
  213. * @receiver MongooseBuffer
  214. */
  215. MongooseBuffer.mixin.subtype = function(subtype) {
  216. if (typeof subtype !== 'number') {
  217. throw new TypeError('Invalid subtype. Expected a number');
  218. }
  219. if (this._subtype !== subtype) {
  220. this._markModified();
  221. }
  222. this._subtype = subtype;
  223. };
  224. /*!
  225. * Module exports.
  226. */
  227. MongooseBuffer.Binary = Binary;
  228. module.exports = MongooseBuffer;