UNPKG

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