Skip to content

Commit

Permalink
feat(bitstream): add barebones bitReader/Writer()
Browse files Browse the repository at this point in the history
- update pkg & readme
  • Loading branch information
postspectacular committed Jul 6, 2022
1 parent 207404f commit e17dff9
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 1 deletion.
33 changes: 32 additions & 1 deletion packages/bitstream/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This project is part of the
- [API](#api)
- [BitOutputStream](#bitoutputstream)
- [BitInputStream](#bitinputstream)
- [Barebones alternatives](#barebones-alternatives)
- [Authors](#authors)
- [License](#license)

Expand Down Expand Up @@ -58,7 +59,7 @@ node --experimental-repl-await
> const bitstream = await import("@thi.ng/bitstream");
```

Package sizes (gzipped, pre-treeshake): ESM: 1.10 KB
Package sizes (gzipped, pre-treeshake): ESM: 1.31 KB

## Dependencies

Expand Down Expand Up @@ -164,6 +165,36 @@ input.read(7)
In addition to the generic `read()` method, there's also the slightly
faster `readBit()` for reading single bits.

### Barebones alternatives

For use cases requiring only word sizes <=8 bits and none of the advanced features provided by the above implementations, the package also provides functional barebones alternatives in the form of [`bitWriter()`](https://docs.thi.ng/umbrella/bitstream/modules.html#bitWriter) and [`bitReader()`](https://docs.thi.ng/umbrella/bitstream/modules.html#bitReader):

```ts
import { bitReader, bitWriter } from "@thi.ng/bistream";

const writer = bitWriter();
// write single bit
writer.write(1);

// write unsigned value (up to 8 bits)
writer.write(31, 5);

// retrieve buffer
const bytes = writer.bytes();
// Uint8Array(1) [ 252 ]

// create reader from byte buffer
const reader = bitReader(bytes);

// read single bit
reader();
// 1

// read n-bit unsigned value
reader(5);
// 31
```

## Authors

Karsten Schmidt
Expand Down
3 changes: 3 additions & 0 deletions packages/bitstream/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
},
"./output": {
"default": "./output.js"
},
"./simple": {
"default": "./simple.js"
}
},
"thi.ng": {
Expand Down
1 change: 1 addition & 0 deletions packages/bitstream/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./input.js";
export * from "./output.js";
export * from "./simple.js";
81 changes: 81 additions & 0 deletions packages/bitstream/src/simple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Barebones alternative to {@link BitOutputStream} for word sizes <= 8 and with
* minimal API surface. The returned object only exposes 2 functions:
*
* - `write(x, size)` - writes a single value of given bit size (default: 1 bit)
* - `bytes()` - retrieve all bytes written so far
*
* @remarks
* The internal backing buffer automatically resizes on demand. The optionally
* provided `capacity` is only the initial buffer size.
*
* @param capacity - initial capacity
*/
export const bitWriter = (capacity = 16) => {
let buf = new Uint8Array(capacity);
let pos = 0;
let bit = 8;

const ensure = () => {
if (++pos === buf.length) {
let b = new Uint8Array(buf.length << 1);
b.set(buf);
buf = b;
}
};

return {
write: (x: number, n = 1) => {
x &= (1 << n) - 1;
let b = bit - n;
let m = bit < 8 ? ~((1 << bit) - 1) : 0;
if (b >= 0) {
m |= (1 << b) - 1;
buf[pos] = (buf[pos] & m) | ((x << b) & ~m);
if (b === 0) {
ensure();
bit = 8;
} else {
bit = b;
}
} else {
bit = 8 + b;
buf[pos] = (buf[pos] & m) | ((x >>> -b) & ~m);
ensure();
buf[pos] = (buf[pos] & ((1 << bit) - 1)) | ((x << bit) & 0xff);
}
},

bytes: () => buf.slice(0, pos + (bit & 7 ? 1 : 0)),
};
};

/**
* Barebones alternative to {@link BitInputStream} for word sizes <= 8 and with
* minimal API surface and WITHOUT bounds checking of any form! The returned
* function reads `n` bits from the originally provided buffer.
*
* @param buf
*/
export const bitReader = (buf: Uint8Array | number[]) => {
let p = 0;
let b = 8;

return (n = 1) => {
let l = b - n;
let out;
if (l >= 0) {
b = l;
out = (buf[p] >>> l) & ((1 << n) - 1);
if (!l) {
p++;
b = 8;
}
} else {
out = (buf[p++] & ((1 << b) - 1)) << -l;
b = 8 + l;
out = out | (buf[p] >>> b);
}
return out;
};
};
31 changes: 31 additions & 0 deletions packages/bitstream/tpl.readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,37 @@ input.read(7)
In addition to the generic `read()` method, there's also the slightly
faster `readBit()` for reading single bits.

### Barebones alternatives

For use cases requiring only word sizes <=8 bits and none of the advanced features provided by the above implementations, the package also provides functional barebones alternatives in the form of [`bitWriter()`](https://docs.thi.ng/umbrella/bitstream/modules.html#bitWriter) and [`bitReader()`](https://docs.thi.ng/umbrella/bitstream/modules.html#bitReader):

```ts
import { bitReader, bitWriter } from "@thi.ng/bistream";

const writer = bitWriter();
// write single bit
writer.write(1);

// write unsigned value (up to 8 bits)
writer.write(31, 5);

// retrieve buffer
const bytes = writer.bytes();
// Uint8Array(1) [ 252 ]


// create reader from byte buffer
const reader = bitReader(bytes);

// read single bit
reader();
// 1

// read n-bit unsigned value
reader(5);
// 31
```

## Authors

${authors}
Expand Down

0 comments on commit e17dff9

Please sign in to comment.