-
-
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 4 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 | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -13,6 +13,7 @@ | |||||||||||||||||||||
var d3 = require('d3'); | ||||||||||||||||||||||
var isNumeric = require('fast-isnumeric'); | ||||||||||||||||||||||
var hasHover = require('has-hover'); | ||||||||||||||||||||||
var b64 = require('base64-arraybuffer'); | ||||||||||||||||||||||
|
||||||||||||||||||||||
var Lib = require('../lib'); | ||||||||||||||||||||||
var Events = require('../lib/events'); | ||||||||||||||||||||||
|
@@ -2156,6 +2157,82 @@ exports.update = function update(gd, traceUpdate, layoutUpdate, _traces) { | |||||||||||||||||||||
}); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
/** | ||||||||||||||||||||||
* Convert a primitive TypedArray representation object into a TypedArray | ||||||||||||||||||||||
* @param {object} v: Object with `dtype` and `data` properties that | ||||||||||||||||||||||
* represens a TypedArray. | ||||||||||||||||||||||
* | ||||||||||||||||||||||
* @returns {TypedArray} | ||||||||||||||||||||||
*/ | ||||||||||||||||||||||
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. We'll need to append this list: Lines 12 to 21 in a8c6217
to make 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. and we'll need to add fallbacks so that browsers w/o typed array support don't break. We have a "test" for that:
|
||||||||||||||||||||||
int8: Int8Array, | ||||||||||||||||||||||
int16: Int16Array, | ||||||||||||||||||||||
int32: Int32Array, | ||||||||||||||||||||||
uint8: Uint8Array, | ||||||||||||||||||||||
uint8_clamped: Uint8ClampedArray, | ||||||||||||||||||||||
uint16: Uint16Array, | ||||||||||||||||||||||
uint32: Uint32Array, | ||||||||||||||||||||||
float32: Float32Array, | ||||||||||||||||||||||
float64: Float64Array | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
function primitiveTypedArrayReprToTypedArray(v) { | ||||||||||||||||||||||
// 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; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
function performImport(v) { | ||||||||||||||||||||||
if(Lib.isPrimitiveTypedArrayRepr(v)) { | ||||||||||||||||||||||
return primitiveTypedArrayReprToTypedArray(v); | ||||||||||||||||||||||
} else if(Lib.isTypedArray(v)) { | ||||||||||||||||||||||
return v; | ||||||||||||||||||||||
} else if(Array.isArray(v)) { | ||||||||||||||||||||||
return v.map(performImport); | ||||||||||||||||||||||
} else if(Lib.isPlainObject(v)) { | ||||||||||||||||||||||
var result = {}; | ||||||||||||||||||||||
for(var k in v) { | ||||||||||||||||||||||
if(v.hasOwnProperty(k)) { | ||||||||||||||||||||||
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. Here we may list the keys using Object.getOwnPropertyNames and loop through them. |
||||||||||||||||||||||
result[k] = performImport(v[k]); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
return result; | ||||||||||||||||||||||
} else { | ||||||||||||||||||||||
return v; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/** | ||||||||||||||||||||||
* Plotly.import: | ||||||||||||||||||||||
* Import an object or array into... TODO | ||||||||||||||||||||||
* @param v | ||||||||||||||||||||||
* @returns {object} | ||||||||||||||||||||||
*/ | ||||||||||||||||||||||
exports.import = function(v) { | ||||||||||||||||||||||
return performImport(v); | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
/** | ||||||||||||||||||||||
* Plotly.react: | ||||||||||||||||||||||
* A plot/update method that takes the full plot state (same API as plot/newPlot) | ||||||||||||||||||||||
|
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,143 @@ | ||
var Plotly = require('@lib/index'); | ||
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 raw = { | ||
data: [{ | ||
y: repr | ||
}], | ||
}; | ||
|
||
var gd = Plotly.import(raw); | ||
|
||
expect(gd.data[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 raw = { | ||
data: [{ | ||
y: repr | ||
}], | ||
}; | ||
|
||
var gd = Plotly.import(raw); | ||
|
||
expect(gd.data[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 raw = { | ||
data: [{ | ||
y: repr | ||
}], | ||
}; | ||
|
||
var gd = Plotly.import(raw); | ||
|
||
expect(gd.data[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 raw = { | ||
data: [{ | ||
y: repr | ||
}], | ||
}; | ||
|
||
var gd = Plotly.import(raw); | ||
expect(gd.data[0].y).toEqual(arraySpec[1]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('mock', function() { | ||
it('should import representation as base 64 and Array in Mock', function() { | ||
|
||
var gd = Plotly.import(mock1); | ||
// Check x | ||
// data_array property | ||
expect(mock1.data[0].x).toEqual({ | ||
'dtype': 'float64', | ||
'value': [3, 2, 1]}); | ||
expect(gd.data[0].x).toEqual(new Float64Array([3, 2, 1])); | ||
|
||
// Check y | ||
// data_array property | ||
expect(mock1.data[0].y).toEqual({ | ||
'dtype': 'float32', | ||
'value': 'AABAQAAAAEAAAIA/'}); | ||
expect(gd.data[0].y).toEqual(new Float32Array([3, 2, 1])); | ||
|
||
// Check marker.color | ||
// This is an arrayOk property not a data_array property | ||
expect(mock1.data[0].marker.color).toEqual({ | ||
'dtype': 'uint16', | ||
'value': 'AwACAAEA'}); | ||
expect(gd.data[0].marker.color).toEqual(new Uint16Array([3, 2, 1])); | ||
}); | ||
}); | ||
}); |
Uh oh!
There was an error while loading. Please reload this page.