npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

whitney-buffers

v0.0.9

Published

tiny self-describing binary protocol based on Arthur Whitney's q protocol

Downloads

18

Readme

npm npm Travis Test Coverage Maintainability

JavaScript Style Guide

Why

  • JSON data is wastefully large and shouldn't be sent over the wire without encoding
  • Schema driven protocols bloat your javascript code as the amount of data structures increase

Benefits

  • Flexible schema
  • Real Longs (64 bit integers) via Long.js
  • ~7kb (minified and gzipped). Source code will never grow.
  • Send data typed for smaller message size or untyped for faster encoding and decoding
  • Interoperable with Kdb+ (see http://code.kx.com/q/interfaces/)

Installation

Whitney Buffers work with node.js and the browser. Install from npm or grab the latest release.

npm i --save whitney-buffers or yarn add whitney-buffers

import wb from 'whitney-buffers'

⚠️ Be aware that this will overwrite the window object for Buffer. It only contains the methods needed for whitney-buffers to operate. If you use buffers elsewhere in your application you should omit including this implementation.

⚠️ Also, including the buffer shim will not guarantee that your bundler will omit adding the full Buffer global to your package. Please refer to your module packager's documentation to find the appropriate configuration.

Usage

This module exposes functions in Q style by default. If you prefer to use the aliased module:

import wb from 'whitney-buffers/aliased'

On either the default or the aliased module you can use destructured imports as well as their respective default imports:

// Default
import wb from 'whitney-buffers'
wb.enc({ works: wb.b(true) })
// Aliased
import wb from 'whitney-buffers/aliased'
wb.encode({ works: wb.bool(true) })

or

// Default
import { enc, b } from 'whitney-buffers'
enc({ works: b(true) })
// Aliased
import { encode, boolean } from 'whitney-buffers/aliased'
encode({ works: boolean(true) })

API

enc(Object) alias: encode

Encodes an Object into a Uint8Array|Buffer

import { enc } from 'whitney-buffers'
const pkt = enc({ hello: 'world' })
// pkt = Uint8Array(38) [...]

with alias:

import { encode } from 'whitney-buffers/aliased'
const pkt = encode({ hello: 'world' })
// pkt = Uint8Array(38) [...]

dec(Uint8Array|Buffer) alias: decode

Decodes a Uint8Array|Buffer from an Object

import { enc, dec } from 'whitney-buffers'
const pkt = enc({ hello: 'world' })
const msg = dec(pkt)
// msg = { hello: 'world' }

with alias:

import { encode, decode } from 'whitney-buffers/aliased'
const pkt = encode({ hello: 'world' })
const msg = decode(pkt)
// msg = { hello: 'world' }

Types

Types allow you to encode a piece of data as a specific type. This can save you a lot of bytes in your packets, with the trade off of slightly slower encode and decode times.

To use a define a type you just pass your value to the desired type function:

import { enc, b, i, f, j } from 'whitney-buffers'
const pkt = enc({
  bool: b(true),
  int: i(-1),
  float: f(3.14),
  long: j({ low: '0xFFFFFFFF', high: '0x22222222' })
})
// pkt = Uint8Array(66) [...]

The types are also exposed as aliases:

import { encode, boolean, int, float, long } from 'whitney-buffers'
const pkt = enc({
  bool: boolean(true),
  int: int(-1),
  float: float(3.14),
  long: long({ low: '0xFFFFFFFF', high: '0x22222222' })
})
// pkt = Uint8Array(66) [...]

Types have a scalar representation and a vector representation. Take the timestamp for example:

import { enc, p, P } from 'whitney-buffers'
const pkt = enc({
  t: p(new Date()),
  ts: P([new Date(), new Date()])
})
// pkt = Uint8Array(73) [...]

Vector representations are aliased as well and are expressed as the plural of the type:

import { encode, timestamp, timestamps } from 'whitney-buffers'
const pkt = encode({
  t: timestamp(new Date()),
  ts: timestamps([new Date(), new Date()])
})
// pkt = Uint8Array(73) [...]

Below is a map of all the types, their vector representations, their size in bytes and their definition.

| Type | Scalar | Vector | Aliased Scalar | Alaised Vector | Size | Definition | |-----------|--------|--------|----------------|----------------|------|-------------------------------------| | Boolean | b | B | boolean | booleans | 1 | b(bool: boolean) | | GUID | g | G | guid | guids | 16 | g(guid: string) | | Byte | x | X | byte | bytes | 1 | x(byte: number) | | Short | h | H | short | shorts | 2 | h(byte: number) | | Integer | i | I | int | ints | 4 | i(byte: number) | | Long | j | J | long | longs | 8 | j({ low: number, high: number })| | Real | e | E | real | reals | 4 | e(real: number) | | Float | f | F | float | floats | 8 | f(float: number) | | Character | c | C | char | chars | 1 | c(char: number) | | Symbol | s | S | symbol | symbols | ~ | s(symbol: string) | | Timestamp | p | P | timestamp | timestamps | 8 | p(timestamp: date) | | Month | m | M | month | months | 4 | m(month: date) | | Date | d | D | date | dates | 4 | d(date: date) | | Datetime | z | Z | datetime | datetimes | 8 | z(datetime: date) | | Timespan | n | N | timespan | timespans | 8 | n(timespan: date) | | Minute | u | U | minute | minutes | 4 | u(minute: date) | | Second | v | V | second | seconds | 4 | v(second: date) | | Time | t | T | time | times | 4 | t(time: date) |

Structures

There are two structures available in both the default and aliased exports. They have the same name in both modules:

dict(Object) Define a typed map

This method is used internally by the encoder on all objects. It is exposed for you to encode your own typed maps explicitly, but is not required.

import { enc, dict, b, i, f, j } from 'whitney-buffers'
const pkt = enc(dict({
  bool: b(true),
  int: i(-1),
  float: f(3.14),
  long: j({ low: '0xFFFFFFFF', high: '0x22222222' })
}))
// pkt = Uint8Array(66)

list(Array)

This method is for describing arrays of mixed types:

import { enc, list, b, i, f, j } from 'whitney-buffers'
const pkt = enc(list([
  b(true),
  i(-1),
  f(3.14),
  j({ low: '0xFFFFFFFF', high: '0x22222222' })
]))
// pkt = Uint8Array(39)

Tips and Tricks

Object keys

All your keys are encoded as symbols (which are essentially strings). You should try to keep your keys short in order to keep your packet size small. One trick that I have been using is to have a shared map of your keys and only pass an int as the key. Something like this:

// A shared class
class Keys {
  constructor(arr = []) {
    this.arr = arr
  }
  get (str) {
    const key = this.arr.indexOf(str)
    if (key === -1) throw new Error('key not found')
    return key
  }
  set (obj) {
    const o = {}
    for (let key in obj) {
      const name = this.arr[key]
      if (name) {
        o[name] = obj[key]
      }
    }
    return o
  }
}
// ✌️"Schema"✌️ definition shared by encoding side and decoding side
export const signup = [
  'name',
  'email'
]
// Message creator
import { enc, s } from 'whitney-buffers'
import Keys, { signupSchema } from '@my/shared-schemas'
import send from './somewhere'
const signupKeys = new Keys(signupSchema)
send(enc({
  [signup.get('name')]: s('Archimedes'),
  [signup.get('email')]: s('[email protected]')
}))
// Message receiver
import { dec, s } from 'whitney-buffers'
import Keys, { signup } from '@my/shared-schemas'
import receive from './somewhere'
const keys = new Keys(signup)
receive.on('message', event => {
  const msg = keys.set(dec(event))
  // msg = {
  //  name: Archimedes,
  //  email: [email protected]
  // }
})

Nulls and empty types

The typed API will not allow you to send an empty type. This is by design. If you want to send null or empty types you can encode the empty value without a type. If are using types to get the smallest possible packet size, your schema design should not require null values. You end up wasting bytes for no reason.

Special Thanks

Special thanks to Michael Wittig for his work on node-q and Feross Aboukhadijeh for his work on buffer.