123 lines
4.0 KiB
JavaScript
123 lines
4.0 KiB
JavaScript
// Client-side parser for .npy files
|
|
// See the specification: http://docs.scipy.org/doc/numpy-dev/neps/npy-format.html
|
|
|
|
const NumpyLoader = (function NumpyLoader() {
|
|
function asciiDecode(buf) {
|
|
return String.fromCharCode.apply(null, new Uint8Array(buf));
|
|
}
|
|
|
|
function readUint16LE(buffer) {
|
|
var view = new DataView(buffer);
|
|
var val = view.getUint8(0);
|
|
val |= view.getUint8(1) << 8;
|
|
return val;
|
|
}
|
|
|
|
function fromArrayBuffer(buf) {
|
|
// Check the magic number
|
|
var magic = asciiDecode(buf.slice(0,6));
|
|
if (magic !== "\x93NUMPY") {
|
|
throw new Error("Bad magic number");
|
|
}
|
|
|
|
var version = new Uint8Array(buf.slice(6,8)),
|
|
headerLength = readUint16LE(buf.slice(8,10)),
|
|
headerStr = asciiDecode(buf.slice(10, 10+headerLength));
|
|
const offsetBytes = 10 + headerLength;
|
|
//rest = buf.slice(10+headerLength); XXX -- This makes a copy!!! https://www.khronos.org/registry/typedarray/specs/latest/#5
|
|
|
|
// Hacky conversion of dict literal string to JS Object
|
|
const info = JSON.parse(headerStr.toLowerCase()
|
|
.replace('(','[')
|
|
.replace('),',']')
|
|
.replace(/'/g, '"')
|
|
.replace(',]', ']'));
|
|
|
|
// Intepret the bytes according to the specified dtype
|
|
var data;
|
|
if (info.descr === "|u1") {
|
|
data = new Uint8Array(buf, offsetBytes);
|
|
} else if (info.descr === "|i1") {
|
|
data = new Int8Array(buf, offsetBytes);
|
|
} else if (info.descr === "<u2") {
|
|
data = new Uint16Array(buf, offsetBytes);
|
|
} else if (info.descr === "<i2") {
|
|
data = new Int16Array(buf, offsetBytes);
|
|
} else if (info.descr === "<u4") {
|
|
data = new Uint32Array(buf, offsetBytes);
|
|
} else if (info.descr === "<i4") {
|
|
data = new Int32Array(buf, offsetBytes);
|
|
} else if (info.descr === "<f4") {
|
|
data = new Float32Array(buf, offsetBytes);
|
|
} else if (info.descr === "<f8") {
|
|
data = new Float64Array(buf, offsetBytes);
|
|
} else if (info.descr === "<u8") {
|
|
// 8 byte uint64s
|
|
data = new Uint8Array(buf, offsetBytes);
|
|
} else if (info.descr === "<i8") {
|
|
// 8 byte int64s
|
|
data = new Uint8Array(buf, offsetBytes);
|
|
} else if (info.descr === "|s8") {
|
|
// 8 byte strings
|
|
data = new Uint8Array(buf, offsetBytes);
|
|
} else {
|
|
throw new Error('unknown numeric dtype '+info.descr)
|
|
}
|
|
|
|
return {
|
|
shape: info.shape,
|
|
fortran_order: info.fortran_order,
|
|
data: data
|
|
};
|
|
}
|
|
|
|
function open(file, callback) {
|
|
var reader = new FileReader();
|
|
reader.onload = function() {
|
|
// the file contents have been read as an array buffer
|
|
var buf = reader.result;
|
|
var ndarray = fromArrayBuffer(buf);
|
|
callback(ndarray);
|
|
};
|
|
reader.readAsArrayBuffer(file);
|
|
}
|
|
|
|
function promise(url) {
|
|
return new Promise(function(resolve, reject) {
|
|
// Do the usual XHR stuff
|
|
var req = new XMLHttpRequest();
|
|
req.onload = function() {
|
|
// This is called even on 404 etc
|
|
// so check the status
|
|
if (req.status == 200) {
|
|
var buf = req.response; // not responseText
|
|
var ndarray = fromArrayBuffer(buf);
|
|
resolve(ndarray);
|
|
} else {
|
|
// Otherwise reject with the status text
|
|
// which will hopefully be a meaningful error
|
|
reject(Error(req.statusText));
|
|
}
|
|
};
|
|
|
|
// Handle network errors
|
|
req.onerror = function() {
|
|
reject(Error("Network Error"));
|
|
};
|
|
|
|
// Make the request
|
|
req.open('GET', url, true);
|
|
req.responseType = "arraybuffer";
|
|
req.send(null);
|
|
});
|
|
}
|
|
|
|
return {
|
|
open: open,
|
|
promise: promise,
|
|
fromArrayBuffer: fromArrayBuffer,
|
|
};
|
|
})();
|
|
|
|
module.exports = NumpyLoader;
|