├── index.d.ts ├── indexv2.d.ts ├── .codecov.yml ├── .gitignore ├── byte_size.js ├── LICENSE.md ├── package.json ├── README.md ├── index.js ├── indexv2.js └── test └── test.js /index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Calculates the approximate number of bytes that the provided object holds. 3 | * @param object 4 | */ 5 | export default function sizeof(object: T): number; -------------------------------------------------------------------------------- /indexv2.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'object-sizeof' { 2 | /** 3 | * Calculates the approximate number of bytes that the provided object holds. 4 | * @param object 5 | */ 6 | export default function sizeof(object: T): number; 7 | } 8 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: # This can be anything, but it needs to exist as the name 5 | # basic settings 6 | target: auto 7 | threshold: 5% 8 | base: auto 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | .idea 17 | .vscode 18 | 19 | .nyc_output 20 | coverage.lcov -------------------------------------------------------------------------------- /byte_size.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Byte sizes are taken from ECMAScript Language Specification 3 | * http://www.ecma-international.org/ecma-262/5.1/ 4 | * http://bclary.com/2004/11/07/#a-4.3.16 5 | */ 6 | 7 | module.exports = { 8 | STRING: 2, 9 | BOOLEAN: 4, 10 | BYTES: 4, 11 | NUMBER: 8, 12 | Int8Array: 1, 13 | Uint8Array: 1, 14 | Uint8ClampedArray: 1, 15 | Int16Array: 2, 16 | Uint16Array: 2, 17 | Int32Array: 4, 18 | Uint32Array: 4, 19 | Float32Array: 4, 20 | Float64Array: 8 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2014, Andrei Karpushonak aka @miktam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object-sizeof", 3 | "version": "2.6.5", 4 | "description": "Sizeof of a JavaScript object in Bytes", 5 | "main": "indexv2.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/standard; ./node_modules/.bin/mocha", 8 | "lint": "./node_modules/.bin/standard", 9 | "coverage": "./node_modules/.bin/nyc npm test; ./node_modules/.bin/nyc report --reporter=text-lcov > coverage.lcov && ./node_modules/.bin/codecov --token=708ee89f-4e9c-402d-8380-1257b979596c" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/miktam/sizeof.git" 14 | }, 15 | "keywords": [ 16 | "sizeof", 17 | "size", 18 | "object", 19 | "bytes" 20 | ], 21 | "author": "Andrei Karpushonak", 22 | "license": "MIT", 23 | "readmeFilename": "README.md", 24 | "dependencies": { 25 | "buffer": "^6.0.3" 26 | }, 27 | "devDependencies": { 28 | "codecov": "^3.8.3", 29 | "mocha": "^10.6.0", 30 | "nyc": "^17.0.0", 31 | "should": "^13.2.3", 32 | "standard": "^17.1.0" 33 | }, 34 | "types": "indexv2.d.ts", 35 | "standard": { 36 | "env": [ 37 | "mocha" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## object-sizeof 2 | 3 | [![Build](https://img.shields.io/npm/v/object-sizeof)](https://img.shields.io/npm/v/object-sizeof) ![GitHub contributors](https://img.shields.io/github/contributors/miktam/sizeof) [![NPM](https://img.shields.io/npm/dy/object-sizeof)](https://img.shields.io/npm/dy/object-sizeof) [![codecov](https://codecov.io/gh/miktam/sizeof/branch/master/graph/badge.svg?token=qPHxmWpC1K)](https://codecov.io/gh/miktam/sizeof) 4 | 5 | ### Get the size of a JavaScript object in Bytes 6 | 7 | Node.js version uses the Buffer.from(objectToString) method to convert the object's string representation to a buffer, and then it uses the byteLength property to obtain the buffer size in bytes. 8 | 9 | The module uses a combination of recursion and a stack to iterate through all of its properties, adding up the number of bytes for each data type it encounters. 10 | 11 | Please note that this function will only work in some cases, especially when dealing with complex data structures or when the object contains functions. 12 | 13 | ### Supported Standard built-in and complex types 14 | 15 | - Map 16 | - Set 17 | - BigInt 18 | - Function 19 | - Typed Arrays (Int8Array, Uint32Array, Float64Array, etc) 20 | 21 | ### Error handling 22 | 23 | Errors indicated by returned -1 in following cases: 24 | 25 | - JSON serialization error, e.g. circular references. 26 | - Unrecognizable TypedArray object. 27 | 28 | It prevents potential exceptions or infinite loops, improving reliability. 29 | 30 | ### Coding standards 31 | 32 | The project follows [JavaScript Standard Style](https://standardjs.com/) as a JavaScript style guide. 33 | Code coverage reports are done using Codecov.io. 34 | 35 | Code is written with the assumptions that any code added, which is not tested properly, is already or will be buggy. 36 | Hence test coverage, with the BDD style unit tests, stating the intent, and expected behaviour, is a must. 37 | 38 | ### Get size of a JavaScript object in Bytes - version 1.x 39 | 40 | JavaScript does not provide sizeof (like in C), and programmer does not need to care about memory allocation/deallocation. 41 | 42 | However, according to [ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/5.1/), each String value is represented by 16-bit unsigned integer, Number uses the double-precision 64-bit format IEEE 754 values including the special "Not-a-Number" (NaN) values, positive infinity, and negative infinity. 43 | 44 | Having this knowledge, the module calculates how much memory object will allocate. 45 | 46 | ### Installation 47 | 48 | `npm install object-sizeof` 49 | 50 | ### Examples 51 | 52 | ```javascript 53 | // import sizeof from 'object-sizeof' 54 | const sizeof = require('object-sizeof') 55 | const sizeObj = sizeof({ abc: 'def' }) 56 | console.log(`Size of the object: ${sizeObj} bytes`) 57 | const sizeInt = sizeof(12345) 58 | console.log(`Size of the int: ${sizeInt} bytes`) 59 | ``` 60 | 61 | ### Licence 62 | 63 | The MIT License (MIT) 64 | 65 | Copyright (c) 2015, Andrei Karpushonak aka @miktam 66 | 67 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmiktam%2Fsizeof.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmiktam%2Fsizeof?ref=badge_shield) 68 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Andrei Karpushonak 2 | 3 | 'use strict' 4 | 5 | const ECMA_SIZES = require('./byte_size') 6 | const Buffer = require('buffer/').Buffer 7 | 8 | const isNodePlatform = 9 | typeof process === 'object' && typeof require === 'function' 10 | 11 | function allProperties (obj) { 12 | const stringProperties = [] 13 | for (const prop in obj) { 14 | stringProperties.push(prop) 15 | } 16 | if (Object.getOwnPropertySymbols) { 17 | const symbolProperties = Object.getOwnPropertySymbols(obj) 18 | Array.prototype.push.apply(stringProperties, symbolProperties) 19 | } 20 | return stringProperties 21 | } 22 | 23 | function sizeOfObject (seen, object) { 24 | if (object == null) { 25 | return 0 26 | } 27 | 28 | let bytes = 0 29 | const properties = allProperties(object) 30 | for (let i = 0; i < properties.length; i++) { 31 | const key = properties[i] 32 | // Do not recalculate circular references 33 | if (typeof object[key] === 'object' && object[key] !== null) { 34 | if (seen.has(object[key])) { 35 | continue 36 | } 37 | seen.add(object[key]) 38 | } 39 | 40 | bytes += getCalculator(seen)(key) 41 | try { 42 | bytes += getCalculator(seen)(object[key]) 43 | } catch (ex) { 44 | if (ex instanceof RangeError) { 45 | // circular reference detected, final result might be incorrect 46 | // let's be nice and not throw an exception 47 | bytes = 0 48 | } 49 | } 50 | } 51 | 52 | return bytes 53 | } 54 | 55 | function getCalculator (seen) { 56 | return function calculator (object) { 57 | if (Buffer.isBuffer(object)) { 58 | return object.length 59 | } 60 | 61 | const objectType = typeof object 62 | switch (objectType) { 63 | case 'string': 64 | // https://stackoverflow.com/questions/68789144/how-much-memory-do-v8-take-to-store-a-string/68791382#68791382 65 | return isNodePlatform 66 | ? 12 + 4 * Math.ceil(object.length / 4) 67 | : object.length * ECMA_SIZES.STRING 68 | case 'boolean': 69 | return ECMA_SIZES.BOOLEAN 70 | case 'number': 71 | return ECMA_SIZES.NUMBER 72 | case 'symbol': { 73 | const isGlobalSymbol = Symbol.keyFor && Symbol.keyFor(object) 74 | return isGlobalSymbol 75 | ? Symbol.keyFor(object).length * ECMA_SIZES.STRING 76 | : (object.toString().length - 8) * ECMA_SIZES.STRING 77 | } 78 | case 'object': 79 | if (Array.isArray(object)) { 80 | return object.map(getCalculator(seen)).reduce(function (acc, curr) { 81 | return acc + curr 82 | }, 0) 83 | } else { 84 | return sizeOfObject(seen, object) 85 | } 86 | default: 87 | return 0 88 | } 89 | } 90 | } 91 | 92 | /** 93 | * Main module's entry point 94 | * Calculates Bytes for the provided parameter 95 | * @param object - handles object/string/boolean/buffer 96 | * @returns {*} 97 | */ 98 | function sizeof (object) { 99 | return getCalculator(new WeakSet())(object) 100 | } 101 | 102 | module.exports = sizeof 103 | -------------------------------------------------------------------------------- /indexv2.js: -------------------------------------------------------------------------------- 1 | // Copyright 2023 ChatGPT May 24 Version 2 | /* eslint-disable new-cap */ // to fix new Buffer.from 3 | 'use strict' 4 | const ECMA_SIZES = require('./byte_size') 5 | const Buffer = 6 | typeof window !== 'undefined' ? require('buffer/').Buffer : global.Buffer 7 | 8 | /** 9 | * Precisely calculate size of string in node 10 | * Based on https://stackoverflow.com/questions/68789144/how-much-memory-do-v8-take-to-store-a-string/68791382#68791382 11 | * @param {} str 12 | */ 13 | function preciseStringSizeNode (str) { 14 | return 12 + 4 * Math.ceil(str.length / 4) 15 | } 16 | 17 | /** 18 | * In the browser environment, window and document are defined as global objects 19 | * @returns true if its a Node.js env, false if it is a browser 20 | */ 21 | function isNodeEnvironment () { 22 | if (typeof window !== 'undefined' && typeof document !== 'undefined') { 23 | return false 24 | } 25 | return true 26 | } 27 | 28 | function getSizeOfTypedArray (typedArray) { 29 | if (typedArray.BYTES_PER_ELEMENT) { 30 | return typedArray.length * typedArray.BYTES_PER_ELEMENT 31 | } 32 | return -1 // error indication 33 | } 34 | 35 | /** 36 | * Size in bytes for complex objects 37 | * @param {*} obj 38 | * @returns size in bytes, or -1 if JSON.stringify threw an exception 39 | */ 40 | function objectSizeComplex (obj) { 41 | let totalSize = 0 42 | const errorIndication = -1 43 | 44 | try { 45 | // convert Map and Set to an object representation 46 | let convertedObj = obj 47 | if (obj instanceof Map) { 48 | convertedObj = Object.fromEntries(obj) 49 | } else if (obj instanceof Set) { 50 | convertedObj = Array.from(obj) 51 | } 52 | 53 | // handle typed arrays 54 | if (ArrayBuffer.isView(obj)) { 55 | return getSizeOfTypedArray(obj) 56 | } 57 | 58 | const serializedObj = JSON.stringify(convertedObj, (key, value) => { 59 | if (typeof value === 'bigint') { 60 | return value.toString() 61 | } else if (typeof value === 'function') { 62 | return value.toString() 63 | } else if (typeof value === 'undefined') { 64 | return 'undefined' 65 | } else if (typeof value === 'symbol') { 66 | return value.toString() 67 | } else if (value instanceof RegExp) { 68 | return value.toString() 69 | } else { 70 | return value 71 | } 72 | }) 73 | 74 | totalSize = Buffer.byteLength(serializedObj, 'utf8') 75 | } catch (ex) { 76 | // do not log anyting to console.error 77 | return new Error(errorIndication) 78 | } 79 | 80 | return totalSize 81 | } 82 | 83 | /** 84 | * Size in bytes for primitive types 85 | * @param {*} obj 86 | * @returns size in bytes 87 | */ 88 | function objectSizeSimple (obj) { 89 | const objectList = [] 90 | const stack = [obj] 91 | let bytes = 0 92 | 93 | while (stack.length) { 94 | const value = stack.pop() 95 | 96 | if (typeof value === 'boolean') { 97 | bytes += ECMA_SIZES.BYTES 98 | } else if (typeof value === 'string') { 99 | if (isNodeEnvironment()) { 100 | bytes += preciseStringSizeNode(value) 101 | } else { 102 | bytes += value.length * ECMA_SIZES.STRING 103 | } 104 | } else if (typeof value === 'number') { 105 | bytes += ECMA_SIZES.NUMBER 106 | } else if (typeof value === 'symbol') { 107 | const isGlobalSymbol = Symbol.keyFor && Symbol.keyFor(obj) 108 | if (isGlobalSymbol) { 109 | bytes += Symbol.keyFor(obj).length * ECMA_SIZES.STRING 110 | } else { 111 | bytes += (obj.toString().length - 8) * ECMA_SIZES.STRING 112 | } 113 | } else if (typeof value === 'bigint') { 114 | bytes += Buffer.from(value.toString()).byteLength 115 | } else if (typeof value === 'function') { 116 | bytes += value.toString().length 117 | } else if (typeof value === 'object' && objectList.indexOf(value) === -1) { 118 | objectList.push(value) 119 | 120 | for (const i in value) { 121 | stack.push(value[i]) 122 | } 123 | } 124 | } 125 | return bytes 126 | } 127 | 128 | module.exports = function (obj) { 129 | let totalSize = 0 130 | 131 | if (obj !== null && typeof obj === 'object') { 132 | totalSize = objectSizeComplex(obj) 133 | } else { 134 | totalSize = objectSizeSimple(obj) 135 | } 136 | 137 | return totalSize 138 | } 139 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* global describe, it */ 4 | 5 | const should = require('should') 6 | const sizeof = require('../indexv2.js') 7 | 8 | describe('sizeof node.js tests', () => { 9 | it('should handle null in object keys', () => { 10 | const badData = { 1: { depot_id: null, hierarchy_node_id: null } } 11 | sizeof(badData).should.be.instanceOf(Number) 12 | }) 13 | 14 | it('should handle null in object keys', () => { 15 | const badData = { 1: { depot_id: null, hierarchy_node_id: null } } 16 | sizeof(badData).should.be.instanceOf(Number) 17 | }) 18 | 19 | it('null is 0', () => { 20 | sizeof(null).should.be.equal(0) 21 | }) 22 | 23 | it('number size shall be 8', () => { 24 | sizeof(5).should.be.equal(8) 25 | }) 26 | 27 | it('undefined is 0', () => { 28 | sizeof().should.be.equal(0) 29 | }) 30 | 31 | it('of 3 chars string is 16 in node.js', () => { 32 | sizeof('abc').should.be.equal(16) 33 | }) 34 | 35 | it('sizeof of empty string', () => { 36 | sizeof('').should.be.equal(12) 37 | }) 38 | 39 | it('boolean size shall be 4', () => { 40 | sizeof(true).should.be.equal(4) 41 | }) 42 | 43 | it('report an error for circular dependency objects', () => { 44 | const firstLevel = { a: 1 } 45 | const secondLevel = { b: 2, c: firstLevel } 46 | firstLevel.second = secondLevel 47 | should.exist(sizeof(firstLevel)) 48 | }) 49 | 50 | it('handle hasOwnProperty key', () => { 51 | sizeof({ hasOwnProperty: undefined }).should.be.instanceOf(Number) 52 | sizeof({ hasOwnProperty: 'Hello World' }).should.be.instanceOf(Number) 53 | sizeof({ hasOwnProperty: 1234 }).should.be.instanceOf(Number) 54 | }) 55 | 56 | it('supports symbol', () => { 57 | const descriptor = 'abcd' 58 | sizeof(Symbol(descriptor)).should.equal(2 * descriptor.length) 59 | }) 60 | 61 | it('supports global symbols', () => { 62 | const globalSymbol = Symbol.for('a') 63 | const obj = { [globalSymbol]: 'b' } 64 | sizeof(obj).should.equal(2) 65 | }) 66 | 67 | it('array support for strings - longer array should have sizeof above the shorter one', () => { 68 | sizeof(['a', 'b', 'c', 'd']).should.be.above(sizeof(['a', 'b'])) 69 | }) 70 | 71 | it('array support for numbers - longer array should have sizeof above the shorter one', () => { 72 | sizeof([1, 2, 3]).should.be.above(sizeof([1, 2])) 73 | }) 74 | 75 | it('array support for NaN - longer array should have sizeof above the shorter one', () => { 76 | sizeof([NaN, NaN]).should.be.above(sizeof([NaN])) 77 | }) 78 | 79 | it('map support', () => { 80 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map 81 | const mapSmaller = new Map() 82 | mapSmaller.set('a', 1) 83 | const mapBigger = new Map() 84 | mapBigger.set('a', 1) 85 | mapBigger.set('b', 2) 86 | sizeof(mapBigger).should.be.above(sizeof(mapSmaller)) 87 | }) 88 | 89 | it('set support', () => { 90 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set 91 | const smallerSet = new Set() 92 | smallerSet.add(1) // Set(1) { 1 } 93 | 94 | const biggerSet = new Set() 95 | biggerSet.add(1) // Set(1) { 1 } 96 | biggerSet.add('some text') // Set(3) { 1, 5, 'some text' } 97 | sizeof(biggerSet).should.be.above(sizeof(smallerSet)) 98 | }) 99 | 100 | it('typed array support', () => { 101 | const arrayInt8Array = new Int8Array([1, 2, 3, 4, 5]) 102 | sizeof(arrayInt8Array).should.equal(5) 103 | 104 | const arrayUint8Array = new Uint8Array([1, 2, 3, 4, 5]) 105 | sizeof(arrayUint8Array).should.equal(5) 106 | 107 | const arrayUint16Array = new Uint16Array([1, 2, 3, 4, 5]) 108 | sizeof(arrayUint16Array).should.equal(10) 109 | 110 | const arrayInt16Array = new Int16Array([1, 2, 3, 4, 5]) 111 | sizeof(arrayInt16Array).should.equal(10) 112 | 113 | const arrayUint32Array = new Uint32Array([1, 2, 3, 4, 5]) 114 | sizeof(arrayUint32Array).should.equal(20) 115 | 116 | const arrayInt32Array = new Int32Array([1, 2, 3, 4, 5]) 117 | sizeof(arrayInt32Array).should.equal(20) 118 | 119 | const arrayFloat32Array = new Float32Array([1, 2, 3, 4, 5]) 120 | sizeof(arrayFloat32Array).should.equal(20) 121 | 122 | const arrayFloat64 = new Float64Array([1, 2, 3, 4, 5]) 123 | sizeof(arrayFloat64).should.equal(40) 124 | }) 125 | 126 | it('BigInt support', () => { 127 | sizeof(BigInt(21474836480)).should.equal(11) 128 | }) 129 | 130 | it('BigInt support in objects', () => { 131 | const nestedBigInt = { 132 | num: BigInt(123123123123123123n) 133 | } 134 | sizeof(nestedBigInt).should.equal(28) 135 | }) 136 | 137 | it('function support in objects', () => { 138 | const nestedFunction = { 139 | func: x => { 140 | return x + x 141 | } 142 | } 143 | sizeof(nestedFunction).should.equal(48) 144 | }) 145 | 146 | it('nested support in objects', () => { 147 | const nestedUndefined = { 148 | undef: undefined 149 | } 150 | sizeof(nestedUndefined).should.equal(21) 151 | }) 152 | 153 | it('should handle nested symbols', () => { 154 | sizeof({ symbol: Symbol('test') }).should.equal(25) 155 | }) 156 | 157 | it('should handle nested regex', () => { 158 | sizeof({ regex: /test/g }).should.equal(19) 159 | }) 160 | 161 | it('nested objects', () => { 162 | const obj = { a: 1, b: 2, c: 3 } 163 | sizeof(obj).should.be.equal(19) 164 | const nested = { d: obj } 165 | sizeof(nested).should.be.equal(25) 166 | }) 167 | 168 | it('Function support', () => { 169 | const func = (one, two) => { 170 | return one + two 171 | } 172 | sizeof(func).should.equal(44) 173 | }) 174 | 175 | it('should calculate size for global symbols', () => { 176 | sizeof(Symbol.for('testKey')).should.equal(14) 177 | }) 178 | }) 179 | 180 | describe('sizeof browser tests', () => { 181 | beforeEach(function () { 182 | global.window = {} 183 | global.document = {} 184 | }) 185 | 186 | it('each caracter is two bytes in a browser environent', () => { 187 | sizeof('abc').should.be.equal(6) 188 | }) 189 | 190 | afterEach(function () { 191 | delete global.window 192 | delete global.document 193 | }) 194 | }) 195 | --------------------------------------------------------------------------------