bson.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import { Binary, UUID } from './binary';
  2. import { Code } from './code';
  3. import { DBRef } from './db_ref';
  4. import { Decimal128 } from './decimal128';
  5. import { Double } from './double';
  6. import { Int32 } from './int_32';
  7. import { Long } from './long';
  8. import { MaxKey } from './max_key';
  9. import { MinKey } from './min_key';
  10. import { ObjectId } from './objectid';
  11. import { internalCalculateObjectSize } from './parser/calculate_size';
  12. // Parts of the parser
  13. import { internalDeserialize, type DeserializeOptions } from './parser/deserializer';
  14. import { serializeInto, type SerializeOptions } from './parser/serializer';
  15. import { BSONRegExp } from './regexp';
  16. import { BSONSymbol } from './symbol';
  17. import { Timestamp } from './timestamp';
  18. import { ByteUtils } from './utils/byte_utils';
  19. export type { UUIDExtended, BinaryExtended, BinaryExtendedLegacy, BinarySequence } from './binary';
  20. export type { CodeExtended } from './code';
  21. export type { DBRefLike } from './db_ref';
  22. export type { Decimal128Extended } from './decimal128';
  23. export type { DoubleExtended } from './double';
  24. export type { EJSONOptions } from './extended_json';
  25. export type { Int32Extended } from './int_32';
  26. export type { LongExtended } from './long';
  27. export type { MaxKeyExtended } from './max_key';
  28. export type { MinKeyExtended } from './min_key';
  29. export type { ObjectIdExtended, ObjectIdLike } from './objectid';
  30. export type { BSONRegExpExtended, BSONRegExpExtendedLegacy } from './regexp';
  31. export type { BSONSymbolExtended } from './symbol';
  32. export type { LongWithoutOverrides, TimestampExtended, TimestampOverrides } from './timestamp';
  33. export type { LongWithoutOverridesClass } from './timestamp';
  34. export type { SerializeOptions, DeserializeOptions };
  35. export {
  36. Code,
  37. BSONSymbol,
  38. DBRef,
  39. Binary,
  40. ObjectId,
  41. UUID,
  42. Long,
  43. Timestamp,
  44. Double,
  45. Int32,
  46. MinKey,
  47. MaxKey,
  48. BSONRegExp,
  49. Decimal128
  50. };
  51. export { BSONValue } from './bson_value';
  52. export { BSONError, BSONVersionError, BSONRuntimeError } from './error';
  53. export { BSONType } from './constants';
  54. export { EJSON } from './extended_json';
  55. /** @public */
  56. export interface Document {
  57. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  58. [key: string]: any;
  59. }
  60. /** @internal */
  61. // Default Max Size
  62. const MAXSIZE = 1024 * 1024 * 17;
  63. // Current Internal Temporary Serialization Buffer
  64. let buffer = ByteUtils.allocate(MAXSIZE);
  65. /**
  66. * Sets the size of the internal serialization buffer.
  67. *
  68. * @param size - The desired size for the internal serialization buffer in bytes
  69. * @public
  70. */
  71. export function setInternalBufferSize(size: number): void {
  72. // Resize the internal serialization buffer if needed
  73. if (buffer.length < size) {
  74. buffer = ByteUtils.allocate(size);
  75. }
  76. }
  77. /**
  78. * Serialize a Javascript object.
  79. *
  80. * @param object - the Javascript object to serialize.
  81. * @returns Buffer object containing the serialized object.
  82. * @public
  83. */
  84. export function serialize(object: Document, options: SerializeOptions = {}): Uint8Array {
  85. // Unpack the options
  86. const checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
  87. const serializeFunctions =
  88. typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  89. const ignoreUndefined =
  90. typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
  91. const minInternalBufferSize =
  92. typeof options.minInternalBufferSize === 'number' ? options.minInternalBufferSize : MAXSIZE;
  93. // Resize the internal serialization buffer if needed
  94. if (buffer.length < minInternalBufferSize) {
  95. buffer = ByteUtils.allocate(minInternalBufferSize);
  96. }
  97. // Attempt to serialize
  98. const serializationIndex = serializeInto(
  99. buffer,
  100. object,
  101. checkKeys,
  102. 0,
  103. 0,
  104. serializeFunctions,
  105. ignoreUndefined,
  106. null
  107. );
  108. // Create the final buffer
  109. const finishedBuffer = ByteUtils.allocate(serializationIndex);
  110. // Copy into the finished buffer
  111. finishedBuffer.set(buffer.subarray(0, serializationIndex), 0);
  112. // Return the buffer
  113. return finishedBuffer;
  114. }
  115. /**
  116. * Serialize a Javascript object using a predefined Buffer and index into the buffer,
  117. * useful when pre-allocating the space for serialization.
  118. *
  119. * @param object - the Javascript object to serialize.
  120. * @param finalBuffer - the Buffer you pre-allocated to store the serialized BSON object.
  121. * @returns the index pointing to the last written byte in the buffer.
  122. * @public
  123. */
  124. export function serializeWithBufferAndIndex(
  125. object: Document,
  126. finalBuffer: Uint8Array,
  127. options: SerializeOptions = {}
  128. ): number {
  129. // Unpack the options
  130. const checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
  131. const serializeFunctions =
  132. typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  133. const ignoreUndefined =
  134. typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
  135. const startIndex = typeof options.index === 'number' ? options.index : 0;
  136. // Attempt to serialize
  137. const serializationIndex = serializeInto(
  138. buffer,
  139. object,
  140. checkKeys,
  141. 0,
  142. 0,
  143. serializeFunctions,
  144. ignoreUndefined,
  145. null
  146. );
  147. finalBuffer.set(buffer.subarray(0, serializationIndex), startIndex);
  148. // Return the index
  149. return startIndex + serializationIndex - 1;
  150. }
  151. /**
  152. * Deserialize data as BSON.
  153. *
  154. * @param buffer - the buffer containing the serialized set of BSON documents.
  155. * @returns returns the deserialized Javascript Object.
  156. * @public
  157. */
  158. export function deserialize(buffer: Uint8Array, options: DeserializeOptions = {}): Document {
  159. return internalDeserialize(ByteUtils.toLocalBufferType(buffer), options);
  160. }
  161. /** @public */
  162. export type CalculateObjectSizeOptions = Pick<
  163. SerializeOptions,
  164. 'serializeFunctions' | 'ignoreUndefined'
  165. >;
  166. /**
  167. * Calculate the bson size for a passed in Javascript object.
  168. *
  169. * @param object - the Javascript object to calculate the BSON byte size for
  170. * @returns size of BSON object in bytes
  171. * @public
  172. */
  173. export function calculateObjectSize(
  174. object: Document,
  175. options: CalculateObjectSizeOptions = {}
  176. ): number {
  177. options = options || {};
  178. const serializeFunctions =
  179. typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
  180. const ignoreUndefined =
  181. typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
  182. return internalCalculateObjectSize(object, serializeFunctions, ignoreUndefined);
  183. }
  184. /**
  185. * Deserialize stream data as BSON documents.
  186. *
  187. * @param data - the buffer containing the serialized set of BSON documents.
  188. * @param startIndex - the start index in the data Buffer where the deserialization is to start.
  189. * @param numberOfDocuments - number of documents to deserialize.
  190. * @param documents - an array where to store the deserialized documents.
  191. * @param docStartIndex - the index in the documents array from where to start inserting documents.
  192. * @param options - additional options used for the deserialization.
  193. * @returns next index in the buffer after deserialization **x** numbers of documents.
  194. * @public
  195. */
  196. export function deserializeStream(
  197. data: Uint8Array | ArrayBuffer,
  198. startIndex: number,
  199. numberOfDocuments: number,
  200. documents: Document[],
  201. docStartIndex: number,
  202. options: DeserializeOptions
  203. ): number {
  204. const internalOptions = Object.assign(
  205. { allowObjectSmallerThanBufferSize: true, index: 0 },
  206. options
  207. );
  208. const bufferData = ByteUtils.toLocalBufferType(data);
  209. let index = startIndex;
  210. // Loop over all documents
  211. for (let i = 0; i < numberOfDocuments; i++) {
  212. // Find size of the document
  213. const size =
  214. bufferData[index] |
  215. (bufferData[index + 1] << 8) |
  216. (bufferData[index + 2] << 16) |
  217. (bufferData[index + 3] << 24);
  218. // Update options with index
  219. internalOptions.index = index;
  220. // Parse the document at this point
  221. documents[docStartIndex + i] = internalDeserialize(bufferData, internalOptions);
  222. // Adjust index by the document size
  223. index = index + size;
  224. }
  225. // Return object containing end index of parsing and list of documents
  226. return index;
  227. }