-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Add support for encoding TypedArrays as primitive objects for serialization #2911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
636e644
5030d3a
dfd44d5
ec21714
8de7b5a
5107c97
5cdb828
1ec957a
a7c3814
2b9bda9
669e8be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -19,7 +19,10 @@ var nestedProperty = require('./nested_property'); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var counterRegex = require('./regex').counter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var DESELECTDIM = require('../constants/interactions').DESELECTDIM; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var wrap180 = require('./angles').wrap180; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var isArrayOrTypedArray = require('./is_array').isArrayOrTypedArray; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var isArray = require('./is_array'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var isArrayOrTypedArray = isArray.isArrayOrTypedArray; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var isPrimitiveTypedArrayRepr = isArray.isPrimitiveTypedArrayRepr; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var b64 = require('base64-arraybuffer'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
exports.valObjectMeta = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
data_array: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -33,8 +36,18 @@ exports.valObjectMeta = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
otherOpts: ['dflt'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coerceFunction: function(v, propOut, dflt) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// TODO maybe `v: {type: 'float32', vals: [/* ... */]}` also | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(isArrayOrTypedArray(v)) propOut.set(v); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else if(dflt !== undefined) propOut.set(dflt); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(isArrayOrTypedArray(v)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
propOut.set(v); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if(isPrimitiveTypedArrayRepr(v)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally, the plotly.js/src/plots/polar/set_convert.js Lines 103 to 136 in 24a0f91
Depending on how slow this conversion can be, moving it to the calc step will help ensure faster interactions (note that the This might be a fairly big job though, you'll have to replace all the So I guess, we should first benchmark There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @etpinard Maybe I'm misunderstanding something. As it is now, I thought these conversions would be happening in the Either way, I'll get some performance numbers for |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var coercedV = primitiveTypedArrayReprToTypedArray(v); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(coercedV === undefined && dflt !== undefined) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
propOut.set(dflt); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
propOut.set(coercedV); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if(dflt !== undefined) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
propOut.set(dflt); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
enumerated: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -392,6 +405,10 @@ exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(opts.arrayOk && isArrayOrTypedArray(v)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
propOut.set(v); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return v; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if(opts.arrayOk && isPrimitiveTypedArrayRepr(v)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var coercedV = primitiveTypedArrayReprToTypedArray(v); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
propOut.set(coercedV); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return coercedV; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var coerceFunction = exports.valObjectMeta[opts.valType].coerceFunction; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -521,3 +538,49 @@ function validate(value, opts) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return out !== failed; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
exports.validate = validate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var dtypeStringToTypedarrayType = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that all of them, except for https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays#Typed_array_views Might as well add it here for completeness. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added in 5030d3a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int8: Int8Array, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int16: Int16Array, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int32: Int32Array, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
uint8: Uint8Array, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
uint8_clamped: Uint8ClampedArray, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
uint16: Uint16Array, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
uint32: Uint32Array, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
float32: Float32Array, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
float64: Float64Array | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Convert a primitive TypedArray representation object into a TypedArray | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @param {object} v: Object with `dtype` and `data` properties that | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* represens a TypedArray. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* @returns {TypedArray} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
function primitiveTypedArrayReprToTypedArray(v) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm curious. Have you benchmarked this routine here for bas64 string corresponding to 1e4, 1e5, and 1e6 pts? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// v has dtype and data properties | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Get TypedArray constructor type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var TypeArrayType = dtypeStringToTypedarrayType[v.dtype]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Process data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var coercedV; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var value = v.value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if(value instanceof ArrayBuffer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// value is an ArrayBuffer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coercedV = new TypeArrayType(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if(value.constructor === DataView) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// value has a buffer property, where the buffer is an ArrayBuffer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coercedV = new TypeArrayType(value.buffer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if(Array.isArray(value)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// value is a primitive array | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coercedV = new TypeArrayType(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else if(typeof value === 'string' || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value instanceof String) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// value is a base64 encoded string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var buffer = b64.decode(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coercedV = new TypeArrayType(buffer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return coercedV; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
var Lib = require('../lib'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You'll need to require lib/index -> lib/is_plain_object -> lib/index -> lib/is_array -> lib/index -> lib/is_plain_object |
||
|
||
'use strict'; | ||
|
||
|
@@ -38,8 +39,14 @@ function isArray1D(a) { | |
return !isArrayOrTypedArray(a[0]); | ||
} | ||
|
||
function isPrimitiveTypedArrayRepr(a) { | ||
return (Lib.isPlainObject(a) && | ||
a.hasOwnProperty('dtype') && a.hasOwnProperty('value')); | ||
} | ||
|
||
module.exports = { | ||
isTypedArray: isTypedArray, | ||
isArrayOrTypedArray: isArrayOrTypedArray, | ||
isArray1D: isArray1D | ||
isArray1D: isArray1D, | ||
isPrimitiveTypedArrayRepr: isPrimitiveTypedArrayRepr | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"data": [{ | ||
"type": "scatter", | ||
"x": {"dtype": "float64", "value": [3, 2, 1]}, | ||
"y": {"dtype": "float32", "value": "AABAQAAAAEAAAIA/"}, | ||
"marker": { | ||
"color": { | ||
"dtype": "uint16", | ||
"value": "AwACAAEA", | ||
}, | ||
} | ||
}] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
var Lib = require('@src/lib'); | ||
var supplyDefaults = require('../assets/supply_defaults'); | ||
var b64 = require('base64-arraybuffer'); | ||
var mock1 = require('@mocks/typed_array_repr_scatter.json'); | ||
|
||
var typedArraySpecs = [ | ||
['int8', new Int8Array([-128, -34, 1, 127])], | ||
['uint8', new Uint8Array([0, 1, 127, 255])], | ||
['uint8_clamped', new Uint8ClampedArray([0, 1, 127, 255])], | ||
['int16', new Int16Array([-32768, -123, 345, 32767])], | ||
['uint16', new Uint16Array([0, 345, 32767, 65535])], | ||
['int32', new Int32Array([-2147483648, -123, 345, 32767, 2147483647])], | ||
['uint32', new Uint32Array([0, 345, 32767, 4294967295])], | ||
['float32', new Float32Array([1.2E-38, -2345.25, 2.7182818, 3.1415926, 2, 3.4E38])], | ||
['float64', new Float64Array([5.0E-324, 2.718281828459045, 3.141592653589793, 1.8E308])] | ||
]; | ||
|
||
describe('Test TypedArray representations', function() { | ||
'use strict'; | ||
|
||
describe('ArrayBuffer', function() { | ||
it('should accept representation as ArrayBuffer', function() { | ||
typedArraySpecs.forEach(function(arraySpec) { | ||
// Build value and confirm its type | ||
var value = arraySpec[1].buffer; | ||
expect(value.constructor).toEqual(ArrayBuffer); | ||
|
||
var repr = { | ||
dtype: arraySpec[0], | ||
value: value | ||
}; | ||
var gd = { | ||
data: [{ | ||
y: repr | ||
}], | ||
}; | ||
|
||
supplyDefaults(gd); | ||
|
||
expect(gd.data[0].y).toEqual(repr); | ||
expect(gd._fullData[0].y).toEqual(arraySpec[1]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('Array', function() { | ||
it('should accept representation as Array', function() { | ||
typedArraySpecs.forEach(function(arraySpec) { | ||
// Build value and confirm its type | ||
var value = Array.prototype.slice.call(arraySpec[1]); | ||
expect(Array.isArray(value)).toEqual(true); | ||
|
||
var repr = { | ||
dtype: arraySpec[0], | ||
value: value | ||
}; | ||
var gd = { | ||
data: [{ | ||
y: repr | ||
}], | ||
}; | ||
|
||
supplyDefaults(gd); | ||
|
||
expect(gd.data[0].y).toEqual(repr); | ||
expect(gd._fullData[0].y).toEqual(arraySpec[1]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('DataView', function() { | ||
it('should accept representation as DataView', function() { | ||
typedArraySpecs.forEach(function(arraySpec) { | ||
// Build value and confirm its type | ||
var value = new DataView(arraySpec[1].buffer); | ||
expect(value.constructor).toEqual(DataView); | ||
|
||
var repr = { | ||
dtype: arraySpec[0], | ||
value: value | ||
}; | ||
var gd = { | ||
data: [{ | ||
y: repr | ||
}], | ||
}; | ||
|
||
supplyDefaults(gd); | ||
|
||
expect(gd.data[0].y).toEqual(repr); | ||
expect(gd._fullData[0].y).toEqual(arraySpec[1]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('base64', function() { | ||
it('should accept representation as base 64 string', function() { | ||
typedArraySpecs.forEach(function(arraySpec) { | ||
// Build value and confirm its type | ||
var value = b64.encode(arraySpec[1].buffer); | ||
expect(typeof value).toEqual('string'); | ||
|
||
var repr = { | ||
dtype: arraySpec[0], | ||
value: value | ||
}; | ||
var gd = { | ||
data: [{ | ||
y: repr | ||
}], | ||
}; | ||
|
||
supplyDefaults(gd); | ||
expect(gd.data[0].y).toEqual(repr); | ||
expect(gd._fullData[0].y).toEqual(arraySpec[1]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('mock', function() { | ||
it('should accept representation as base 64 and Array in Mock', function() { | ||
|
||
var gd = Lib.extendDeep({}, mock1); | ||
supplyDefaults(gd); | ||
|
||
// Check x | ||
// data_array property | ||
expect(gd.data[0].x).toEqual({ | ||
'dtype': 'float64', | ||
'value': [3, 2, 1]}); | ||
expect(gd._fullData[0].x).toEqual(new Float64Array([3, 2, 1])); | ||
|
||
// Check y | ||
// data_array property | ||
expect(gd.data[0].y).toEqual({ | ||
'dtype': 'float32', | ||
'value': 'AABAQAAAAEAAAIA/'}); | ||
expect(gd._fullData[0].y).toEqual(new Float32Array([3, 2, 1])); | ||
|
||
// Check marker.color | ||
// This is an arrayOk property not a data_array property | ||
expect(gd.data[0].marker.color).toEqual({ | ||
'dtype': 'uint16', | ||
'value': 'AwACAAEA'}); | ||
expect(gd._fullData[0].marker.color).toEqual(new Uint16Array([3, 2, 1])); | ||
}); | ||
}); | ||
}); |
Uh oh!
There was an error while loading. Please reload this page.