Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

compactr

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

compactr - npm Package Compare versions

Comparing version 0.0.1 to 1.0.1

docs/SPECS.md

2

package.json
{
"name": "compactr",
"version": "0.0.1",
"version": "1.0.1",
"description": "A compression library for the modern web",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -14,3 +14,3 @@ # Compactr

Compactr is a library to compress and decompress Javascript objects before sending them over the web. It's immencely usefull for web applications that use sockets a lot. Smaller payloads equals better, faster throughput and less bandwidth costs.
Compactr is a library to compress and decompress Javascript objects before sending them over the web. It's immensely useful for web applications that use sockets a lot. Smaller payloads equals better, faster throughput and less bandwidth costs.

@@ -25,3 +25,3 @@

Why yes, Protocol Buffer is by far the better performing protocol out there, but there's a few things about it I don't like - as a Node developper.
Why yes, Protocol Buffer is by far the better performing protocol out there, but there's a few things about it I don't like - as a Node developer.

@@ -32,6 +32,8 @@ The first thing that comes to mind is the painful management of `.proto` files.

Furthermore, Compactr has **NO** dependencies or compiled modules. It's the lightest module you've ever seen!
## So what's your solution?
Protocol Buffers are awesome. Having schemas to deflate and inflate data while maintaining some kind of validation is a great concept. Compactr's goal is to build on that to better suite Node server developement and reduce noise by allowing you to re-use your current Model schemas.
Protocol Buffers are awesome. Having schemas to deflate and inflate data while maintaining some kind of validation is a great concept. Compactr's goal is to build on that to better suit Node server development and reduce noise by allowing you to re-use your current Model schemas.

@@ -43,20 +45,9 @@

```
/* Waterline Schema (User) */
| **Waterline** | **Mongoose** |
| --- | --- | --- |
| `{` <br> ` id: {` <br> ` type: 'integer',` <br> ` required: true` <br> ` },` <br> ` name: 'string'` <br> `}` | `{` <br> ` id: {` <br> ` type: Number,` <br> ` required: true` <br> ` },` <br> ` name: String` <br> `}` |
{
id: {
type: 'integer',
required: true
},
name: {
type: 'string',
defaultsTo: 'John'
}
}
```
```
/* User compessing in controller */
/* User compessing in a controller */

@@ -69,3 +60,3 @@ const Compactr = require('compactr');

```
```

@@ -78,2 +69,3 @@ ```

```
No need to create additional models for serialization!

@@ -97,4 +89,5 @@

See this chart:
**TODO**
I'm still working on graphs and proper test scenarios, but I can say that it performs as fast, and sometimes faster than JSON encoding/decoding and outputs a buffer that is more or less half the size!

@@ -105,7 +98,6 @@ ## Alright, what about features?

- [ ] Use Waterline schemas
- [ ] Synchronously encode/decode
- [ ] Asynchronously encode/decode (Promise-based)
- [ ] Stream encoding-decoding
- [ ] Nested objects
- [x] Use Waterline schemas
- [x] Use Mongoose schemas
- [x] Synchronously encode/decode
- [ ] Nested objects/ Arrays

@@ -115,3 +107,3 @@ ## Alright, I'm convinced! How can I help?

Just open an issue, identifying it as a feature that you want to tackle.
Ex: `STORY - [...]`
And we'll take the discussion there.
Ex: `STORY - [...]`
And we'll take the discussion there.

@@ -9,39 +9,160 @@ /**

const Workbench = require('./Workbench');
const Types = require('./Types');
/* Local variables -----------------------------------------------------------*/
const SEP_CODE = 255;
const READ_SKIP = 2;
const INT8_SIZE = 1;
const INT16_SIZE = 2;
const INT32_SIZE = 4;
const DOUBLE_SIZE = 6;
const CPR = Object.create(null);
let doubleBuffer = Buffer.alloc(8);
/* Methods -------------------------------------------------------------------*/
/**
* Decodes a Compactr Buffer using a Schema
* @param {object} schema The Schema to use to decode the buffer
* @param {Buffer} data The buffer to decode
* @returns {object} The decoded buffer
*/
function Decode(schema, data) {
let result = {};
let keys = Object.keys(schema);
let bytes = new Workbench(data);
const keys = Object.keys(schema);
const len = data.length;
let _caret = len;
let _propName;
let _propType;
let _caret = 0;
for (let i = len - 1; i >= 0; i--) {
if (data[i] === SEP_CODE) {
let _propName = keys[read_index(data, i)];
if (_propName !== undefined && schema[_propName] !== undefined) {
let _propType = Types.resolve(schema[_propName].type || schema[_propName]);
result[_propName] = read(data, _propType, i + READ_SKIP, _caret);
if (i === 0) break;
_caret = i;
i -= READ_SKIP;
}
}
}
data.forEach((byte, index) => {
// SEP
if (byte === Workbench.SEP_CODE) {
if (index > 0) {
_propName = keys[bytes.read(Types.NUMBER, _caret + 1, _caret + 2)];
_propType = Types.resolve(schema[_propName].type || schema[_propName]);
result[_propName] = bytes.read(_propType, _caret + 2, index);
}
_caret = index;
}
if (index === data.length - 1) {
_propName = keys[bytes.read(Types.NUMBER, _caret + 1, _caret + 2)];
_propType = Types.resolve(schema[_propName].type || schema[_propName]);
result[_propName] = bytes.read(_propType, _caret + 2, index + 1);
}
});
return result;
}
/**
* Returns a Schema key index - 1 byte - (0<->255)
* @param {Buffer} buffer The buffer to read from
* @param {integer} index The buffer index to read at
* @returns {integer} The Schema index key
*/
function read_index(buffer, index) {
return buffer[index + 1];
}
/**
* Returns a Boolean value - 1 byte - (0<->1)
* @param {Buffer} buffer The buffer to read from
* @param {integer} index The buffer index to read at
* @returns {integer} The Boolean value
*/
function read_boolean(buffer, index) {
return buffer[index] === 1;
}
/**
* Returns a signed INT8 value - 1 byte - (-128<->127)
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {integer} The INT8 value
*/
function read_int8(buffer, from, to) {
return buffer.readInt8(from, to);
}
/**
* Returns a signed INT16 value - 2 bytes - (-32768<->32767)
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {integer} The INT16 value
*/
function read_int16(buffer, from, to) {
return buffer.readInt16BE(from, to);
}
/**
* Returns a signed INT32 value - 4 bytes - (...)
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {integer} The INT32 value
*/
function read_int32(buffer, from, to) {
return buffer.readInt32BE(from, to);
}
/**
* Returns a String value
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {integer} The String value
*/
function read_string(buffer, from, to) {
let acc = [];
for (let i = from; i < to; i++) {
acc.push(buffer[i]);
}
return String.fromCodePoint.apply(CPR, acc);
}
/**
* Returns a double value - 6 bytes
* !Doubles are in fact 8 bytes long, but only the first 6 are encoded!
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {integer} The double value
*/
function read_double(buffer, from, to) {
buffer.copy(
doubleBuffer,
0,
from,
from + DOUBLE_SIZE
);
return doubleBuffer.readDoubleBE();
}
/**
* Returns the decoded value for a property
* @param {integer} type The expected variable type
* @param {Buffer} buffer The buffer to read from
* @param {integer} from The buffer index to read from
* @param {integer} to The buffer index to read to
* @returns {?} The decoded value
*/
function read(buffer, type, from, to) {
let res;
if (type === Types.BOOLEAN) res = read_boolean(buffer, from);
else if (type === Types.NUMBER) {
if (to - from === INT8_SIZE) res = read_int8(buffer, from, to);
else if (to - from === INT16_SIZE) res = read_int16(buffer, from, to);
else if (to - from === INT32_SIZE) res = read_int32(buffer, from, to);
else res = read_double(buffer, from, to);
}
else if (type === Types.STRING) res = read_string(buffer, from, to);
return res;
}
/* Exports -------------------------------------------------------------------*/
module.exports = Decode;

@@ -9,25 +9,167 @@ /**

const Workbench = require('./Workbench');
const Types = require('./Types');
/* Local variables -----------------------------------------------------------*/
// One frame - all overheads
const MAX_SIZE = 1400;
// Number ranges and byte sizes
const MIN_INT8 = -128;
const MAX_INT8 = 127;
const MIN_INT16 = -32768;
const MAX_INT16 = 32767;
const INT8_SIZE = 1;
const INT16_SIZE = 2;
const INT32_SIZE = 4;
const DOUBLE_SIZE = 6;
const SEP_CODE = 255;
const allowed_types = ['number', 'boolean', 'string'];
let work_buffer = Buffer.allocUnsafe(MAX_SIZE);
/* Methods -------------------------------------------------------------------*/
function Encode(schema, obj) {
let result = new Workbench();
/**
* Encodes a JS object into a Buffer using a Schema
* @param {object} schema The Schema to use for encoding
* @param {object} payload The payload to encode
* @returns {Buffer} The encoded Buffer
*/
function Encode(schema, payload) {
let result = work_buffer;
const keys = Object.keys(schema);
const len = keys.length;
Object.keys(schema).forEach((key, index) => {
if (obj[key]) {
let _type = schema[key].type || schema[key];
result.append(Types.SEP);
result.append(Types.NUMBER, index);
result.append(Types.resolve(_type), obj[key]);
result.caret = 0;
for (let i = len - 1; i >= 0; i--) {
let key = keys[i];
if (is_valid(key, payload)) {
let type = Types.resolve(schema[key].type || schema[key]);
append_index(result, i);
if (type === Types.BOOLEAN) append_boolean(result, payload[key]);
else if (type === Types.NUMBER) append_number(result, payload[key]);
else if (type === Types.STRING) append_string(result, payload[key]);
}
});
return result.read(Types.BUFFER);
}
return result.slice(0, result.caret);
}
/**
* Returns wether a payload property is valid for encoding
* If not, it will be skipped
* @param {string} key The property key to validate
* @param {object} payload The payload to encode
* @returns {boolean} Wether the property is valid for encoding
*/
function is_valid(key, payload) {
if (key in payload) {
let _type = typeof payload[key];
return (allowed_types.includes(_type));
}
return false;
}
/**
* Appends a Number type value to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {number} data The data to append
*/
function append_number(buffer, data) {
if (Number.isInteger(data)) {
if (data <= MAX_INT8 && data >= MIN_INT8) {
append_int8(buffer, data);
}
else if (data <= MAX_INT16 && data >= MIN_INT16) {
append_int16(buffer, data);
}
else append_int32(buffer, data);
}
else append_double(buffer, data);
}
/**
* Appends a Boolean type value to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {number} data The data to append
*/
function append_boolean(buffer, data) {
buffer[buffer.caret] = data ? 1 : 0;
buffer.caret += INT8_SIZE;
}
/**
* Appends a signed INT8 type value to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {number} data The data to append
*/
function append_int8(buffer, data) {
buffer.writeInt8(data, buffer.caret);
buffer.caret += INT8_SIZE;
}
/**
* Appends a signed INT16 type value to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {number} data The data to append
*/
function append_int16(buffer, data) {
buffer.writeInt16BE(data, buffer.caret);
buffer.caret += INT16_SIZE;
}
/**
* Appends a signed INT32 type value to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {number} data The data to append
*/
function append_int32(buffer, data) {
buffer.writeInt32BE(data, buffer.caret);
buffer.caret += INT32_SIZE;
}
/**
* Appends a double type value to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {number} data The data to append
*/
function append_double(buffer, data) {
// Ommit last 2 digits of the double
buffer.writeDoubleBE(data, buffer.caret);
buffer.caret += DOUBLE_SIZE;
}
/**
* Appends a String type value to the Buffer
* @param {Buffer} buffer The Buffer to append to
* @param {number} data The data to append
*/
function append_string(buffer, data) {
let len = data.length;
for (let i = 0; i < len; i++) {
buffer[buffer.caret + i] = data.codePointAt(i);
}
buffer.caret += len;
}
/**
* Appends an index type value to the Buffer [255, x]
* @param {Buffer} buffer The Buffer to append to
* @param {number} data The data to append
*/
function append_index(buffer, data) {
buffer[buffer.caret] = SEP_CODE;
// Unsigned Int
buffer[buffer.caret + 1] = data;
buffer.caret += INT16_SIZE;
}
/* Exports -------------------------------------------------------------------*/
module.exports = Encode;
/**
* Main module for Kompactor
* Main module for Compactr
*/

@@ -4,0 +4,0 @@

@@ -9,21 +9,34 @@ /**

const ARRAY = 0;
const BOOLEAN = 1;
const BUFFER = 2;
const NUMBER = 3;
const STRING = 4;
const SEP = 5;
const BOOLEAN = 0;
const BUFFER = 1;
const NUMBER = 2;
const STRING = 3;
const INDEX = 4;
const BOOLEAN_STR = 'boolean';
const BUFFER_STR = 'buffer';
const NUMBER_STR = 'number';
const STRING_STR = 'string';
/* Methods -------------------------------------------------------------------*/
/**
* Returns the matching id for a data type
* @param {string|function} type Either a type constructor or name
* @returns {integer} The matching index
*/
function resolve(type) {
type = type.toLowerCase();
if (type === 'array' || type === Array) return ARRAY;
if (type === 'boolean' || type === Boolean) return BOOLEAN;
if (type === 'buffer' || type === Buffer) return BUFFER;
if (type === 'number' || type === Number) return NUMBER;
if (type === 'string' || type === String) return STRING;
throw new Error('Unrecognized type ' + type);
let res = BOOLEAN;
if (typeof type === STRING_STR) {
if (type === BUFFER_STR) res = BUFFER;
else if (type === NUMBER_STR) res = NUMBER;
else if (type === STRING_STR) res = STRING;
}
else {
if (type === Buffer) res = BUFFER;
else if (type === Number) res = NUMBER;
else if (type === String) res = STRING;
}
return res;
}

@@ -33,2 +46,2 @@

module.exports = { ARRAY, BOOLEAN, BUFFER, NUMBER, STRING, resolve };
module.exports = { BOOLEAN, BUFFER, NUMBER, STRING, INDEX, resolve };
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc