├── .travis.yml ├── .gitignore ├── LICENSE ├── package.json ├── README.md ├── test └── test.js └── pool.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.8" 4 | - "0.10" 5 | before_install: 6 | - npm install -g npm@~1.4.6 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2013 Mikola Lysenko 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typedarray-pool", 3 | "version": "1.2.0", 4 | "description": "Reuse typed arrays", 5 | "main": "pool.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": { 10 | "bit-twiddle": "^1.0.0", 11 | "dup": "^1.0.0" 12 | }, 13 | "devDependencies": { 14 | "tape": "^2.12.3" 15 | }, 16 | "scripts": { 17 | "test": "tape test/*.js" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/mikolalysenko/typedarray-pool.git" 22 | }, 23 | "keywords": [ 24 | "typed", 25 | "array", 26 | "cache", 27 | "pool", 28 | "memory", 29 | "malloc", 30 | "free", 31 | "reuse", 32 | "optimize", 33 | "construct", 34 | "overhead" 35 | ], 36 | "author": "Mikola Lysenko", 37 | "license": "MIT", 38 | "readmeFilename": "README.md", 39 | "gitHead": "d7a8a448caf51042e6c371b11074aab6ebaf53ad", 40 | "testling": { 41 | "files": "test/*.js", 42 | "browsers": [ 43 | "ie/10..latest", 44 | "firefox/17..latest", 45 | "firefox/nightly", 46 | "chrome/22..latest", 47 | "chrome/canary", 48 | "opera/12..latest", 49 | "opera/next", 50 | "safari/6.0..latest", 51 | "ipad/6.0..latest", 52 | "iphone/6.0..latest", 53 | "android-browser/4.2..latest" 54 | ] 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | typedarray-pool 2 | =============== 3 | A global pool for typed arrays. 4 | 5 | [![testling badge](https://ci.testling.com/mikolalysenko/typedarray-pool.png)](https://ci.testling.com/mikolalysenko/typedarray-pool) 6 | 7 | [![build status](https://secure.travis-ci.org/mikolalysenko/typedarray-pool.png)](http://travis-ci.org/mikolalysenko/typedarray-pool) 8 | 9 | # Example 10 | 11 | ```javascript 12 | var pool = require("typedarray-pool") 13 | 14 | //Allocate a buffer with at least 128 floats 15 | var f = pool.malloc(128, "float") 16 | 17 | // ... do stuff ... 18 | 19 | //When done, release buffer 20 | pool.free(f) 21 | ``` 22 | 23 | # Install 24 | 25 | npm install typedarray-pool 26 | 27 | # API 28 | 29 | ```javascript 30 | var pool = require("typedarray-pool") 31 | ``` 32 | 33 | ### `pool.malloc(n[, dtype])` 34 | Allocates a typed array (or ArrayBuffer) with at least n elements. 35 | 36 | * `n` is the number of elements in the array 37 | * `dtype` is the data type of the array to allocate. Must be one of: 38 | 39 | + `"uint8"` 40 | + `"uint16"` 41 | + `"uint32"` 42 | + `"int8"` 43 | + `"int16"` 44 | + `"int32"` 45 | + `"float"` 46 | + `"float32"` 47 | + `"double"` 48 | + `"float64"` 49 | + `"arraybuffer"` 50 | + `"data"` 51 | + `"uint8_clamped"` 52 | + `"bigint64"` 53 | + `"biguint64"` 54 | + `"buffer"` 55 | 56 | **Returns** A typed array with at least `n` elements in it. If `dtype` is undefined, an ArrayBuffer is returned. 57 | 58 | **Note** You can avoid the dispatch by directly calling one of the following methods: 59 | 60 | * `pool.mallocUint8` 61 | * `pool.mallocUint16` 62 | * `pool.mallocUint32` 63 | * `pool.mallocInt8` 64 | * `pool.mallocInt16` 65 | * `pool.mallocInt32` 66 | * `pool.mallocFloat` 67 | * `pool.mallocDouble` 68 | * `pool.mallocArrayBuffer` 69 | * `pool.mallocDataView` 70 | * `pool.mallocUint8Clamped` 71 | * `pool.mallocBigInt64` 72 | * `pool.mallocBigUint64` 73 | * `pool.mallocBuffer` 74 | 75 | ### `pool.free(array)` 76 | Returns the array back to the pool. 77 | 78 | * `array` The array object to return to the pool. 79 | 80 | **Note** You can speed up the method if you know the type of array before hand by calling one of the following: 81 | 82 | * `pool.freeUint8` 83 | * `pool.freeUint16` 84 | * `pool.freeUint32` 85 | * `pool.freeInt8` 86 | * `pool.freeInt16` 87 | * `pool.freeInt32` 88 | * `pool.freeFloat` 89 | * `pool.freeDouble` 90 | * `pool.freeArrayBuffer` 91 | * `pool.freeDataView` 92 | * `pool.freeUint8Clamped` 93 | * `pool.freeBigInt64` 94 | * `pool.freeBigUint64` 95 | * `pool.freeBuffer` 96 | 97 | ### `pool.clearCache()` 98 | Removes all references to cached arrays. Use this when you are done with the pool to return all the cached memory to the garbage collector. 99 | 100 | # Credits 101 | (c) 2014 Mikola Lysenko. MIT License 102 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | //Check upgrade works 4 | var dup = require("dup") 5 | global.__TYPEDARRAY_POOL = { 6 | UINT8 : dup([32, 0]) 7 | , UINT16 : dup([32, 0]) 8 | , UINT32 : dup([32, 0]) 9 | , INT8 : dup([32, 0]) 10 | , INT16 : dup([32, 0]) 11 | , INT32 : dup([32, 0]) 12 | , FLOAT : dup([32, 0]) 13 | , DOUBLE : dup([32, 0]) 14 | , DATA : dup([32, 0]) 15 | } 16 | 17 | var pool = require("../pool.js") 18 | 19 | require("tape")("typedarray-pool", function(t) { 20 | 21 | pool.clearCache() 22 | 23 | for(var i=1; i<100; ++i) { 24 | var a 25 | a = pool.malloc(i, "int8") 26 | t.assert(a instanceof Int8Array, "int8array valid") 27 | t.assert(a.length >= i, "int8array length") 28 | pool.free(a) 29 | 30 | a = pool.malloc(i, "int16") 31 | t.assert(a instanceof Int16Array, "int16") 32 | t.assert(a.length >= i) 33 | pool.free(a) 34 | 35 | a = pool.malloc(i, "int32") 36 | t.assert(a instanceof Int32Array, "int32") 37 | t.assert(a.length >= i) 38 | pool.free(a) 39 | 40 | a = pool.malloc(i, "uint8") 41 | t.assert(a instanceof Uint8Array, "uint8") 42 | t.assert(!Buffer.isBuffer(a), "not buffer") 43 | t.assert(a.length >= i) 44 | pool.free(a) 45 | 46 | a = pool.malloc(i, "uint16") 47 | t.assert(a instanceof Uint16Array, "uint16") 48 | t.assert(a.length >= i) 49 | pool.free(a) 50 | 51 | a = pool.malloc(i, "uint32") 52 | t.assert(a instanceof Uint32Array, "uint32") 53 | t.assert(a.length >= i) 54 | pool.free(a) 55 | 56 | a = pool.malloc(i, "float") 57 | t.assert(a instanceof Float32Array, "float") 58 | t.assert(a.length >= i) 59 | pool.free(a) 60 | 61 | a = pool.malloc(i, "double") 62 | t.assert(a instanceof Float64Array, "double") 63 | t.assert(a.length >= i) 64 | pool.free(a) 65 | 66 | a = pool.malloc(i, "uint8_clamped") 67 | if((typeof Uint8ClampedArray) !== "undefined") { 68 | t.assert(a instanceof Uint8ClampedArray, "uint8_clamped") 69 | } else { 70 | t.assert(a instanceof Uint8Array, "unit8_clamped clamped default to uint8") 71 | } 72 | t.assert(a.length >= i) 73 | pool.free(a) 74 | 75 | a = pool.malloc(i, "bigint64") 76 | if((typeof BigInt64Array) !== "undefined") { 77 | t.assert(a instanceof BigInt64Array, "bigint64") 78 | } else { 79 | t.assert(a instanceof BigInt64Array, "bigint64 defaults to null") 80 | } 81 | t.assert(a.length >= i) 82 | pool.free(a) 83 | 84 | a = pool.malloc(i, "biguint64") 85 | if((typeof BigUint64Array) !== "undefined") { 86 | t.assert(a instanceof BigUint64Array, "biguint64") 87 | } else { 88 | t.assert(a instanceof BigUint64Array, "biguint64 defaults to null") 89 | } 90 | t.assert(a.length >= i) 91 | pool.free(a) 92 | 93 | a = pool.malloc(i, "buffer") 94 | t.assert(Buffer.isBuffer(a), "buffer") 95 | t.assert(a.length >= i) 96 | pool.free(a) 97 | 98 | a = pool.malloc(i) 99 | t.assert(a instanceof ArrayBuffer, "array buffer") 100 | t.assert(a.byteLength >= i) 101 | pool.free(a) 102 | 103 | a = pool.malloc(i, "arraybuffer") 104 | t.assert(a instanceof ArrayBuffer, "array buffer") 105 | t.assert(a.byteLength >= i) 106 | pool.free(a) 107 | 108 | a = pool.malloc(i, "dataview") 109 | t.assert(a instanceof DataView, "dataview") 110 | t.assert(a.byteLength >= i) 111 | pool.free(a) 112 | } 113 | 114 | for(var i=1; i<100; ++i) { 115 | var a 116 | a = pool.mallocInt8(i) 117 | t.assert(a instanceof Int8Array, "int8") 118 | t.assert(a.length >= i) 119 | pool.freeInt8(a) 120 | 121 | a = pool.mallocInt16(i) 122 | t.assert(a instanceof Int16Array, "int16") 123 | t.assert(a.length >= i) 124 | pool.freeInt16(a) 125 | 126 | a = pool.mallocInt32(i) 127 | t.assert(a instanceof Int32Array, "int32") 128 | t.assert(a.length >= i) 129 | pool.freeInt32(a) 130 | 131 | a = pool.mallocUint8(i) 132 | t.assert(a instanceof Uint8Array, "uint8") 133 | t.assert(!Buffer.isBuffer(a), "not buffer") 134 | t.assert(a.length >= i) 135 | pool.freeUint8(a) 136 | 137 | a = pool.mallocUint16(i) 138 | t.assert(a instanceof Uint16Array, "uint16") 139 | t.assert(a.length >= i) 140 | pool.freeUint16(a) 141 | 142 | a = pool.mallocUint32(i) 143 | t.assert(a instanceof Uint32Array, "uint32") 144 | t.assert(a.length >= i) 145 | pool.freeUint32(a) 146 | 147 | a = pool.mallocFloat(i) 148 | t.assert(a instanceof Float32Array, "float32") 149 | t.assert(a.length >= i) 150 | pool.freeFloat(a) 151 | 152 | a = pool.mallocDouble(i) 153 | t.assert(a instanceof Float64Array, "float64") 154 | t.assert(a.length >= i) 155 | pool.freeDouble(a) 156 | 157 | a = pool.mallocUint8Clamped(i) 158 | if((typeof Uint8ClampedArray) !== "undefined") { 159 | t.assert(a instanceof Uint8ClampedArray, "uint8 clamped") 160 | } else { 161 | t.assert(a instanceof Uint8Array, "uint8 clamped defaults to unt8") 162 | } 163 | t.assert(a.length >= i) 164 | pool.freeUint8Clamped(a) 165 | 166 | a = pool.mallocBigInt64(i) 167 | if((typeof BigInt64Array) !== "undefined") { 168 | t.assert(a instanceof BigInt64Array, "bigint64") 169 | } else { 170 | t.equal(a, null, "bigint64 defaults null") 171 | } 172 | t.assert(a.length >= i) 173 | pool.freeBigInt64(a) 174 | 175 | a = pool.mallocBigUint64(i) 176 | if((typeof BigUint64Array) !== "undefined") { 177 | t.assert(a instanceof BigUint64Array, "biguint64") 178 | } else { 179 | t.equal(a, null, "bigint64 defaults null") 180 | } 181 | t.assert(a.length >= i) 182 | pool.freeBigUint64(a) 183 | 184 | a = pool.mallocBuffer(i) 185 | t.assert(Buffer.isBuffer(a), "buffer") 186 | t.assert(a.length >= i) 187 | pool.freeBuffer(a) 188 | 189 | a = pool.mallocArrayBuffer(i) 190 | t.assert(a instanceof ArrayBuffer, "array buffer") 191 | t.assert(a.byteLength >= i) 192 | pool.freeArrayBuffer(a) 193 | 194 | a = pool.mallocDataView(i) 195 | t.assert(a instanceof DataView, "data view") 196 | t.assert(a.byteLength >= i) 197 | pool.freeDataView(a) 198 | } 199 | 200 | pool.clearCache() 201 | 202 | t.end() 203 | }) 204 | -------------------------------------------------------------------------------- /pool.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var bits = require('bit-twiddle') 4 | var dup = require('dup') 5 | var Buffer = require('buffer').Buffer 6 | 7 | //Legacy pool support 8 | if(!global.__TYPEDARRAY_POOL) { 9 | global.__TYPEDARRAY_POOL = { 10 | UINT8 : dup([32, 0]) 11 | , UINT16 : dup([32, 0]) 12 | , UINT32 : dup([32, 0]) 13 | , BIGUINT64 : dup([32, 0]) 14 | , INT8 : dup([32, 0]) 15 | , INT16 : dup([32, 0]) 16 | , INT32 : dup([32, 0]) 17 | , BIGINT64 : dup([32, 0]) 18 | , FLOAT : dup([32, 0]) 19 | , DOUBLE : dup([32, 0]) 20 | , DATA : dup([32, 0]) 21 | , UINT8C : dup([32, 0]) 22 | , BUFFER : dup([32, 0]) 23 | } 24 | } 25 | 26 | var hasUint8C = (typeof Uint8ClampedArray) !== 'undefined' 27 | var hasBigUint64 = (typeof BigUint64Array) !== 'undefined' 28 | var hasBigInt64 = (typeof BigInt64Array) !== 'undefined' 29 | var POOL = global.__TYPEDARRAY_POOL 30 | 31 | //Upgrade pool 32 | if(!POOL.UINT8C) { 33 | POOL.UINT8C = dup([32, 0]) 34 | } 35 | if(!POOL.BIGUINT64) { 36 | POOL.BIGUINT64 = dup([32, 0]) 37 | } 38 | if(!POOL.BIGINT64) { 39 | POOL.BIGINT64 = dup([32, 0]) 40 | } 41 | if(!POOL.BUFFER) { 42 | POOL.BUFFER = dup([32, 0]) 43 | } 44 | 45 | //New technique: Only allocate from ArrayBufferView and Buffer 46 | var DATA = POOL.DATA 47 | , BUFFER = POOL.BUFFER 48 | 49 | exports.free = function free(array) { 50 | if(Buffer.isBuffer(array)) { 51 | BUFFER[bits.log2(array.length)].push(array) 52 | } else { 53 | if(Object.prototype.toString.call(array) !== '[object ArrayBuffer]') { 54 | array = array.buffer 55 | } 56 | if(!array) { 57 | return 58 | } 59 | var n = array.length || array.byteLength 60 | var log_n = bits.log2(n)|0 61 | DATA[log_n].push(array) 62 | } 63 | } 64 | 65 | function freeArrayBuffer(buffer) { 66 | if(!buffer) { 67 | return 68 | } 69 | var n = buffer.length || buffer.byteLength 70 | var log_n = bits.log2(n) 71 | DATA[log_n].push(buffer) 72 | } 73 | 74 | function freeTypedArray(array) { 75 | freeArrayBuffer(array.buffer) 76 | } 77 | 78 | exports.freeUint8 = 79 | exports.freeUint16 = 80 | exports.freeUint32 = 81 | exports.freeBigUint64 = 82 | exports.freeInt8 = 83 | exports.freeInt16 = 84 | exports.freeInt32 = 85 | exports.freeBigInt64 = 86 | exports.freeFloat32 = 87 | exports.freeFloat = 88 | exports.freeFloat64 = 89 | exports.freeDouble = 90 | exports.freeUint8Clamped = 91 | exports.freeDataView = freeTypedArray 92 | 93 | exports.freeArrayBuffer = freeArrayBuffer 94 | 95 | exports.freeBuffer = function freeBuffer(array) { 96 | BUFFER[bits.log2(array.length)].push(array) 97 | } 98 | 99 | exports.malloc = function malloc(n, dtype) { 100 | if(dtype === undefined || dtype === 'arraybuffer') { 101 | return mallocArrayBuffer(n) 102 | } else { 103 | switch(dtype) { 104 | case 'uint8': 105 | return mallocUint8(n) 106 | case 'uint16': 107 | return mallocUint16(n) 108 | case 'uint32': 109 | return mallocUint32(n) 110 | case 'int8': 111 | return mallocInt8(n) 112 | case 'int16': 113 | return mallocInt16(n) 114 | case 'int32': 115 | return mallocInt32(n) 116 | case 'float': 117 | case 'float32': 118 | return mallocFloat(n) 119 | case 'double': 120 | case 'float64': 121 | return mallocDouble(n) 122 | case 'uint8_clamped': 123 | return mallocUint8Clamped(n) 124 | case 'bigint64': 125 | return mallocBigInt64(n) 126 | case 'biguint64': 127 | return mallocBigUint64(n) 128 | case 'buffer': 129 | return mallocBuffer(n) 130 | case 'data': 131 | case 'dataview': 132 | return mallocDataView(n) 133 | 134 | default: 135 | return null 136 | } 137 | } 138 | return null 139 | } 140 | 141 | function mallocArrayBuffer(n) { 142 | var n = bits.nextPow2(n) 143 | var log_n = bits.log2(n) 144 | var d = DATA[log_n] 145 | if(d.length > 0) { 146 | return d.pop() 147 | } 148 | return new ArrayBuffer(n) 149 | } 150 | exports.mallocArrayBuffer = mallocArrayBuffer 151 | 152 | function mallocUint8(n) { 153 | return new Uint8Array(mallocArrayBuffer(n), 0, n) 154 | } 155 | exports.mallocUint8 = mallocUint8 156 | 157 | function mallocUint16(n) { 158 | return new Uint16Array(mallocArrayBuffer(2*n), 0, n) 159 | } 160 | exports.mallocUint16 = mallocUint16 161 | 162 | function mallocUint32(n) { 163 | return new Uint32Array(mallocArrayBuffer(4*n), 0, n) 164 | } 165 | exports.mallocUint32 = mallocUint32 166 | 167 | function mallocInt8(n) { 168 | return new Int8Array(mallocArrayBuffer(n), 0, n) 169 | } 170 | exports.mallocInt8 = mallocInt8 171 | 172 | function mallocInt16(n) { 173 | return new Int16Array(mallocArrayBuffer(2*n), 0, n) 174 | } 175 | exports.mallocInt16 = mallocInt16 176 | 177 | function mallocInt32(n) { 178 | return new Int32Array(mallocArrayBuffer(4*n), 0, n) 179 | } 180 | exports.mallocInt32 = mallocInt32 181 | 182 | function mallocFloat(n) { 183 | return new Float32Array(mallocArrayBuffer(4*n), 0, n) 184 | } 185 | exports.mallocFloat32 = exports.mallocFloat = mallocFloat 186 | 187 | function mallocDouble(n) { 188 | return new Float64Array(mallocArrayBuffer(8*n), 0, n) 189 | } 190 | exports.mallocFloat64 = exports.mallocDouble = mallocDouble 191 | 192 | function mallocUint8Clamped(n) { 193 | if(hasUint8C) { 194 | return new Uint8ClampedArray(mallocArrayBuffer(n), 0, n) 195 | } else { 196 | return mallocUint8(n) 197 | } 198 | } 199 | exports.mallocUint8Clamped = mallocUint8Clamped 200 | 201 | function mallocBigUint64(n) { 202 | if(hasBigUint64) { 203 | return new BigUint64Array(mallocArrayBuffer(8*n), 0, n) 204 | } else { 205 | return null; 206 | } 207 | } 208 | exports.mallocBigUint64 = mallocBigUint64 209 | 210 | function mallocBigInt64(n) { 211 | if (hasBigInt64) { 212 | return new BigInt64Array(mallocArrayBuffer(8*n), 0, n) 213 | } else { 214 | return null; 215 | } 216 | } 217 | exports.mallocBigInt64 = mallocBigInt64 218 | 219 | function mallocDataView(n) { 220 | return new DataView(mallocArrayBuffer(n), 0, n) 221 | } 222 | exports.mallocDataView = mallocDataView 223 | 224 | function mallocBuffer(n) { 225 | n = bits.nextPow2(n) 226 | var log_n = bits.log2(n) 227 | var cache = BUFFER[log_n] 228 | if(cache.length > 0) { 229 | return cache.pop() 230 | } 231 | return new Buffer(n) 232 | } 233 | exports.mallocBuffer = mallocBuffer 234 | 235 | exports.clearCache = function clearCache() { 236 | for(var i=0; i<32; ++i) { 237 | POOL.UINT8[i].length = 0 238 | POOL.UINT16[i].length = 0 239 | POOL.UINT32[i].length = 0 240 | POOL.INT8[i].length = 0 241 | POOL.INT16[i].length = 0 242 | POOL.INT32[i].length = 0 243 | POOL.FLOAT[i].length = 0 244 | POOL.DOUBLE[i].length = 0 245 | POOL.BIGUINT64[i].length = 0 246 | POOL.BIGINT64[i].length = 0 247 | POOL.UINT8C[i].length = 0 248 | DATA[i].length = 0 249 | BUFFER[i].length = 0 250 | } 251 | } 252 | --------------------------------------------------------------------------------