| class npyjs { |
|
|
| constructor(opts) { |
| if (opts && !('convertFloat16' in opts)) { |
| console.warn([ |
| "npyjs constructor now accepts {convertFloat16?: boolean}.", |
| "For usage, go to https://github.com/jhuapl-boss/npyjs." |
| ].join(" ")); |
| } |
|
|
| this.convertFloat16 = opts?.convertFloat16 ?? true; |
|
|
| this.dtypes = { |
| "<u1": { |
| name: "uint8", |
| size: 8, |
| arrayConstructor: Uint8Array, |
| }, |
| "|u1": { |
| name: "uint8", |
| size: 8, |
| arrayConstructor: Uint8Array, |
| }, |
| "<u2": { |
| name: "uint16", |
| size: 16, |
| arrayConstructor: Uint16Array, |
| }, |
| "|i1": { |
| name: "int8", |
| size: 8, |
| arrayConstructor: Int8Array, |
| }, |
| "<i2": { |
| name: "int16", |
| size: 16, |
| arrayConstructor: Int16Array, |
| }, |
| "<u4": { |
| name: "uint32", |
| size: 32, |
| arrayConstructor: Uint32Array, |
| }, |
| "<i4": { |
| name: "int32", |
| size: 32, |
| arrayConstructor: Int32Array, |
| }, |
| "<u8": { |
| name: "uint64", |
| size: 64, |
| arrayConstructor: BigUint64Array, |
| }, |
| "<i8": { |
| name: "int64", |
| size: 64, |
| arrayConstructor: BigInt64Array, |
| }, |
| "<f4": { |
| name: "float32", |
| size: 32, |
| arrayConstructor: Float32Array |
| }, |
| "<f8": { |
| name: "float64", |
| size: 64, |
| arrayConstructor: Float64Array |
| }, |
| "<f2": { |
| name: "float16", |
| size: 16, |
| arrayConstructor: Uint16Array, |
| converter: this.convertFloat16 ? this.float16ToFloat32Array : undefined |
| }, |
| }; |
| } |
|
|
| float16ToFloat32Array(float16Array) { |
| const length = float16Array.length; |
| const float32Array = new Float32Array(length); |
| |
| for (let i = 0; i < length; i++) { |
| float32Array[i] = npyjs.float16ToFloat32(float16Array[i]); |
| } |
| |
| return float32Array; |
| } |
|
|
| static float16ToFloat32(float16) { |
| |
| const sign = (float16 >> 15) & 0x1; |
| const exponent = (float16 >> 10) & 0x1f; |
| const fraction = float16 & 0x3ff; |
|
|
| |
| if (exponent === 0) { |
| if (fraction === 0) { |
| |
| return sign ? -0 : 0; |
| } |
| |
| return (sign ? -1 : 1) * Math.pow(2, -14) * (fraction / 0x400); |
| } else if (exponent === 0x1f) { |
| if (fraction === 0) { |
| |
| return sign ? -Infinity : Infinity; |
| } |
| |
| return NaN; |
| } |
|
|
| |
| return (sign ? -1 : 1) * Math.pow(2, exponent - 15) * (1 + fraction / 0x400); |
| } |
|
|
| parse(arrayBufferContents) { |
| |
| const headerLength = new DataView(arrayBufferContents.slice(8, 10)).getUint8(0); |
| const offsetBytes = 10 + headerLength; |
|
|
| const hcontents = new TextDecoder("utf-8").decode( |
| new Uint8Array(arrayBufferContents.slice(10, 10 + headerLength)) |
| ); |
| const header = JSON.parse( |
| hcontents |
| .toLowerCase() |
| .replace(/'/g, '"') |
| .replace("(", "[") |
| .replace(/,*\),*/g, "]") |
| ); |
| const shape = header.shape; |
| const dtype = this.dtypes[header.descr]; |
|
|
| if (!dtype) { |
| console.error(`Unsupported dtype: ${header.descr}`); |
| return null; |
| } |
|
|
| const nums = new dtype.arrayConstructor( |
| arrayBufferContents, |
| offsetBytes |
| ); |
|
|
| |
| const data = dtype.converter ? dtype.converter.call(this, nums) : nums; |
|
|
| return { |
| dtype: dtype.name, |
| data: data, |
| shape, |
| fortranOrder: header.fortran_order |
| }; |
| } |
|
|
| async load(filename, callback, fetchArgs) { |
| |
| |
| |
| fetchArgs = fetchArgs || {}; |
| let arrayBuf; |
| |
| if (filename instanceof ArrayBuffer) { |
| arrayBuf = filename; |
| } |
| |
| else { |
| const resp = await fetch(filename, { ...fetchArgs }); |
| arrayBuf = await resp.arrayBuffer(); |
| } |
| const result = this.parse(arrayBuf); |
| if (callback) { |
| return callback(result); |
| } |
| return result; |
| } |
| } |
|
|