Skip to content

Commit

Permalink
README.md: replace the examples with something more complex
Browse files Browse the repository at this point in the history
Closes #4.
  • Loading branch information
pabigot committed Nov 22, 2015
1 parent 339b479 commit 097cfee
Show file tree
Hide file tree
Showing 3 changed files with 406 additions and 36 deletions.
208 changes: 172 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,50 +27,186 @@ Layout support is provided for these types of data:
Development and testing is done using Node.js, supporting versions 0.12
and later. Install with `npm install buffer-layout`.

## Usage
## Examples

Assume you have a sensor that records environmental data using the
following (packed) C structure:
All examples are from the `test/examples.js` unit test and assume the
following context:

struct reading {
uint8_t sensor_id;
int16_t T_Cel;
uint16_t RH_pph;
uint32_t timestamp_posix;
};
var assert = require("assert"),
_ = require("lodash"),
lo = require("buffer-layout");

buffer-layout will allow you to process this data with code like this:
The examples give only a taste of what can be done. Structures, unions,
and sequences can nest; [union
discriminators](http://pabigot.github.io/buffer-layout/module-Layout-UnionDiscriminator.html)
can be within the union or external to it; sequence and blob lengths may
be fixed or read from the buffer.

var lo = require('buffer-layout');

var rds = new lo.Structure([lo.u8('sensor_id'),
lo.s16('T_Cel'),
lo.u16('RH_pph'),
lo.u32('timestamp_posix')]),
rdb = Buffer('0517003200de262d56', 'hex');
console.log(rds.decode(rdb));
For full details see the [documentation](http://pabigot.github.io/buffer-layout/).

which produces:
### Four-element array of 16-bit signed little-endian integers

{ sensor_id: 5,
T_Cel: 23,
RH_pph: 50,
timestamp_posix: 1445799646 }
The C definition:

If you need to generate data encoded in this format:
int16_t arr[4] = { 1, -1, 3, -3 };

function Reading (sn, temp, hum) {
this.sensor_id = sn;
this.T_Cel = temp;
this.RH_pph = hum;
this.timestamp_posix = 1445799646;
}
rd = new Reading(7, -5, 93);
rds.encode(rd, rdb);
console.log(rdb.toString('hex'));
The buffer-layout way:

which produces:
var ds = lo.seq(lo.s16(), 4),
b = new Buffer(8);
ds.encode([1, -1, 3, -3], b);
assert.equal(Buffer('0100ffff0300fdff', 'hex').compare(b), 0);
assert(_.isEqual(ds.decode(b), [1, -1, 3, -3]));

07fbff5d00de262d56
See [Int](http://pabigot.github.io/buffer-layout/module-Layout-Int.html)
and [Sequence](http://pabigot.github.io/buffer-layout/module-Layout-Sequence.html).

For full details see the [documentation](http://pabigot.github.io/buffer-layout/).
### A native C `struct` on a 32-bit little-endian machine

The C definition:

struct ds {
uint8_t v;
uint32_t u32;
} st;

The buffer-layout way:

var ds = lo.struct([lo.u8('v'),
lo.seq(lo.u8(), 3), // alignment padding
lo.u32('u32')]),
b = new Buffer(8);
b.fill(0xbd);
ds.encode({v:1, u32: 0x12345678}, b);
assert.equal(Buffer('01bdbdbd78563412', 'hex').compare(b), 0);
assert(_.isEqual(ds.decode(b), {v: 1, u32: 0x12345678}));

Note that the C language requires padding which must be explicitly added
in the buffer-layout structure definition. Since the padding is not
accessible, the corresponding layout has no
[property](http://pabigot.github.io/buffer-layout/module-Layout-Layout.html#property).

See [Structure](http://pabigot.github.io/buffer-layout/module-Layout-Structure.html).

### A packed C `struct` on a 32-bit little-endian machine

The C definition:

struct ds {
uint8_t v;
uint32_t u32;
} __attribute__((__packed__)) st;

The buffer-layout way:

var ds = lo.struct([lo.u8('v'),
lo.u32('u32')]),
b = new Buffer(5);
b.fill(0xbd);
ds.encode({v:1, u32: 0x12345678}, b);
assert.equal(Buffer('0178563412', 'hex').compare(b), 0);
assert(_.isEqual(ds.decode(b), {v: 1, u32: 0x12345678}));

### A tagged union of 4-byte values

Assume a 5-byte packed structure where the interpretation of the last
four bytes depends on the first byte. The C definition:

struct {
uint8_t t;
union ds {
uint8_t u8[4]; // default interpretation
int16_t s16[2]; // when t is 'h'
uint32_t u32; // when t is 'w'
float f32; // when t is 'f'
} u;
} __attribute__((__packed__)) un;

The buffer-layout way:

var t = lo.u8('t'),
un = lo.union(t, lo.seq(lo.u8(), 4, 'u8')),
u32 = un.addVariant('w'.charCodeAt(0), lo.u32(), 'u32'),
s16 = un.addVariant('h'.charCodeAt(0), lo.seq(lo.s16(), 2), 's16'),
f32 = un.addVariant('f'.charCodeAt(0), lo.f32(), 'f32'),
b = new Buffer(un.span);
assert(_.isEqual(un.decode(Buffer('7778563412', 'hex')), { u32: 0x12345678 }));
assert(_.isEqual(un.decode(Buffer('660000bd41', 'hex')), { f32: 23.625 }));
assert(_.isEqual(un.decode(Buffer('a5a5a5a5a5', 'hex')), { t: 0xa5, u8:[ 0xa5, 0xa5, 0xa5, 0xa5 ]}));
s16.encode({s16:[123, -123]}, b);
assert.equal(Buffer('687b0085ff', 'hex').compare(b), 0);

See [Union](http://pabigot.github.io/buffer-layout/module-Layout-Union.html).

#### Packed bit fields on a little-endian machine

The C definition:

struct ds {
unsigned int b00l03: 3;
unsigned int b03l01: 1;
unsigned int b04l18: 24;
unsigned int b1Cl04: 4;
} st;

The buffer-layout way:

var ds = lo.bits(lo.u32()),
b = new Buffer(4);
ds.addField(3, 'b00l03');
ds.addField(1, 'b03l01');
ds.addField(24, 'b04l18');
ds.addField(4, 'b1Cl04');
b.fill(0xff);
ds.encode({b00l03:3, b04l18:24, b1Cl04:4}, b);
assert.equal(Buffer('8b010040', 'hex').compare(b), 0);
assert(_.isEqual(ds.decode(b), {b00l03:3, b03l01:1, b04l18:24, b1Cl04:4}));

See [BitStructure](http://pabigot.github.io/buffer-layout/module-Layout-BitStructure.html).

### A NUL-terminated C string

The C definition:

const char str[] = "hi!";

The buffer-layout way:

var ds = lo.cstr(),
b = new Buffer(8);
ds.encode('hi!', b);
var slen = ds.getSpan(b);
assert.equal(slen, 4);
assert.equal(Buffer('68692100', 'hex').compare(b.slice(0, slen)), 0);
assert.equal(ds.decode(b), 'hi!');

See [CString](http://pabigot.github.io/buffer-layout/module-Layout-CString.html).

### A fixed-length block of data offset within a buffer

The buffer-layout way:

var ds = lo.blob(4),
b = Buffer('0102030405060708', 'hex');
assert.equal(Buffer('03040506', 'hex').compare(ds.decode(b, 2)), 0);

See [Blob](http://pabigot.github.io/buffer-layout/module-Layout-Blob.html).

### A variable-length array of pairs of C strings

The buffer-layout way:

var pr = lo.seq(lo.cstr(), 2),
n = lo.u8('n'),
vla = lo.seq(pr, lo.offset(n, -1), 'a'),
st = lo.struct([n, vla], 'st'),
b = new Buffer(32),
arr = [['k1', 'v1'], ['k2', 'v2'], ['k3', 'etc']];
b.fill(0);
st.encode({a: arr}, b);
var span = st.getSpan(b);
assert.equal(span, 20);
assert.equal(Buffer('036b31007631006b32007632006b330065746300', 'hex').compare(b.slice(0, span)), 0);
assert(_.isEqual(st.decode(b), { n:3, a:arr}));

See [OffsetLayout](http://pabigot.github.io/buffer-layout/module-Layout-OffsetLayout.html).
112 changes: 112 additions & 0 deletions test/examples.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>

void
hexlify (const char * tag,
const void * sp,
size_t count)
{
const uint8_t * u8p = (const uint8_t *)sp;
const uint8_t * const u8pe = u8p + count;
printf("%s: ", tag);
while (u8p < u8pe) {
printf("%02x", *u8p);
++u8p;
}
putchar('\n');
}

void
ex_struct (void)
{
struct ds {
uint8_t v;
uint32_t u32;
} st;
memset(&st, 0xbd, sizeof(st));
st.v = 1;
st.u32 = 0x12345678;
hexlify("C struct", &st, sizeof(st));
}

void
ex_packed_struct (void)
{
struct ds {
uint8_t v;
uint32_t u32;
} __attribute__((__packed__)) st;
memset(&st, 0xbd, sizeof(st));
st.v = 1;
st.u32 = 0x12345678;
hexlify("packed C struct", &st, sizeof(st));
}

void
ex_cstr (void)
{
const char str[] = "hi!";
hexlify("C string", str, 1+strlen(str));
}

void
ex_bitfield (void)
{
struct ds {
unsigned int b00l03: 3;
unsigned int b03l01: 1;
unsigned int b04l18: 24;
unsigned int b1Cl04: 4;
} st;
assert(4 == sizeof(st));
memset(&st, 0xFF, sizeof(st));
st.b00l03 = 3;
st.b04l18 = 24;
st.b1Cl04 = 4;
hexlify("bitfield lsb on le", &st, sizeof(st));
}

void
ex_arr4s16 (void)
{
int16_t arr[4] = { 1, -1, 3, -3 };
hexlify("arr[4] int16_t", arr, sizeof(arr));
}

void
ex_union4B (void)
{
struct {
uint8_t t;
union ds {
uint8_t u8[4];
int16_t s16[2];
uint32_t u32;
float f32;
} u;
} __attribute__((__packed__)) un;
un.t = 'w';
un.u.u32 = 0x12345678;
hexlify("un u32", &un, sizeof(un));
un.t = 'f';
un.u.f32 = 23.625;
hexlify("un f32", &un, sizeof(un));
memset(&un, 0xa5, sizeof(un));
hexlify("un dflt", &un, sizeof(un));
}

int
main (int argc,
char * argv[])
{
ex_struct();
ex_packed_struct();
ex_cstr();
ex_bitfield();
ex_arr4s16();
ex_union4B();
return EXIT_SUCCESS;
}
Loading

0 comments on commit 097cfee

Please sign in to comment.