|
|
import { Binary } from '../binary'; |
|
|
import type { Document } from '../bson'; |
|
|
import { BSONVersionError } from '../error'; |
|
|
import * as constants from '../constants'; |
|
|
import { ByteUtils } from '../utils/byte_utils'; |
|
|
import { isAnyArrayBuffer, isDate, isRegExp } from './utils'; |
|
|
|
|
|
export function internalCalculateObjectSize( |
|
|
object: Document, |
|
|
serializeFunctions?: boolean, |
|
|
ignoreUndefined?: boolean |
|
|
): number { |
|
|
let totalLength = 4 + 1; |
|
|
|
|
|
if (Array.isArray(object)) { |
|
|
for (let i = 0; i < object.length; i++) { |
|
|
totalLength += calculateElement( |
|
|
i.toString(), |
|
|
object[i], |
|
|
serializeFunctions, |
|
|
true, |
|
|
ignoreUndefined |
|
|
); |
|
|
} |
|
|
} else { |
|
|
|
|
|
|
|
|
if (typeof object?.toBSON === 'function') { |
|
|
object = object.toBSON(); |
|
|
} |
|
|
|
|
|
|
|
|
for (const key of Object.keys(object)) { |
|
|
totalLength += calculateElement(key, object[key], serializeFunctions, false, ignoreUndefined); |
|
|
} |
|
|
} |
|
|
|
|
|
return totalLength; |
|
|
} |
|
|
|
|
|
|
|
|
function calculateElement( |
|
|
name: string, |
|
|
|
|
|
value: any, |
|
|
serializeFunctions = false, |
|
|
isArray = false, |
|
|
ignoreUndefined = false |
|
|
) { |
|
|
|
|
|
if (typeof value?.toBSON === 'function') { |
|
|
value = value.toBSON(); |
|
|
} |
|
|
|
|
|
switch (typeof value) { |
|
|
case 'string': |
|
|
return 1 + ByteUtils.utf8ByteLength(name) + 1 + 4 + ByteUtils.utf8ByteLength(value) + 1; |
|
|
case 'number': |
|
|
if ( |
|
|
Math.floor(value) === value && |
|
|
value >= constants.JS_INT_MIN && |
|
|
value <= constants.JS_INT_MAX |
|
|
) { |
|
|
if (value >= constants.BSON_INT32_MIN && value <= constants.BSON_INT32_MAX) { |
|
|
|
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (4 + 1); |
|
|
} else { |
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1); |
|
|
} |
|
|
} else { |
|
|
|
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1); |
|
|
} |
|
|
case 'undefined': |
|
|
if (isArray || !ignoreUndefined) |
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1; |
|
|
return 0; |
|
|
case 'boolean': |
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1); |
|
|
case 'object': |
|
|
if ( |
|
|
value != null && |
|
|
typeof value._bsontype === 'string' && |
|
|
value[Symbol.for('@@mdb.bson.version')] !== constants.BSON_MAJOR_VERSION |
|
|
) { |
|
|
throw new BSONVersionError(); |
|
|
} else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') { |
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1; |
|
|
} else if (value._bsontype === 'ObjectId') { |
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1); |
|
|
} else if (value instanceof Date || isDate(value)) { |
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1); |
|
|
} else if ( |
|
|
ArrayBuffer.isView(value) || |
|
|
value instanceof ArrayBuffer || |
|
|
isAnyArrayBuffer(value) |
|
|
) { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength |
|
|
); |
|
|
} else if ( |
|
|
value._bsontype === 'Long' || |
|
|
value._bsontype === 'Double' || |
|
|
value._bsontype === 'Timestamp' |
|
|
) { |
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1); |
|
|
} else if (value._bsontype === 'Decimal128') { |
|
|
return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1); |
|
|
} else if (value._bsontype === 'Code') { |
|
|
|
|
|
if (value.scope != null && Object.keys(value.scope).length > 0) { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
1 + |
|
|
4 + |
|
|
4 + |
|
|
ByteUtils.utf8ByteLength(value.code.toString()) + |
|
|
1 + |
|
|
internalCalculateObjectSize(value.scope, serializeFunctions, ignoreUndefined) |
|
|
); |
|
|
} else { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
1 + |
|
|
4 + |
|
|
ByteUtils.utf8ByteLength(value.code.toString()) + |
|
|
1 |
|
|
); |
|
|
} |
|
|
} else if (value._bsontype === 'Binary') { |
|
|
const binary: Binary = value; |
|
|
|
|
|
if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
(binary.position + 1 + 4 + 1 + 4) |
|
|
); |
|
|
} else { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1) |
|
|
); |
|
|
} |
|
|
} else if (value._bsontype === 'Symbol') { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
ByteUtils.utf8ByteLength(value.value) + |
|
|
4 + |
|
|
1 + |
|
|
1 |
|
|
); |
|
|
} else if (value._bsontype === 'DBRef') { |
|
|
|
|
|
const ordered_values = Object.assign( |
|
|
{ |
|
|
$ref: value.collection, |
|
|
$id: value.oid |
|
|
}, |
|
|
value.fields |
|
|
); |
|
|
|
|
|
|
|
|
if (value.db != null) { |
|
|
ordered_values['$db'] = value.db; |
|
|
} |
|
|
|
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
1 + |
|
|
internalCalculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined) |
|
|
); |
|
|
} else if (value instanceof RegExp || isRegExp(value)) { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
1 + |
|
|
ByteUtils.utf8ByteLength(value.source) + |
|
|
1 + |
|
|
(value.global ? 1 : 0) + |
|
|
(value.ignoreCase ? 1 : 0) + |
|
|
(value.multiline ? 1 : 0) + |
|
|
1 |
|
|
); |
|
|
} else if (value._bsontype === 'BSONRegExp') { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
1 + |
|
|
ByteUtils.utf8ByteLength(value.pattern) + |
|
|
1 + |
|
|
ByteUtils.utf8ByteLength(value.options) + |
|
|
1 |
|
|
); |
|
|
} else { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
internalCalculateObjectSize(value, serializeFunctions, ignoreUndefined) + |
|
|
1 |
|
|
); |
|
|
} |
|
|
case 'function': |
|
|
if (serializeFunctions) { |
|
|
return ( |
|
|
(name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + |
|
|
1 + |
|
|
4 + |
|
|
ByteUtils.utf8ByteLength(value.toString()) + |
|
|
1 |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|