Skip to content

Instantly share code, notes, and snippets.

@rreusser
Last active June 28, 2018 00:46
Show Gist options
  • Save rreusser/3d652e6905c1b1caee801517b4db09a8 to your computer and use it in GitHub Desktop.
Save rreusser/3d652e6905c1b1caee801517b4db09a8 to your computer and use it in GitHub Desktop.

dynamic-typedarray

Introduction

This module wraps typed arrays with a minimal abstraction so that you can resize and append to them without reallocating and transferring data yourself.

Example

When initializing, you may pass an Array, a typed array, a size, or a dtype string. (Adding a dtype string will cast the data if necessary.) The following are equivalent ways to initialize a double precision dynamic typed array.

var DynamicTypedarray = require('dynamic-typedarray');

var arr;
arr = new DynamicTypedarray(3);
arr = new DynamicTypedarray(3, 'float64');
arr = new DynamicTypedarray([0, 0, 0]);
arr = new DynamicTypedarray(new Float64Array(3));
arr = new DynamicTypedarray(new Float64Array(3), 'float64');
arr = new DynamicTypedarray(new Uint32Array(3), 'float64');

arr.view
// => Float64Array([0, 0, 0])

arr.buffer
// => Float64Array([0, 0, 0, 0])

arr.length
// => 3

You may push elements:

var arr = new DynamicTypedarrary([1, 2, 3]);
arr.push(4, 5, 6);

arr.view
// => Float64Array([1, 2, 3, 4, 5, 6])

arr.buffer
// => Float64Array([1, 2, 3, 4, 5, 6, 0, 0])

arr.length
// => 6

You can set the length directly in order to allocate space or truncate.

var arr = new DynamicTypedarrary([1, 2, 3]);
arr.length = 100;

arr.view.length
// => 100

arr.buffer.length
// => 128

If I were really trying to use this module and minimize overhead to append to a typed array, I'd do this:

// Instantiate from some data source
var array = new DynamicTypedArray(Int32Array([...]));
var initialLength = array.length;

// Make space for fifty extra entries
array.length += 50;

// Query the view once
var arrayView = array.view;

// Set the next fifty entries directly
for (var i = 0, pointer = initialLength; i < 50; i++, pointer++) {
  arrayView[pointer] = myFunction(i);
}

API

Constructor

require('dynamic-typedarray')([dataOrLength[, dtype]])

Constructor for a new dynamic typed array instance. dataOrLength may either be an Array or typed array of data or an whole number length. If provided, dtype is a string indicating the data type. The list of available dtype strings may be found here.

Properties

.length

Readable and writable. If it exceeds the current buffer length, will allocate storage according to the next power of two.

.view

Read-only. Creates and caches a subarray of the buffer matching the current length of the dynamic array. The subarray will be cached and recreated as needed so that you must not retain this reference if the typed array length may have changed.

Methods

.push(value[, value1[, value2[, ...]]])

Push one or multiple values and return the resulting length of the array. Roughly equivalent to Array.prototype.push.

.pushArray(array)

Push a typed array. and return the resulting length. Similar to Array.prototype.concat, but mutates the current array and does not allocate a new array.

.ensureCapacity(length)

Ensure the buffer has enough space for the requested length. Can be used in advance before appending a known number of values so that you don't need to check the length (or use a method like .push that implicitly checks the length) on each addition.

See also

  • allocated-dynamic-typedarray: Lovely, but it's not for me: "Notice how the order of the items is not guaranteed after a .remove(). ... If you need functionality otherwise, try a different module."
  • dynamic-typed-array: A very nice module, but adds a bit too much, uses typescript, and doesn't quite satisfy my aims.

License

© 2018 Ricky Reusser.

