diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index c0e52bc..0cb9525 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -1,5 +1,10 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
+ globals: {
+ BigInt: false,
+ BigInt64Array: false,
+ BigUint64Array: false,
+ },
root: true,
parserOptions: {
ecmaVersion: 'latest',
diff --git a/README.md b/README.md
index fc87076..5822aad 100644
--- a/README.md
+++ b/README.md
@@ -223,22 +223,24 @@ console.log(schema.size()); // 1, because it's a bool.
## Primitive types
-| Type Name | Bytes | Range of Values |
-|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
-| bool (alias: boolean) | 1 (worst case, see [boolean coalescence](#boolean-coalescence)) | Truthy values are coerced to `true`; falsy values to `false` |
-| int8 | 1 | -128 to 127 |
-| uint8 | 1 | 0 to 255 |
-| int16 | 2 | -32,768 to 32,767 |
-| uint16 | 2 | 0 to 65,535 |
-| int32 | 4 | -2,147,483,648 to 2,147,483,647 |
-| uint32 | 4 | 0 to 4,294,967,295 |
-| float32 | 4 | 3.4E +/- 38 (7 digits) |
-| float64 | 8 | 1.7E +/- 308 (15 digits) |
-| string | [Prefix](#varuint-prefixes) followed by the [encoded](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encodeInto) string bytes | Any string |
-| buffer | [Prefix](#varuint-prefixes) followed by the bytes of the buffer | Any `ArrayBuffer`
Decodes to a `DataView`.
See: [buffers and arrays](#buffers-and-arrays). |
-| varuint |
size | min | max |
---|
1 | 0 | 127 |
2 | 128 | 16,383 |
3 | 16,384 | 2,097,151 |
4 | 2,097,152 | 268,435,455 |
5 | 268,435,456 | 34,359,738,367 |
6 | 34,359,738,368 | 4,398,046,511,103 |
7 | 4,398,046,511,104 | 562,949,953,421,311 |
| 0 to 562,949,953,421,311 |
-| varint | size | min | max |
---|
1 | -64 | 63 |
2 | -8,192 | 8,191 |
3 | -1,048,576 | 1,048,575 |
4 | -134,217,728 | 134,217,727 |
5 | -17,179,869,184 | 17,179,869,183 |
6 | -2,199,023,255,552 | 2,199,023,255,551 |
7 | -281,474,976,710,656 | 281,474,976,710,655 |
| -281,474,976,710,656 to 281,474,976,710,655 |
-| date | Same as `string` above after calling [`toIsoString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) | Value is coerced to `Date` e.g. `new Date(value).toIsoString()` |
+| Type Name | Bytes | Range of Values |
+|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| bool (alias: boolean) | 1 (worst case, see [boolean coalescence](#boolean-coalescence)) | Truthy values are coerced to `true`; falsy values to `false` |
+| int8 | 1 | -128 to 127 |
+| uint8 | 1 | 0 to 255 |
+| int16 | 2 | -32,768 to 32,767 |
+| uint16 | 2 | 0 to 65,535 |
+| int32 | 4 | -2,147,483,648 to 2,147,483,647 |
+| uint32 | 4 | 0 to 4,294,967,295 |
+| int64 | 8 | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
**NOTE:** Only accepts and decodes to [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)s. |
+| uint64 | 8 | 0 to 18,446,744,073,709,551,615
**NOTE:** Only accepts and decodes to [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)s. |
+| float32 | 4 | 3.4E +/- 38 (7 digits) |
+| float64 | 8 | 1.7E +/- 308 (15 digits) |
+| string | [Prefix](#varuint-prefixes) followed by the [encoded](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encodeInto) string bytes | Any string |
+| buffer | [Prefix](#varuint-prefixes) followed by the bytes of the buffer | Any `ArrayBuffer`
**NOTE:** Decodes to a `DataView`.
See: [buffers and arrays](#buffers-and-arrays). |
+| varuint | size | min | max |
---|
1 | 0 | 127 |
2 | 128 | 16,383 |
3 | 16,384 | 2,097,151 |
4 | 2,097,152 | 268,435,455 |
5 | 268,435,456 | 34,359,738,367 |
6 | 34,359,738,368 | 4,398,046,511,103 |
7 | 4,398,046,511,104 | 562,949,953,421,311 |
| 0 to 562,949,953,421,311 |
+| varint | size | min | max |
---|
1 | -64 | 63 |
2 | -8,192 | 8,191 |
3 | -1,048,576 | 1,048,575 |
4 | -134,217,728 | 134,217,727 |
5 | -17,179,869,184 | 17,179,869,183 |
6 | -2,199,023,255,552 | 2,199,023,255,551 |
7 | -281,474,976,710,656 | 281,474,976,710,655 |
| -281,474,976,710,656 to 281,474,976,710,655 |
+| date | Same as `string` above after calling [`toIsoString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) | Value is coerced to `Date` e.g. `new Date(value).toIsoString()` |
## Aggregate types
@@ -385,16 +387,13 @@ A similar performance gain is also used for arrays. The fast path is used for ar
- `uint32`
- `float32`
- `float64`
+- `int64`
+- `uint64`
Instead of copying the data from the buffer, a [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Typed_arrays#typed_array_views) is created over the encoded binary and returned instead. The same optimization is applied for encoding. This is roughly **1.5x** faster for encoding and ***50x*** faster for decoding a 1024-byte array on the machine used to benchmark. The gains increase even more as the size of the array increases.
**NOTE:** `float64` arrays are padded with an extra 4 bytes after the length prefix to satisfy the required 8-byte alignment.
-# TODO
-
-- BigInts?
-- Endianness?
-
# Q/A
**Q**: Why did you call it `crunches`?
diff --git a/src/codecs/array.js b/src/codecs/array.js
index 5a2fa08..b0524c6 100644
--- a/src/codecs/array.js
+++ b/src/codecs/array.js
@@ -10,6 +10,8 @@ export function typeToElementClass(type) {
case 'uint32': return Uint32Array;
case 'float32': return Float32Array;
case 'float64': return Float64Array;
+ case 'bigint64': return BigInt64Array;
+ case 'biguint64': return BigUint64Array;
}
return undefined;
}
diff --git a/src/codecs/array.test.js b/src/codecs/array.test.js
index b43d316..2a9c93d 100644
--- a/src/codecs/array.test.js
+++ b/src/codecs/array.test.js
@@ -11,6 +11,8 @@ import Uint32Codec from './uint32.js';
import Float32Codec from './float32.js';
import Float64Codec from './float64.js';
import StringCodec from './string.js';
+import Int64Codec from './int64.js';
+import Uint64Codec from './uint64.js';
Codecs.uint8 = Uint8Codec;
Codecs.int8 = Int8Codec;
@@ -22,6 +24,8 @@ Codecs.uint32 = Uint32Codec;
Codecs.float32 = Float32Codec;
Codecs.float64 = Float64Codec;
Codecs.string = StringCodec;
+Codecs.int64 = Int64Codec;
+Codecs.uint64 = Uint64Codec;
for (const numberType of [
'int8',
@@ -47,6 +51,26 @@ for (const numberType of [
});
}
+test('int64 array', async () => {
+ const codec = new Codec({
+ element: {type: 'int64'},
+ });
+ const value = [1n, -2n, 3n, -4n];
+ const view = new DataView(new ArrayBuffer(codec.size(value)));
+ expect(codec.encode(value, view, 0)).to.equal(36);
+ expect(codec.decode(view, {byteOffset: 0})).to.deep.equal(value);
+});
+
+test('uint64 array', async () => {
+ const codec = new Codec({
+ element: {type: 'uint64'},
+ });
+ const value = [1n, 2n, 3n, 4n];
+ const view = new DataView(new ArrayBuffer(codec.size(value)));
+ expect(codec.encode(value, view, 0)).to.equal(36);
+ expect(codec.decode(view, {byteOffset: 0})).to.deep.equal(value);
+});
+
test('string array', async () => {
const codec = new Codec({
element: {type: 'string'},
diff --git a/src/codecs/int64.js b/src/codecs/int64.js
new file mode 100644
index 0000000..ff8417f
--- /dev/null
+++ b/src/codecs/int64.js
@@ -0,0 +1,16 @@
+class Int64Codec {
+ decode(view, target) {
+ const value = view.getBigInt64(target.byteOffset);
+ target.byteOffset += 8;
+ return value;
+ }
+ encode(value, view, byteOffset) {
+ view.setBigInt64(byteOffset, value);
+ return 8;
+ }
+ size() {
+ return 8;
+ }
+}
+
+export default Int64Codec;
diff --git a/src/codecs/int64.test.js b/src/codecs/int64.test.js
new file mode 100644
index 0000000..cc04a39
--- /dev/null
+++ b/src/codecs/int64.test.js
@@ -0,0 +1,11 @@
+import {expect, test} from 'vitest';
+
+import Codec from './int64.js';
+
+test('int64', async () => {
+ const codec = new Codec();
+ const value = -32n;
+ const view = new DataView(new ArrayBuffer(codec.size(value)));
+ expect(codec.encode(value, view, 0)).to.equal(8);
+ expect(codec.decode(view, {byteOffset: 0})).to.deep.equal(value);
+});
diff --git a/src/codecs/uint64.js b/src/codecs/uint64.js
new file mode 100644
index 0000000..8b4911f
--- /dev/null
+++ b/src/codecs/uint64.js
@@ -0,0 +1,16 @@
+class Uint64Codec {
+ decode(view, target) {
+ const value = view.getBigUint64(target.byteOffset);
+ target.byteOffset += 8;
+ return value;
+ }
+ encode(value, view, byteOffset) {
+ view.setBigUint64(byteOffset, value);
+ return 8;
+ }
+ size() {
+ return 8;
+ }
+}
+
+export default Uint64Codec;
diff --git a/src/codecs/uint64.test.js b/src/codecs/uint64.test.js
new file mode 100644
index 0000000..3ef068c
--- /dev/null
+++ b/src/codecs/uint64.test.js
@@ -0,0 +1,11 @@
+import {expect, test} from 'vitest';
+
+import Codec from './uint64.js';
+
+test('uint64', async () => {
+ const codec = new Codec();
+ const value = 32n;
+ const view = new DataView(new ArrayBuffer(codec.size(value)));
+ expect(codec.encode(value, view, 0)).to.equal(8);
+ expect(codec.decode(view, {byteOffset: 0})).to.deep.equal(value);
+});