json-pack-gzip
Advanced tools
Comparing version
93
index.js
@@ -5,48 +5,73 @@ const { gunzip, gzip } = require('./lib/gzip'); | ||
} = require('./lib/hpack'); | ||
const { calculateSize } = require('./utils/calculateSize'); | ||
const { debugWrapper } = require('./utils/debugWrapper'); | ||
/** | ||
* Calculate the byte length of the input data. | ||
* It supports data types: String, Buffer, Array, and Object. | ||
* For non-buffer objects, the function first stringifies them before calculating the size. | ||
* Compresses a JSON object using the Gzip compression algorithm. This is accomplished by first | ||
* stringifying the JSON object, and then compressing it into a Buffer object. If the debug | ||
* parameter is set to true, the function measures and logs the performance and the size | ||
* of the compressed data. | ||
* | ||
* @param {String|Buffer|Object|Array} data - The data to be size-calculated. | ||
* @returns {Number} The byte length of the input data. | ||
* @function compress | ||
* @param {Object} jsonObject - The JSON object that needs to be compressed. | ||
* @param {boolean} [debug=false] - Optional parameter that defaults to false. If set to true, | ||
* it will measure the performance and size of the compressed data and log these details. | ||
* @returns {Buffer} The compressed data as a Buffer object. If an error occurs during | ||
* compression, an Error is thrown. | ||
* @throws {Error} If an error occurs during the compression process. | ||
*/ | ||
function calculateSize(data) { | ||
if (typeof data === 'string' || data instanceof String || data instanceof Buffer) { | ||
return Buffer.byteLength(data); | ||
} | ||
function compress(jsonObject, debug = false) { | ||
const [startDebug, endDebug, errorDebug] = debugWrapper('compress'); | ||
try { | ||
if (debug) { | ||
startDebug(); | ||
} | ||
if (typeof data === 'object') { | ||
return Buffer.byteLength(JSON.stringify(data)); | ||
const hPacked = stringify(jsonObject); | ||
const bufferOriginal = Buffer.from(hPacked, 'utf-8'); | ||
const gzipped = gzip(bufferOriginal); | ||
if (debug) { | ||
endDebug(jsonObject, gzipped); | ||
} | ||
return gzipped; | ||
} catch (error) { | ||
return errorDebug(error); | ||
} | ||
return 0; | ||
} | ||
/** | ||
* Compresses a JSON object using Gzip compression algorithm after first packing it into | ||
* a homogeneous array and stringifying it. | ||
* | ||
* @function compress | ||
* @param {Object[]} jsonObject - The JSON object to be compressed. | ||
* @returns {Buffer} - The compressed data as a Buffer object. | ||
*/ | ||
function compress(jsonObject) { | ||
const hPacked = stringify(jsonObject); | ||
const bufferOriginal = Buffer.from(hPacked, 'utf-8'); | ||
const gzipped = gzip(bufferOriginal); | ||
return gzipped; | ||
} | ||
/** | ||
* Decompresses a Buffer object to a JSON object using Gzip decompression algorithm, | ||
* after first parsing it from a homogeneous array into a list of objects. | ||
* Decompresses a Buffer object into a JSON object using the Gzip decompression algorithm. | ||
* This is accomplished by first decompressing the Buffer object and then parsing it into | ||
* a JSON object. If the debug parameter is set to true, the function measures and logs the | ||
* performance and the size of the decompressed data. | ||
* | ||
* @function decompress | ||
* @param {Buffer} jsonBuffer - The Buffer object to be decompressed. | ||
* @returns {Object[]} - The decompressed and unpacked JSON object as a list of objects. | ||
* @param {Buffer} jsonBuffer - The Buffer object that needs to be decompressed. | ||
* @param {boolean} [debug=false] - Optional parameter that defaults to false. If set to true, | ||
* it will measure the performance and size of the decompressed data and log these details. | ||
* @returns {Object} The decompressed and parsed JSON object. If an error occurs during | ||
* decompression, the original Buffer object is returned. | ||
* @throws {Error} If the decompression process fails, an error is logged, but not thrown. | ||
*/ | ||
function decompress(jsonBuffer) { | ||
const bufferDecompressed = gunzip(jsonBuffer); | ||
const jsonUnpacked = parse(bufferDecompressed); | ||
return jsonUnpacked; | ||
function decompress(jsonBuffer, debug = false) { | ||
const [startDebug, endDebug, errorDebug] = debugWrapper('decompress'); | ||
try { | ||
if (debug) { | ||
startDebug(); | ||
} | ||
const bufferDecompressed = gunzip(jsonBuffer); | ||
const jsonUnpacked = parse(bufferDecompressed); | ||
if (debug) { | ||
endDebug(jsonUnpacked, jsonBuffer); | ||
} | ||
return jsonUnpacked; | ||
} catch (error) { | ||
return errorDebug(error); | ||
} | ||
} | ||
@@ -53,0 +78,0 @@ |
const { gzipSync, gunzipSync } = require('node:zlib'); | ||
const { debugWrapper } = require('../utils/debugWrapper'); | ||
@@ -7,7 +8,21 @@ /** | ||
* @param {string} string - The JSON object to be compressed. | ||
* @param {boolean} [debug=false] - Optional parameter that defaults to false. If set to true, | ||
* it will measure the performance and size of the compressed data and log these details. | ||
* @returns {Buffer} - The compressed data as a Buffer object. | ||
*/ | ||
function gzip(string) { | ||
const bufferOriginal = Buffer.from(string, 'utf-8'); | ||
return gzipSync(bufferOriginal); | ||
function gzip(string, debug = false) { | ||
const [startDebug, endDebug, errorDebug] = debugWrapper('gzip'); | ||
try { | ||
if (debug) { | ||
startDebug(); | ||
} | ||
const bufferOriginal = Buffer.from(string, 'utf-8'); | ||
const gzipped = gzipSync(bufferOriginal); | ||
if (debug) { | ||
endDebug(string, gzipped); | ||
} | ||
return gzipped; | ||
} catch (error) { | ||
return errorDebug(error); | ||
} | ||
} | ||
@@ -19,7 +34,21 @@ | ||
* @param {Buffer} buffer - The Buffer object to be decompressed. | ||
* @param {boolean} [debug=false] - Optional parameter that defaults to false. If set to true, | ||
* it will measure the performance and size of the compressed data and log these details. | ||
* @returns {string} - The decompressed JSON object as a string. | ||
*/ | ||
function gunzip(buffer) { | ||
const bufferDecompressed = gunzipSync(buffer); | ||
return bufferDecompressed.toString('utf-8'); | ||
function gunzip(buffer, debug = false) { | ||
const [startDebug, endDebug, errorDebug] = debugWrapper('gunzip'); | ||
try { | ||
if (debug) { | ||
startDebug(); | ||
} | ||
const bufferDecompressed = gunzipSync(buffer); | ||
const gunzipped = bufferDecompressed.toString('utf-8'); | ||
if (debug) { | ||
endDebug(gunzipped, buffer); | ||
} | ||
return gunzipped; | ||
} catch (error) { | ||
return errorDebug(error); | ||
} | ||
} | ||
@@ -26,0 +55,0 @@ |
181
lib/hpack.js
@@ -1,47 +0,11 @@ | ||
/** | ||
* JSONH - JSON Homogeneous Collections Handler | ||
* | ||
* Transforms [{a:"A"},{a:"B"}] into [1,"a","A","B"] and vice versa. | ||
* | ||
* @module jsonh | ||
* @version 1.0.0 | ||
* @author Andrea Giammarchi, @WebReflection | ||
* @license MIT | ||
*/ | ||
const { debugWrapper } = require('../utils/debugWrapper'); | ||
const { | ||
keys: ObjectKeys = Object.keys, | ||
isArray = Array.isArray, | ||
stringify: jsonStringify = JSON.stringify, | ||
parse: jsonParse = JSON.parse, | ||
} = { | ||
keys: Object.keys || ((o) => { | ||
const keys = []; | ||
Object.keys(o).forEach((k) => { | ||
if (Object.prototype.hasOwnProperty.call(o, k)) { | ||
keys.push(k); | ||
} | ||
}); | ||
return keys; | ||
}), | ||
isArray: Array.isArray || ((toString) => { | ||
const arrayToString = toString.call([]); | ||
return (o) => toString.call(o) === arrayToString; | ||
})({}.toString), | ||
stringify: JSON.stringify, | ||
parse: JSON.parse, | ||
}; | ||
const ObjectKeys = Object.keys; | ||
const { isArray } = Array; | ||
const jsonStringify = JSON.stringify; | ||
const jsonParse = JSON.parse; | ||
const arr = []; | ||
const { concat } = arr; | ||
const map = arr.map || ((callback, context) => { | ||
const self = this; | ||
let i = self.length; | ||
const result = Array(i); | ||
while (i > 0) { | ||
i -= 1; | ||
result[i] = callback.call(context, self[i], i, self); | ||
} | ||
return result; | ||
}); | ||
const { map } = arr; | ||
@@ -81,33 +45,104 @@ function iteratingWith(method) { | ||
function hpack(list) { | ||
const { length } = list; | ||
const keys = ObjectKeys(length ? list[0] : {}); | ||
const klength = keys.length; | ||
const result = Array(length * klength); | ||
let j = 0; | ||
for (let i = 0; i < length; i += 1) { | ||
const o = list[i]; | ||
for (let ki = 0; ki < klength; ki += 1) { | ||
result[j] = o[keys[ki]]; | ||
j += 1; | ||
function hpack(obj, debug = false) { | ||
const [startDebug, endDebug, errorDebug] = debugWrapper('hpack'); | ||
try { | ||
if (debug) { | ||
startDebug(); | ||
} | ||
if (isArray(obj)) { | ||
const { length } = obj; | ||
const keys = ObjectKeys(length ? obj[0] : {}); | ||
const klength = keys.length; | ||
const result = Array(length * klength); | ||
let j = 0; | ||
for (let i = 0; i < length; i += 1) { | ||
const o = obj[i]; | ||
for (let ki = 0; ki < klength; ki += 1) { | ||
let value = o[keys[ki]]; | ||
if (typeof value === 'object' && value !== null) { | ||
value = isArray(value) ? value.map((x) => hpack(x)) : hpack(value); | ||
} | ||
result[j] = value; | ||
j += 1; | ||
} | ||
} | ||
const returnValue = concat.call([klength], keys, result); | ||
if (debug) { | ||
endDebug(obj, returnValue); | ||
} | ||
return returnValue; | ||
} if (typeof obj === 'object' && obj !== null) { | ||
const keys = ObjectKeys(obj); | ||
const returnValue = keys.reduce((res, key) => { | ||
res[key] = isArray(obj[key]) ? hpack(obj[key]) : obj[key]; | ||
return res; | ||
}, {}); | ||
if (debug) { | ||
endDebug(obj, returnValue); | ||
} | ||
return returnValue; | ||
} | ||
if (debug) { | ||
endDebug(obj, obj); | ||
} | ||
return obj; | ||
} catch (error) { | ||
return errorDebug(error); | ||
} | ||
return concat.call([klength], keys, result); | ||
} | ||
function hunpack(hlist) { | ||
const { length } = hlist; | ||
const klength = hlist[0]; | ||
const result = Array(((length - klength - 1) / klength) || 0); | ||
let j = 0; | ||
for (let i = 1 + klength; i < length;) { | ||
const o = {}; | ||
for (let ki = 0; ki < klength; ki += 1) { | ||
o[hlist[ki + 1]] = hlist[i]; | ||
i += 1; | ||
function hunpack(packedObj, debug = false) { | ||
const [startDebug, endDebug, errorDebug] = debugWrapper('hunpack'); | ||
try { | ||
if (debug) { | ||
startDebug(); | ||
} | ||
result[j] = o; | ||
j += 1; | ||
if (isArray(packedObj)) { | ||
const { length } = packedObj; | ||
const klength = packedObj[0]; | ||
const result = Array(((length - klength - 1) / klength) || 0); | ||
let j = 0; | ||
for (let i = 1 + klength; i < length;) { | ||
const o = {}; | ||
for (let ki = 0; ki < klength; ki += 1) { | ||
let value = packedObj[i]; | ||
if (isArray(value)) { | ||
value = value.map((item) => (isArray(item) ? hunpack(item) : item)); | ||
} else if (typeof value === 'object' && value !== null) { | ||
value = hunpack(value); | ||
} | ||
o[packedObj[ki + 1]] = value; | ||
i += 1; | ||
} | ||
result[j] = o; | ||
j += 1; | ||
} | ||
if (debug) { | ||
endDebug(result, packedObj); | ||
} | ||
return result; | ||
} if (typeof packedObj === 'object' && packedObj !== null) { | ||
const result = ObjectKeys(packedObj).reduce((res, key) => { | ||
res[key] = isArray(packedObj[key]) ? hunpack(packedObj[key]) : packedObj[key]; | ||
return res; | ||
}, {}); | ||
if (debug) { | ||
endDebug(result, packedObj); | ||
} | ||
return result; | ||
} | ||
if (debug) { | ||
endDebug(packedObj, packedObj); | ||
} | ||
return packedObj; | ||
} catch (error) { | ||
return errorDebug(error); | ||
} | ||
return result; | ||
} | ||
@@ -124,6 +159,8 @@ | ||
* @param {string[]} [schema] - The schema to use for packing. | ||
* @param {boolean} [debug=false] - Optional parameter that defaults to false. If set to true, | ||
* it will measure the performance and size of the compressed data and log these details. | ||
* @returns {Array} - The packed array. | ||
*/ | ||
function pack(list, schema) { | ||
return schema ? packSchema(list, schema) : hpack(list); | ||
function pack(list, schema, debug = false) { | ||
return schema ? packSchema(list, schema) : hpack(list, debug); | ||
} | ||
@@ -137,6 +174,8 @@ | ||
* @param {string[]} [schema] - The schema to use for unpacking. | ||
* @param {boolean} [debug=false] - Optional parameter that defaults to false. If set to true, | ||
* it will measure the performance and size of the compressed data and log these details. | ||
* @returns {Object[]} - The unpacked list of objects. | ||
*/ | ||
function unpack(hlist, schema) { | ||
return schema ? unpackSchema(hlist, schema) : hunpack(hlist); | ||
function unpack(hlist, schema, debug = false) { | ||
return schema ? unpackSchema(hlist, schema) : hunpack(hlist, debug); | ||
} | ||
@@ -143,0 +182,0 @@ |
{ | ||
"name": "json-pack-gzip", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "A module for compressing and decompressing JSON data using Gzip compression algorithm and HPack compression algorithm.", | ||
@@ -9,3 +9,4 @@ "main": "index.js", | ||
"eslint": "./node_modules/eslint/bin/eslint.js --max-warnings 0 --ext .js .", | ||
"eslint:fix": "./node_modules/eslint/bin/eslint.js --max-warnings 0 --ext .js --fix ." | ||
"eslint:fix": "./node_modules/eslint/bin/eslint.js --max-warnings 0 --ext .js --fix .", | ||
"semantic-release": "semantic-release" | ||
}, | ||
@@ -31,9 +32,16 @@ "keywords": [ | ||
"devDependencies": { | ||
"@types/debug": "^4.1.8", | ||
"eslint": "^8.42.0", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
"eslint-plugin-import": "^2.27.5", | ||
"eslint-plugin-jest": "^27.2.1", | ||
"jest": "^29.5.0", | ||
"eslint-plugin-jest": "^27.2.1" | ||
"semantic-release": "^21.0.7" | ||
}, | ||
"dependencies": {} | ||
} | ||
"dependencies": { | ||
"debug": "^4.3.4" | ||
}, | ||
"publishConfig": { | ||
"@MkDierz:registry": "https://npm.pkg.github.com" | ||
} | ||
} |
@@ -29,3 +29,3 @@ # json-pack-gzip | ||
### ```compress(jsonObject)``` | ||
### ```compress(jsonObject, debug = false)``` | ||
@@ -35,9 +35,11 @@ Compresses a JSON object using Gzip compression algorithm. The input JSON object is first packed into a homogeneous array and then stringified before compression. | ||
- ```jsonObject```(Array): The JSON object to be compressed. | ||
- ```debug```(Boolean): options for debugging, defaults to false. | ||
Returns a compressed data as a Buffer object. | ||
### ```decompress(jsonBuffer)``` | ||
### ```decompress(jsonBuffer, debug = false)``` | ||
Decompresses a Buffer object to a JSON object using Gzip decompression algorithm. The decompressed data is first parsed from a homogeneous array into a list of objects. | ||
- ```jsonBuffer``` (Buffer): The Buffer object to be decompressed. | ||
- ```debug```(Boolean): options for debugging, defaults to false. | ||
@@ -44,0 +46,0 @@ Returns a decompressed and unpacked JSON object as a list of objects. |
@@ -56,1 +56,66 @@ const { | ||
}); | ||
test('complex json compress', () => { | ||
const data = { | ||
products: [ | ||
{ | ||
id: 11, | ||
title: 'perfume Oil', | ||
price: 13, | ||
}, | ||
{ | ||
id: 12, | ||
title: 'Brown Perfume', | ||
price: 40, | ||
}, | ||
{ | ||
id: 13, | ||
title: 'Fog Scent Xpressio Perfume', | ||
price: 13, | ||
}, | ||
{ | ||
id: 14, | ||
title: 'Non-Alcoholic Concentrated Perfume Oil', | ||
price: 120, | ||
}, | ||
{ | ||
id: 15, | ||
title: 'Eau De Perfume Spray', | ||
price: 30, | ||
}, | ||
{ | ||
id: 16, | ||
title: 'Hyaluronic Acid Serum', | ||
price: 19, | ||
}, | ||
{ | ||
id: 17, | ||
title: 'Tree Oil 30ml', | ||
price: 12, | ||
}, | ||
{ | ||
id: 18, | ||
title: 'Oil Free Moisturizer 100ml', | ||
price: 40, | ||
}, | ||
{ | ||
id: 19, | ||
title: 'Skin Beauty Serum.', | ||
price: 46, | ||
}, | ||
{ | ||
id: 20, | ||
title: 'Freckle Treatment Cream- 15gm', | ||
price: 70, | ||
}, | ||
], | ||
total: 100, | ||
skip: 10, | ||
limit: 10, | ||
}; | ||
expect(() => { | ||
const compressed = compress(data); | ||
const decompressed = decompress(compressed); | ||
return decompressed === data; | ||
}).toBeTruthy(); | ||
}); |
23292
68.73%14
75%520
64.04%62
3.33%1
Infinity%7
40%+ Added
+ Added
+ Added