-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e17451c
Showing
7 changed files
with
555 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
language: node_js | ||
|
||
node_js: | ||
- "node" | ||
- "lts/*" | ||
|
||
os: | ||
- linux | ||
- osx | ||
- windows | ||
|
||
after_success: | ||
- c8 -r text-lcov npm test | coveralls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Changelog | ||
|
||
## [1.0.0] - 2021-01-02 | ||
|
||
- Initial release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright 2021 Chris Veness | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# UintNArray | ||
|
||
[![Build Status](https://travis-ci.org/chrisveness/uintnarray.svg?branch=master)](https://travis-ci.org/chrisveness/uintnarray) | ||
[![Coverage Status](https://coveralls.io/repos/github/chrisveness/uintnarray/badge.svg)](https://coveralls.io/github/chrisveness/uintnarray) | ||
|
||
Arbitrary bit-width [typed array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) of unsigned integers. | ||
|
||
Unsigned integer arrays of anything between 1 and 32 bit-width words, extending the standard JavaScript Uint8Array, Uint16Array, and Uint32Array. | ||
|
||
Usage is equivalent to that of the standard [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array), [Uint16Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array), and [Uint32Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array), except that the constructor takes an initial argument specifying the bit-width of the words in the array. | ||
|
||
## Usage | ||
|
||
### In browser: | ||
|
||
import UintNArray from 'https://cdn.jsdelivr.net/npm/uintnarray@1.0.0/uintnarray.js'; | ||
|
||
### In Node.js: | ||
|
||
Install from [npm](https://www.npmjs.com/package/uintnarray) and then | ||
|
||
import UintNArray from 'uintnarray'; | ||
|
||
### Example: | ||
|
||
````javascript | ||
const ui8 = new Uint8Array([1, 2, 3, 4, 255, 254, 253, 252]); | ||
const uiN = new UintNArray(4, ui8.buffer); | ||
console.log(uiN.toString()); // "0,1,0,2,0,3,0,4,15,15,15,14,15,13,15,12" | ||
ui8[1] = 99; | ||
console.log(uiN.toString()); // "0,1,6,3,0,3,0,4,15,15,15,14,15,13,15,12" | ||
```` | ||
|
||
## Reference | ||
|
||
Static properties, static methods, instance properties, and instance methods are as per the standard typed arrays [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array), [Uint16Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array), and [Uint32Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array), with the following exceptions: | ||
|
||
### Constructor | ||
|
||
`UintNArray(bitWidth)`\ | ||
`UintNArray(bitWidth, length)`\ | ||
`UintNArray(bitWidth, typedArray)`\ | ||
`UintNArray(bitWidth, object)`\ | ||
`UintNArray(bitWidth, buffer [, bitOffset [, length]])` | ||
|
||
Equivalent to typed array constructors, except that the first argument is the bit width, which may be between 1 and 32. | ||
|
||
When the second argument is a buffer, the third argument is a bit offset rather than a byte offset. | ||
|
||
### Static properties | ||
|
||
`UintNArray.BYTES_PER_ELEMENT` is not available as bit-width is specified on construction. | ||
|
||
### Static methods | ||
|
||
`UintNArray.from()` and `UintNArray.of()` are not available, as a `UintNArray` cannot be created without specifying bit-width. | ||
|
||
### Instance properties | ||
|
||
`Uint8Array.prototype.byteLength` may return fractional values. | ||
|
||
`Uint8Array.prototype.byteOffset` may return fractional values. | ||
|
||
## Endian-ness | ||
|
||
Since it is working with arbitrary bit-widths, `UintNArray` is intrinsically big-endian. | ||
|
||
Little-endian order is useful for flexibility in register storage: an (e.g.) `int8*` pointing to `00001001` will give the same value (9) as an `int16*` pointing to `00001001 00000000`. Most modern computer architectures (32-bit and increasingly 64-bit) are little-endian. | ||
|
||
With arbitrary bit-width words, the opposite applies: the value ‘9’ should be the same in (e.g.) a 15-bit, 16-bit, 0r 17-bit word: | ||
|
||
15-bit: `. .0000000 00001001`\ | ||
16-bit: `. 00000000 00001001`\ | ||
17-bit: `0 00000000 00001001` | ||
|
||
Also, big-endian works as a byte stream: most network communications are big-endian – referred to as ‘network order’ (binary file formats such as PNG/GIF vary). | ||
|
||
If required, little-endian ordering of units within the UintNArray can be obtained by using a [`DataView`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/DataView) on the `UintNArray.buffer`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
{ | ||
"name": "uintnarray", | ||
"description": "Arbitrary bit-width typed array of unsigned integers", | ||
"author": "Chris Veness", | ||
"version": "1.0.0", | ||
"license": "MIT", | ||
"type": "module", | ||
"main": "uintnarray.js", | ||
"files": "uintnarray.js", | ||
"scripts": { | ||
"test": "mocha --exit test.js", | ||
"lint": "eslint uintnarray.js test.js", | ||
"cover": "c8 -r html npm test" | ||
}, | ||
"devDependencies": { | ||
"@babel/eslint-parser": "^7.0.0", | ||
"@babel/plugin-syntax-top-level-await": "^7.0.0", | ||
"c8": "^7.0.0", | ||
"chai": "chaijs/chai", | ||
"coveralls": "^3.0.0", | ||
"eslint": "^7.0.0", | ||
"mocha": "^8.0.0" | ||
}, | ||
"eslintConfig": { | ||
"env": { | ||
"browser": true, | ||
"es6": true, | ||
"mocha": true, | ||
"node": true | ||
}, | ||
"parser": "@babel/eslint-parser", | ||
"parserOptions": { | ||
"ecmaVersion": 2015, | ||
"sourceType": "module" | ||
}, | ||
"extends": "eslint:recommended", | ||
"globals": { | ||
"chai": true, | ||
"should": true | ||
}, | ||
"rules": { | ||
"array-bracket-spacing": [ "error", "always" ], | ||
"comma-dangle": [ "error", "always-multiline" ], | ||
"comma-spacing": [ "error" ], | ||
"curly": [ "error", "multi-line" ], | ||
"indent": [ "error", 4, { "SwitchCase": 1 } ], | ||
"key-spacing": [ "error", { "align": "value" } ], | ||
"keyword-spacing": [ "error" ], | ||
"no-case-declarations": "warn", | ||
"no-console": [ "warn", { "allow": [ "error", "info", "debug" ] } ], | ||
"no-irregular-whitespace": "warn", | ||
"no-redeclare": "warn", | ||
"no-shadow": "warn", | ||
"no-unused-vars": "warn", | ||
"no-var": "error", | ||
"object-curly-spacing": [ "error", "always" ], | ||
"prefer-const": "error", | ||
"quotes": [ "error", "single", "avoid-escape" ], | ||
"require-await": "error", | ||
"semi": [ "error", "always" ], | ||
"space-before-blocks": [ "error", "always" ], | ||
"space-in-parens": [ "error" ], | ||
"strict": [ "error", "global" ] | ||
} | ||
}, | ||
"babel": { | ||
"plugins": ["@babel/plugin-syntax-top-level-await"] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
/* UintNArray Test Harness © Chris Veness 2021 */ | ||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | ||
|
||
import UintNArray from './uintnarray.js'; | ||
|
||
if (typeof window == 'undefined') { // node ($ npm test) | ||
const chai = await import('chai'); | ||
global.should = chai.should(); | ||
} else { // browser (movable-type.co.uk/dev/uintnarray-test.html) | ||
window.should = chai.should(); | ||
} | ||
|
||
const test = it; // just an alias | ||
|
||
describe('construct from numeric (length)', function() { | ||
test('numeric length', () => new UintNArray(4, 5).toString().should.equal('0,0,0,0,0')); | ||
test('string length', () => new UintNArray(4, '5').toString().should.equal('0,0,0,0,0')); | ||
test('resulting buffer', () => new UintNArray(4, 5).buffer.byteLength.should.equal(3)); | ||
}); | ||
|
||
describe('construct from iterable', function() { | ||
test('toString', () => new UintNArray(4, [ 1, 2, 3, 4 ]).toString().should.equal('1,2,3,4')); | ||
test('element overflow', () => new UintNArray(4, [ 15, 16, 17 ]).toString().should.equal('15,0,1')); | ||
test('just below 2 bytes', () => new UintNArray(15, [ 10, 12 ]).toString().should.equal('10,12')); | ||
test('just below 2 bytes buffer', () => new UintNArray(15, [ 10, 12 ]).buffer.byteLength.should.equal(4)); | ||
test('just above 2 bytes', () => new UintNArray(17, [ 10, 12 ]).toString().should.equal('10,12')); | ||
test('just above 2 bytes buffer', () => new UintNArray(17, [ 10, 12 ]).buffer.byteLength.should.equal(5)); | ||
}); | ||
|
||
describe('construct from buffer', function() { | ||
const ui8 = new Uint8Array([ 1, 2, 3, 4, 255, 254, 253, 252 ]); | ||
test('default offset & length', () => new UintNArray(4, ui8.buffer).toString().should.equal('0,1,0,2,0,3,0,4,15,15,15,14,15,13,15,12')); | ||
test('offset & default length', () => new UintNArray(4, ui8.buffer, 4).toString().should.equal('1,0,2,0,3,0,4,15,15,15,14,15,13,15,12')); | ||
test('offset & length', () => new UintNArray(4, ui8.buffer, 4, 5).toString().should.equal('1,0,2,0,3')); | ||
test('offset & zero length', () => new UintNArray(4, ui8.buffer, 0, 0).toString().should.equal('')); | ||
test('non-numeric offset -> 0', () => new UintNArray(4, ui8.buffer, 'string').toString().should.equal('0,1,0,2,0,3,0,4,15,15,15,14,15,13,15,12')); | ||
test('non-numeric length -> 0', () => new UintNArray(4, ui8.buffer, 0, 'string').toString().should.equal('')); | ||
}); | ||
|
||
describe('construct from other', function() { | ||
test('default arg2', () => new UintNArray(4).toString().should.equal('')); | ||
test('non-numeric arg2', () => new UintNArray(4, 'string').toString().should.equal('')); | ||
}); | ||
|
||
describe('typed array properties', function() { | ||
const ui8 = new Uint8Array([ 1, 2, 3, 4, 255, 254, 253, 252 ]); | ||
test('bytes per elmt', () => should.equal(UintNArray.BYTES_PER_ELEMENT, undefined)); | ||
test('byteLength', () => new UintNArray(3, [ 1, 2, 3, 4 ]).byteLength.should.equal(1.5)); | ||
test('buffer', () => new UintNArray(4, ui8.buffer).buffer.should.equal(ui8.buffer)); | ||
test('length', () => new UintNArray(4, ui8.buffer).length.should.equal(16)); // requires special handling | ||
test('byteOffset', () => new UintNArray(4, ui8.buffer, 4).byteOffset.should.equal(0.5)); | ||
test('byteLength', () => new UintNArray(4, ui8.buffer, 4).byteLength.should.equal(7.5)); | ||
}); | ||
|
||
describe('typed array methods', function() { | ||
const uiN1 = new UintNArray(4, 6); | ||
uiN1.set([ 1, 2, 3 ], 2); | ||
test('set() from array', () => uiN1.toString().should.equal('0,0,1,2,3,0')); | ||
const uiN2 = new UintNArray(4, 6); | ||
uiN2.set(new Uint8Array([ 4, 5, 6 ]), 2); | ||
test('set() from typed array', () => uiN2.toString().should.equal('0,0,4,5,6,0')); | ||
test('subarray() no args', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray().toString().should.equal('1,2,3,4')); | ||
test('subarray() begin', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray(2).toString().should.equal('3,4')); | ||
test('subarray() begin+end', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray(2, 3).toString().should.equal('3')); | ||
test('subarray() begin+end 0', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray(0, 0).toString().should.equal('')); | ||
test('subarray() -ve begin', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray(-2).toString().should.equal('3,4')); | ||
test('subarray() -ve end', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray(1, -1).toString().should.equal('2,3')); | ||
test('subarray() outside range', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray(-99, 99).toString().should.equal('1,2,3,4')); | ||
test('subarray() non-numeric begin', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray('string').toString().should.equal('1,2,3,4')); | ||
test('subarray() non-numeric end', () => new UintNArray(4, [ 1, 2, 3, 4 ]).subarray(0, 'string').toString().should.equal('')); | ||
const uiN3 = new UintNArray(4, [ 1, 2, 3, 4 ]); | ||
uiN3[-1] = 9; | ||
uiN3[99] = 9; | ||
test('ignore out-of-bounds set', () => uiN3.toString().should.equal('1,2,3,4')); | ||
}); | ||
|
||
describe('inherited properties (a few)', function() { | ||
test('name', () => UintNArray.name.should.equal('UintNArray')); | ||
test('length', () => new UintNArray(4, 5).length.should.equal(5)); | ||
test('instanceof Array', () => (new UintNArray(1) instanceof Array).should.be.true); | ||
test('instanceof UintNArray', () => (new UintNArray(1) instanceof UintNArray).should.be.true); | ||
const uiN = new UintNArray(4); | ||
uiN.myProperty = 99; | ||
test('set arbitrary property', () => uiN.myProperty.should.equal(99)); | ||
}); | ||
|
||
describe('inherited methods (a few)', function() { | ||
const ui8 = new Uint8Array([ 1, 2, 3, 4, 255, 254, 253, 252 ]); | ||
test('toString', () => new UintNArray(4, ui8.buffer).toString().should.equal('0,1,0,2,0,3,0,4,15,15,15,14,15,13,15,12')); | ||
test('includes ✓', () => new UintNArray(4, ui8.buffer).includes(4).should.equal(true)); | ||
test('includes ✗', () => new UintNArray(4, ui8.buffer).includes(5).should.equal(false)); | ||
test('fill', () => new UintNArray(4, 5).fill(12).toString().should.equal('12,12,12,12,12')); | ||
}); | ||
|
||
describe('array bracket access', function() { | ||
test('[0]', () => new UintNArray(4, [ 1, 2 ])[0].should.equal(1)); | ||
test('[1]', () => new UintNArray(4, [ 1, 2 ])[1].should.equal(2)); | ||
test('beyond', () => should.equal(new UintNArray(4, [ 1, 2 ])[2]), undefined); | ||
test('before', () => should.equal(new UintNArray(4, [ 1, 2 ])[-1]), undefined); | ||
}); | ||
|
||
describe('overridden properties', function() { | ||
test('from', () => (typeof UintNArray.from([])).should.equal('undefined')); | ||
test('of', () => (typeof UintNArray.of(1)).should.equal('undefined')); | ||
}); | ||
|
||
describe('irregular word boundaries', function() { | ||
test('3×3', () => new UintNArray(3, [ 1, 2, 3 ]).toString().should.equal('1,2,3')); | ||
test('3×3 l', () => new UintNArray(3, [ 1, 2, 3 ]).buffer.byteLength.should.equal(2)); | ||
test('15×3', () => new UintNArray(15, [ 1, 2, 3 ]).toString().should.equal('1,2,3')); | ||
test('15×3 l', () => new UintNArray(15, [ 1, 2, 3 ]).buffer.byteLength.should.equal(6)); | ||
test('17×3', () => new UintNArray(17, [ 1, 2, 3 ]).toString().should.equal('1,2,3')); | ||
test('17×3 l', () => new UintNArray(17, [ 1, 2, 3 ]).buffer.byteLength.should.equal(7)); | ||
}); | ||
|
||
describe('truncate oversized iterable inputs', function() { | ||
test('2-bit array', () => new UintNArray(2, [ 1, 2, 3, 4, 5, 6 ]).toString().should.equal('1,2,3,0,1,2')); | ||
test('3-bit array', () => new UintNArray(3, [ 5, 6, 7, 8, 9, 10 ]).toString().should.equal('5,6,7,0,1,2')); | ||
test('4-bit array', () => new UintNArray(4, [ 13, 14, 15, 16, 17, 18 ]).toString().should.equal('13,14,15,0,1,2')); | ||
test('15-bit array', () => new UintNArray(15, [ 0x7ffd, 0x7ffe, 0x7fff, 0x8000, 0x8001, 0x8002 ]).toString().should.equal('32765,32766,32767,0,1,2')); | ||
}); | ||
|
||
describe('constructor errors', function() { | ||
test('0-width', () => should.Throw(function() { new UintNArray(0); }, RangeError)); | ||
test('+-width', () => should.Throw(function() { new UintNArray(33); }, RangeError)); | ||
test('no new', () => should.Throw(function() { UintNArray(4, 5); }, TypeError)); | ||
test('-ve length', () => should.Throw(function() { new UintNArray(1, -1); }, RangeError)); | ||
const ui8 = new Uint8Array([ 1, 2, 3, 4, 255, 254, 253, 252 ]); | ||
test('-ve b-offset', () => should.Throw(function() { new UintNArray(4, ui8.buffer, -1); }, RangeError)); | ||
test('excess b-offset', () => should.Throw(function() { new UintNArray(4, ui8.buffer, 65); }, RangeError)); | ||
test('-ve b-length', () => should.Throw(function() { new UintNArray(4, ui8.buffer, 0, -1); }, RangeError)); | ||
test('excess b-length', () => should.Throw(function() { new UintNArray(4, ui8.buffer, 0, 17); }, RangeError)); | ||
}); | ||
|
||
describe('method errors', function() { | ||
test('set() un-iterable', () => should.Throw(function() { new UintNArray(4).set(1, 1); }, TypeError)); | ||
test('set() non-numeric offset', () => should.Throw(function() { new UintNArray(4).set([ 1 ], 'string'); }, RangeError)); | ||
test('set() -ve offset', () => should.Throw(function() { new UintNArray(4).set([ 1, 2, 3 ], -1); }, RangeError)); | ||
test('set() excess offset', () => should.Throw(function() { new UintNArray(4).set([ 1, 2, 3 ], 2); }, RangeError)); | ||
}); |
Oops, something went wrong.