'use strict';
module.exports = DynamicTypedarray;
var dtypeCtor = require('dtype');
var isTypedarray = require('is-typedarray');
var nextPOT = require('next-power-of-two');
function typedarrayDtypeString (typedArray) {
var typeString = Object.prototype.toString.call(typedArray);
switch (typeString) {
case '[object Float64Array]':
return 'float64';
case '[object Float32Array]':
return 'float32';
case '[object Int8Array]':
return 'int8';
case '[object Int16Array]':
return 'int16';
case '[object Int32Array]':
return 'int32';
case '[object Uint8Array]':
return 'uint8';
case '[object Uint16Array]':
return 'uint16';
case '[object Uint32Array]':
return 'uint32';
case '[object Uint8ClampedArray]':
return 'uint8_clamped';
}
}
function DynamicTypedarray (viewOrSize, dtype) {
var input;
if (isTypedarray(viewOrSize)) {
input = viewOrSize;
this._length = input.length;
this.dtype = dtype || typedarrayDtypeString(viewOrSize);
} else if (Array.isArray(viewOrSize)) {
input = viewOrSize;
this._length = viewOrSize.length;
this.dtype = dtype || 'float64';
} else if (typeof viewOrSize === 'string' && !dtype) {
this.dtype = viewOrSize;
this._length = 0;
} else if (viewOrSize === +viewOrSize) {
this.dtype = dtype || 'float64';
this._length = +viewOrSize;
} else if (viewOrSize) {
throw new Error('Unexpected view type, "' + typeof (viewOrSize) + '"');
} else {
this.dtype = 'float64';
this._length = 0;
}
var Ctor = this.ctor = dtypeCtor(this.dtype);
if (!this.ctor) {
throw new Error('invalid dtype string, "' + this.dtype + '"');
}
this.buffer = new Ctor(nextPOT(this._length));
this._view = null;
if (input) this.buffer.set(input);
}
DynamicTypedarray.prototype = {
get view () {
if (!this._view) {
this._view = this.buffer.subarray(0, this._length);
}
return this._view;
},
get length () {
return this._length;
},
set length (length) {
var oldBuffer, Ctor;
if (length > this._length) {
if (length > this.buffer.length) {
oldBuffer = this.buffer;
Ctor = this.ctor;
this.buffer = new Ctor(nextPOT(length));
this.buffer.set(oldBuffer);
}
this._length = length;
this._view = null;
} else if (length < this._length) {
if (length < this.buffer.length >>> 1) {
oldBuffer = this.buffer;
Ctor = this.ctor;
this.buffer = new Ctor(nextPOT(length));
this.buffer.set(oldBuffer.subarray(0, length));
}
this._length = length;
this._view = null;
}
return this._length;
},
ensureCapacity: function (length) {
var oldBuffer;
if (length > this.buffer.length) {
oldBuffer = this.buffer;
var Ctor = this.ctor;
this.buffer = new Ctor(nextPOT(length));
this.buffer.set(oldBuffer);
this._view = null;
}
return this.buffer.length;
},
push: function () {
var n = arguments.length;
var length = this._length;
this.length = length + n;
for (var i = 0; i < n; i++) {
this.buffer[length + i] = arguments[i];
}
return this._length;
},
pushArray: function (data) {
var n = data.length;
var length = this._length;
this.length = length + n;
for (var i = 0; i < n; i++) {
this.buffer[length + i] = data[i];
}
return this._length;
}
};
{
"name": "dynamic-typedarray",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "node test.js",
"lint": "semistandard",
"lint-fix": "semistandard --fix"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"defined": "^1.0.0",
"dtype": "^2.0.0",
"is-typedarray": "^1.0.0",
"next-power-of-two": "^1.0.0",
"typedarray-pool": "^1.1.0"
},
"devDependencies": {
"semistandard": "^12.0.1",
"tape": "^4.9.0"
}
}
'use strict';
var test = require('tape');
var DynamicTypedarray = require('./');
test('DynamicTypedarray', function (t) {
t.test('constructing by dtype string', function (t) {
t.equal((new DynamicTypedarray('float64')).dtype, 'float64');
t.equal((new DynamicTypedarray('float32')).dtype, 'float32');
t.equal((new DynamicTypedarray('int32')).dtype, 'int32');
t.equal((new DynamicTypedarray('int16')).dtype, 'int16');
t.equal((new DynamicTypedarray('int8')).dtype, 'int8');
t.equal((new DynamicTypedarray('uint32')).dtype, 'uint32');
t.equal((new DynamicTypedarray('uint16')).dtype, 'uint16');
t.equal((new DynamicTypedarray('uint8')).dtype, 'uint8');
t.equal((new DynamicTypedarray('uint8_clamped')).dtype, 'uint8_clamped');
t.throws(function () {
var x = new DynamicTypedarray('foo');
x.push(1);
}, /invalid dtype string, "foo"/);
t.end();
});
t.test('constructing by view array', function (t) {
t.equal((new DynamicTypedarray(new Float64Array(1))).dtype, 'float64');
t.equal((new DynamicTypedarray(new Float32Array(1))).dtype, 'float32');
t.equal((new DynamicTypedarray(new Int32Array(1))).dtype, 'int32');
t.equal((new DynamicTypedarray(new Int16Array(1))).dtype, 'int16');
t.equal((new DynamicTypedarray(new Int8Array(1))).dtype, 'int8');
t.equal((new DynamicTypedarray(new Uint32Array(1))).dtype, 'uint32');
t.equal((new DynamicTypedarray(new Uint16Array(1))).dtype, 'uint16');
t.equal((new DynamicTypedarray(new Uint8Array(1))).dtype, 'uint8');
t.equal((new DynamicTypedarray(new Uint8ClampedArray(1))).dtype, 'uint8_clamped');
t.equal((new DynamicTypedarray([1])).dtype, 'float64');
t.throws(function () {
var x = new DynamicTypedarray({});
x.push(1);
}, /Unexpected view type, "object"/);
t.end();
});
t.test('constructing by size', function (t) {
t.equal((new DynamicTypedarray(7)).dtype, 'float64');
t.equal((new DynamicTypedarray(7, 'float32')).dtype, 'float32');
t.equal((new DynamicTypedarray(7)).view.length, 7);
t.end();
});
t.test('constructing by view and dtype string', function (t) {
t.equal((new DynamicTypedarray([1], 'float64')).dtype, 'float64');
t.equal((new DynamicTypedarray([1], 'float32')).dtype, 'float32');
t.equal((new DynamicTypedarray([1], 'int32')).dtype, 'int32');
t.equal((new DynamicTypedarray([1], 'int16')).dtype, 'int16');
t.equal((new DynamicTypedarray([1], 'int8')).dtype, 'int8');
t.equal((new DynamicTypedarray([1], 'uint32')).dtype, 'uint32');
t.equal((new DynamicTypedarray([1], 'uint16')).dtype, 'uint16');
t.equal((new DynamicTypedarray([1], 'uint8')).dtype, 'uint8');
t.equal((new DynamicTypedarray([1], 'uint8_clamped')).dtype, 'uint8_clamped');
t.end();
});
t.test('initialization with Array view', function (t) {
var x = new DynamicTypedarray([1, 2, 3]);
t.equal(x.view[0], 1);
t.equal(x.view[1], 2);
t.equal(x.view[2], 3);
t.end();
});
t.test('initialization with typed array view', function (t) {
var x = new DynamicTypedarray(new Float32Array([1.5, 2.5, 3.5]));
t.equal(x.view[0], 1.5);
t.equal(x.view[1], 2.5);
t.equal(x.view[2], 3.5);
t.end();
});
t.test('casts typed array input to type', function (t) {
var input = new Float32Array([1.5, 2.5, 3.5]);
var x = new DynamicTypedarray(input, 'uint8');
t.equal(x.view[0], 1);
t.equal(x.view[1], 2);
t.equal(x.view[2], 3);
x.view[0] = 5;
x.view[1] = 6;
x.view[2] = 7;
t.deepEqual(input, new Float32Array([1.5, 2.5, 3.5]));
t.end();
});
t.test('casts array input to type', function (t) {
var input = [1.5, 2.5, 3.5];
var x = new DynamicTypedarray(input, 'uint8');
t.equal(x.view[0], 1);
t.equal(x.view[1], 2);
t.equal(x.view[2], 3);
t.deepEqual(input, [1.5, 2.5, 3.5]);
t.end();
});
t.test('.setLength() (expansion)', function (t) {
var x = new DynamicTypedarray([0, 1, 2, 3, 4]);
t.equal(x.buffer.length, 8);
t.equal(x.view.length, 5);
x.length = 7;
t.equal(x.buffer.length, 8);
t.equal(x.view.length, 7);
t.deepEqual(x.view, new Float32Array([0, 1, 2, 3, 4, 0, 0]));
x.length = 15;
t.equal(x.buffer.length, 16);
t.equal(x.view.length, 15);
t.deepEqual(x.view, new Float32Array([0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
t.end();
});
t.test('++, --, += for the length', function (t) {
var x = new DynamicTypedarray(4);
t.equal(x.view.length, 4);
t.equal(x.buffer.length, 4);
x.length++;
t.equal(x.view.length, 5);
t.equal(x.buffer.length, 8);
x.length--;
t.equal(x.view.length, 4);
t.equal(x.buffer.length, 8);
x.length += 8;
t.equal(x.view.length, 12);
t.equal(x.buffer.length, 16);
t.end();
});
t.test('.setLength() (truncation)', function (t) {
var x = new DynamicTypedarray([0, 1, 2, 3, 4, 5]);
t.equal(x.buffer.length, 8);
t.equal(x.view.length, 6);
x.length = 5;
t.equal(x.buffer.length, 8);
t.equal(x.length, 5);
t.equal(x.view.length, 5);
t.deepEqual(x.view, new Float32Array([0, 1, 2, 3, 4]));
x.length = 3;
t.equal(x.buffer.length, 4);
t.equal(x.view.length, 3);
t.deepEqual(x.view, new Float32Array([0, 1, 2]));
t.end();
});
t.test('.push()', function (t) {
var x = new DynamicTypedarray();
t.equal(x.view.length, 0);
t.equal(x.buffer.length, 1);
var returnValue = x.push(5);
t.equal(returnValue, 1);
t.equal(x.buffer.length, 1);
t.deepEqual(x.view, new Float64Array([5]));
returnValue = x.push(1, 2, 3);
t.equal(returnValue, 4);
t.equal(x.buffer.length, 4);
t.deepEqual(x.view, new Float64Array([5, 1, 2, 3]));
returnValue = x.push(5, 4);
t.equal(returnValue, 6);
t.equal(x.buffer.length, 8);
t.deepEqual(x.view, new Float64Array([5, 1, 2, 3, 5, 4]));
returnValue = x.push(6);
t.equal(returnValue, 7);
t.equal(x.buffer.length, 8);
t.deepEqual(x.view, new Float64Array([5, 1, 2, 3, 5, 4, 6]));
t.end();
});
t.test('.pushArray()', function (t) {
var x = new DynamicTypedarray();
t.equal(x.view.length, 0);
t.equal(x.buffer.length, 1);
var returnValue = x.pushArray([5]);
t.equal(returnValue, 1);
t.equal(x.buffer.length, 1);
t.deepEqual(x.view, new Float64Array([5]));
returnValue = x.pushArray([1, 2, 3]);
t.equal(returnValue, 4);
t.equal(x.buffer.length, 4);
t.deepEqual(x.view, new Float64Array([5, 1, 2, 3]));
returnValue = x.pushArray([5, 4]);
t.equal(returnValue, 6);
t.equal(x.buffer.length, 8);
t.deepEqual(x.view, new Float64Array([5, 1, 2, 3, 5, 4]));
returnValue = x.pushArray([6]);
t.equal(returnValue, 7);
t.equal(x.buffer.length, 8);
t.deepEqual(x.view, new Float64Array([5, 1, 2, 3, 5, 4, 6]));
t.end();
});
t.test('.ensureCapacity()', function (t) {
var x = new DynamicTypedarray([1, 2, 3]);
t.equal(x.view.length, 3);
t.equal(x.buffer.length, 4);
x.ensureCapacity(7);
t.equal(x.view.length, 3);
t.equal(x.buffer.length, 8);
t.deepEqual(x.view, new Float64Array([1, 2, 3]));
t.end();
});
t.end();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment