calculate_size.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. 'use strict';
  2. const Buffer = require('buffer').Buffer;
  3. const Long = require('../long');
  4. const Double = require('../double');
  5. const Timestamp = require('../timestamp');
  6. const ObjectId = require('../objectid');
  7. const BSONSymbol = require('../symbol');
  8. const BSONRegExp = require('../regexp');
  9. const Code = require('../code');
  10. const Decimal128 = require('../decimal128');
  11. const MinKey = require('../min_key');
  12. const MaxKey = require('../max_key');
  13. const DBRef = require('../db_ref');
  14. const Binary = require('../binary');
  15. const normalizedFunctionString = require('./utils').normalizedFunctionString;
  16. const constants = require('../constants');
  17. // To ensure that 0.4 of node works correctly
  18. function isDate(d) {
  19. return typeof d === 'object' && Object.prototype.toString.call(d) === '[object Date]';
  20. }
  21. function calculateObjectSize(object, serializeFunctions, ignoreUndefined) {
  22. let totalLength = 4 + 1;
  23. if (Array.isArray(object)) {
  24. for (let i = 0; i < object.length; i++) {
  25. totalLength += calculateElement(
  26. i.toString(),
  27. object[i],
  28. serializeFunctions,
  29. true,
  30. ignoreUndefined
  31. );
  32. }
  33. } else {
  34. // If we have toBSON defined, override the current object
  35. if (object.toBSON) {
  36. object = object.toBSON();
  37. }
  38. // Calculate size
  39. for (let key in object) {
  40. totalLength += calculateElement(key, object[key], serializeFunctions, false, ignoreUndefined);
  41. }
  42. }
  43. return totalLength;
  44. }
  45. /**
  46. * @ignore
  47. * @api private
  48. */
  49. function calculateElement(name, value, serializeFunctions, isArray, ignoreUndefined) {
  50. // If we have toBSON defined, override the current object
  51. if (value && value.toBSON) {
  52. value = value.toBSON();
  53. }
  54. switch (typeof value) {
  55. case 'string':
  56. return 1 + Buffer.byteLength(name, 'utf8') + 1 + 4 + Buffer.byteLength(value, 'utf8') + 1;
  57. case 'number':
  58. if (
  59. Math.floor(value) === value &&
  60. value >= constants.JS_INT_MIN &&
  61. value <= constants.JS_INT_MAX
  62. ) {
  63. if (value >= constants.BSON_INT32_MIN && value <= constants.BSON_INT32_MAX) {
  64. // 32 bit
  65. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (4 + 1);
  66. } else {
  67. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (8 + 1);
  68. }
  69. } else {
  70. // 64 bit
  71. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (8 + 1);
  72. }
  73. case 'undefined':
  74. if (isArray || !ignoreUndefined)
  75. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + 1;
  76. return 0;
  77. case 'boolean':
  78. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (1 + 1);
  79. case 'object':
  80. if (
  81. value == null ||
  82. value instanceof MinKey ||
  83. value instanceof MaxKey ||
  84. value['_bsontype'] === 'MinKey' ||
  85. value['_bsontype'] === 'MaxKey'
  86. ) {
  87. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + 1;
  88. } else if (value instanceof ObjectId || value['_bsontype'] === 'ObjectId') {
  89. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (12 + 1);
  90. } else if (value instanceof Date || isDate(value)) {
  91. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (8 + 1);
  92. } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
  93. return (
  94. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (1 + 4 + 1) + value.length
  95. );
  96. } else if (
  97. value instanceof Long ||
  98. value instanceof Double ||
  99. value instanceof Timestamp ||
  100. value['_bsontype'] === 'Long' ||
  101. value['_bsontype'] === 'Double' ||
  102. value['_bsontype'] === 'Timestamp'
  103. ) {
  104. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (8 + 1);
  105. } else if (value instanceof Decimal128 || value['_bsontype'] === 'Decimal128') {
  106. return (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (16 + 1);
  107. } else if (value instanceof Code || value['_bsontype'] === 'Code') {
  108. // Calculate size depending on the availability of a scope
  109. if (value.scope != null && Object.keys(value.scope).length > 0) {
  110. return (
  111. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  112. 1 +
  113. 4 +
  114. 4 +
  115. Buffer.byteLength(value.code.toString(), 'utf8') +
  116. 1 +
  117. calculateObjectSize(value.scope, serializeFunctions, ignoreUndefined)
  118. );
  119. } else {
  120. return (
  121. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  122. 1 +
  123. 4 +
  124. Buffer.byteLength(value.code.toString(), 'utf8') +
  125. 1
  126. );
  127. }
  128. } else if (value instanceof Binary || value['_bsontype'] === 'Binary') {
  129. // Check what kind of subtype we have
  130. if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
  131. return (
  132. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  133. (value.position + 1 + 4 + 1 + 4)
  134. );
  135. } else {
  136. return (
  137. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) + (value.position + 1 + 4 + 1)
  138. );
  139. }
  140. } else if (value instanceof BSONSymbol || value['_bsontype'] === 'Symbol') {
  141. return (
  142. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  143. Buffer.byteLength(value.value, 'utf8') +
  144. 4 +
  145. 1 +
  146. 1
  147. );
  148. } else if (value instanceof DBRef || value['_bsontype'] === 'DBRef') {
  149. // Set up correct object for serialization
  150. const ordered_values = Object.assign(
  151. {
  152. $ref: value.collection,
  153. $id: value.oid
  154. },
  155. value.fields
  156. );
  157. // Add db reference if it exists
  158. if (value.db != null) {
  159. ordered_values['$db'] = value.db;
  160. }
  161. return (
  162. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  163. 1 +
  164. calculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined)
  165. );
  166. } else if (
  167. value instanceof RegExp ||
  168. Object.prototype.toString.call(value) === '[object RegExp]'
  169. ) {
  170. return (
  171. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  172. 1 +
  173. Buffer.byteLength(value.source, 'utf8') +
  174. 1 +
  175. (value.global ? 1 : 0) +
  176. (value.ignoreCase ? 1 : 0) +
  177. (value.multiline ? 1 : 0) +
  178. 1
  179. );
  180. } else if (value instanceof BSONRegExp || value['_bsontype'] === 'BSONRegExp') {
  181. return (
  182. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  183. 1 +
  184. Buffer.byteLength(value.pattern, 'utf8') +
  185. 1 +
  186. Buffer.byteLength(value.options, 'utf8') +
  187. 1
  188. );
  189. } else {
  190. return (
  191. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  192. calculateObjectSize(value, serializeFunctions, ignoreUndefined) +
  193. 1
  194. );
  195. }
  196. case 'function':
  197. // WTF for 0.4.X where typeof /someregexp/ === 'function'
  198. if (
  199. value instanceof RegExp ||
  200. Object.prototype.toString.call(value) === '[object RegExp]' ||
  201. String.call(value) === '[object RegExp]'
  202. ) {
  203. return (
  204. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  205. 1 +
  206. Buffer.byteLength(value.source, 'utf8') +
  207. 1 +
  208. (value.global ? 1 : 0) +
  209. (value.ignoreCase ? 1 : 0) +
  210. (value.multiline ? 1 : 0) +
  211. 1
  212. );
  213. } else {
  214. if (serializeFunctions && value.scope != null && Object.keys(value.scope).length > 0) {
  215. return (
  216. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  217. 1 +
  218. 4 +
  219. 4 +
  220. Buffer.byteLength(normalizedFunctionString(value), 'utf8') +
  221. 1 +
  222. calculateObjectSize(value.scope, serializeFunctions, ignoreUndefined)
  223. );
  224. } else if (serializeFunctions) {
  225. return (
  226. (name != null ? Buffer.byteLength(name, 'utf8') + 1 : 0) +
  227. 1 +
  228. 4 +
  229. Buffer.byteLength(normalizedFunctionString(value), 'utf8') +
  230. 1
  231. );
  232. }
  233. }
  234. }
  235. return 0;
  236. }
  237. module.exports = calculateObjectSize;