Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 - uses precise calculation for node.js, and separate implementation for the browser. #70

Merged
merged 8 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ npm-debug.log
node_modules
.idea
.vscode

.nyc_output
coverage.lcov
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_js:
- v12
- v13
- v14
- v16
94 changes: 53 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
## object-sizeof

[![Build Status](https://travis-ci.org/miktam/sizeof.svg?branch=master)](https://travis-ci.org/miktam/sizeof) [![Dependency Status](https://david-dm.org/miktam/sizeof.svg)](https://david-dm.org/miktam/sizeof)
[![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)
[![Build Status](https://travis-ci.org/miktam/sizeof.svg?branch=master)](https://travis-ci.org/miktam/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)

### Get size of a JavaScript object in Bytes - version 2.x

New version uses the Buffer.from(objectToString) method to convert the string representation of the object to a buffer and then it uses the byteLength property to obtain the size of the buffer in bytes.
Note that this method only work in Node.js environment.

For everything else, the calculation takes an object as an argument and 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.

[![NPM](https://nodei.co/npm/object-sizeof.png?downloads=true&downloadRank=true)](https://nodei.co/npm/object-sizeof/)
Please note that this function will not work on all cases, specially when dealing with complex data structures or when the object contains functions.

### Get size of a JavaScript object in Bytes
### Coding standards

JavaScript does not provide sizeof (like in C), and programmer does not need to care about memory allocation/deallocation.
Project uses [JavaScript Standard Style](https://standardjs.com/).
Code coverage reports done using Codecov.io.

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.
### Get size of a JavaScript object in Bytes - version 1.x

Having this knowledge, the module calculates how much memory object will allocate.
JavaScript does not provide sizeof (like in C), and programmer does not need to care about memory allocation/deallocation.

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.

Having this knowledge, the module calculates how much memory object will allocate.

### Limitations
Please note, that V8 which compiles the JavaScript into native machine code, is not taken into account, as the compiled code is additionally heavily optimized.

Please note, that V8 which compiles the JavaScript into native machine code, is not taken into account, as the compiled code is additionally heavily optimized.

### Installation

Expand All @@ -25,45 +37,45 @@ Please note, that V8 which compiles the JavaScript into native machine code, is
#### ES5

```javascript
var sizeof = require('object-sizeof')

// 2B per character, 6 chars total => 12B
console.log(sizeof({abc: 'def'}))

// 8B for Number => 8B
console.log(sizeof(12345))

var param = {
'a': 1,
'b': 2,
'c': {
'd': 4
}
var sizeof = require('object-sizeof')

// 2B per character, 6 chars total => 12B
console.log(sizeof({ abc: 'def' }))

// 8B for Number => 8B
console.log(sizeof(12345))

var param = {
a: 1,
b: 2,
c: {
d: 4
}
// 4 one two-bytes char strings and 3 eighth-bytes numbers => 32B
console.log(sizeof(param))
}
// 4 one two-bytes char strings and 3 eighth-bytes numbers => 32B
console.log(sizeof(param))
```

#### ES6

```javascript
import sizeof from 'object-sizeof'

// 2B per character, 6 chars total => 12B
console.log(sizeof({abc: 'def'}))

// 8B for Number => 8B
console.log(sizeof(12345))

const param = {
'a': 1,
'b': 2,
'c': {
'd': 4
}
import sizeof from 'object-sizeof'

// 2B per character, 6 chars total => 12B
console.log(sizeof({ abc: 'def' }))

// 8B for Number => 8B
console.log(sizeof(12345))

const param = {
a: 1,
b: 2,
c: {
d: 4
}
// 4 one two-bytes char strings and 3 eighth-bytes numbers => 32B
console.log(sizeof(param))
}
// 4 one two-bytes char strings and 3 eighth-bytes numbers => 32B
console.log(sizeof(param))
```

### Licence
Expand All @@ -72,4 +84,4 @@ The MIT License (MIT)

Copyright (c) 2015, Andrei Karpushonak aka @miktam

[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmiktam%2Fsizeof.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmiktam%2Fsizeof?ref=badge_large)
[![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)
1 change: 1 addition & 0 deletions byte_size.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
module.exports = {
STRING: 2,
BOOLEAN: 4,
BYTES: 4,
NUMBER: 8
}
41 changes: 25 additions & 16 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

'use strict'

var ECMA_SIZES = require('./byte_size')
var Buffer = require('buffer/').Buffer
const ECMA_SIZES = require('./byte_size')
const Buffer = require('buffer/').Buffer

function allProperties(obj) {
const isNodePlatform =
typeof process === 'object' && typeof require === 'function'

function allProperties (obj) {
const stringProperties = []
for (var prop in obj) {
stringProperties.push(prop)
for (const prop in obj) {
stringProperties.push(prop)
}
if (Object.getOwnPropertySymbols) {
var symbolProperties = Object.getOwnPropertySymbols(obj)
Array.prototype.push.apply(stringProperties, symbolProperties)
const symbolProperties = Object.getOwnPropertySymbols(obj)
Array.prototype.push.apply(stringProperties, symbolProperties)
}
return stringProperties
}
Expand All @@ -22,10 +25,10 @@ function sizeOfObject (seen, object) {
return 0
}

var bytes = 0
var properties = allProperties(object)
for (var i = 0; i < properties.length; i++) {
var key = properties[i]
let bytes = 0
const properties = allProperties(object)
for (let i = 0; i < properties.length; i++) {
const key = properties[i]
// Do not recalculate circular references
if (typeof object[key] === 'object' && object[key] !== null) {
if (seen.has(object[key])) {
Expand All @@ -50,22 +53,28 @@ function sizeOfObject (seen, object) {
}

function getCalculator (seen) {
return function calculator(object) {
return function calculator (object) {
if (Buffer.isBuffer(object)) {
return object.length
}

var objectType = typeof (object)
const objectType = typeof object
switch (objectType) {
case 'string':
return object.length * ECMA_SIZES.STRING
// https://stackoverflow.com/questions/68789144/how-much-memory-do-v8-take-to-store-a-string/68791382#68791382
return isNodePlatform
? 12 + 4 * Math.ceil(object.length / 4)
: object.length * ECMA_SIZES.STRING
case 'boolean':
return ECMA_SIZES.BOOLEAN
case 'number':
return ECMA_SIZES.NUMBER
case 'symbol':
case 'symbol': {
const isGlobalSymbol = Symbol.keyFor && Symbol.keyFor(object)
return isGlobalSymbol ? Symbol.keyFor(object).length * ECMA_SIZES.STRING : (object.toString().length - 8) * ECMA_SIZES.STRING
return isGlobalSymbol
? Symbol.keyFor(object).length * ECMA_SIZES.STRING
: (object.toString().length - 8) * ECMA_SIZES.STRING
}
case 'object':
if (Array.isArray(object)) {
return object.map(getCalculator(seen)).reduce(function (acc, curr) {
Expand Down
5 changes: 5 additions & 0 deletions indexv2.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Calculates the approximate number of bytes that the provided object holds.
* @param object
*/
export default function sizeof<T>(object: T): number;
87 changes: 87 additions & 0 deletions indexv2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2023 ChatGPT Jan 9 Version
/* eslint-disable new-cap */ // to fix new Buffer.from
'use strict'
const ECMA_SIZES = require('./byte_size')

/**
* Size in bytes in a Node.js environment
* @param {*} obj
* @returns size in bytes, or -1 if JSON.stringify threw an exception
*/
function objectSizeNode (obj) {
let totalSize = 0
const errorIndication = -1
try {
const objectToString = JSON.stringify(obj)
const buffer = new Buffer.from(objectToString)
totalSize = buffer.byteLength
} catch (ex) {
console.error('Error detected, return ' + errorIndication, ex)
return errorIndication
}
return totalSize
}

/**
* Size in bytes in a browser environment
* @param {*} obj
* @returns size in bytes
*/
function objectSizeBrowser (obj) {
const objectList = []
const stack = [obj]
let bytes = 0

while (stack.length) {
const value = stack.pop()

if (typeof value === 'boolean') {
bytes += ECMA_SIZES.BYTES
} else if (typeof value === 'string') {
bytes += value.length * ECMA_SIZES.STRING
} else if (typeof value === 'number') {
bytes += ECMA_SIZES.NUMBER
} else if (typeof value === 'symbol') {
const isGlobalSymbol = Symbol.keyFor && Symbol.keyFor(obj)
if (isGlobalSymbol) {
bytes += Symbol.keyFor(obj).length * ECMA_SIZES.STRING
} else {
bytes += (obj.toString().length - 8) * ECMA_SIZES.STRING
}
} else if (typeof value === 'object' && objectList.indexOf(value) === -1) {
objectList.push(value)

for (const i in value) {
stack.push(value[i])
}
}
}
return bytes
}

/**
* Are we running in a Node.js environment
* @returns boolean
*/
function isNodeEnvironment () {
if (
typeof process !== 'undefined' &&
process.versions &&
process.versions.node
) {
return true
}
return false
}

module.exports = function (obj) {
let totalSize = 0

if (obj !== null && typeof obj === 'object' && isNodeEnvironment()) {
totalSize = objectSizeNode(obj)
} else {
totalSize = objectSizeBrowser(obj)
}

return totalSize
}
Loading