File size: 2,434 Bytes
30e9731
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// https://github.com/aplbrain/npyjs/blob/master/LICENSE

const 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: Int32Array,
  },
  '<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
  },
};

function parse(buffer){
  const buf = new Uint8Array(buffer);
  if (buf[6] != 1) throw 'Only npy version 1 is supported';

  const headerLength = buf[8] + buf[9]*256;
  const offsetBytes = 10 + headerLength;

  const header = JSON.parse(
    new TextDecoder('utf-8')
      .decode(buf.slice(10, 10 + headerLength))
      .replace(/'/g, '"')
      .replace('False', 'false')
      .replace('(', '[')
      .replace(/,*\),*/g, ']')
  );

  if (header.fortan_order) throw 'Fortran-contiguous array data are not supported';
  const dtype = dtypes[header.descr];

  return {
    data: new dtype['arrayConstructor'](buf.slice(offsetBytes).buffer),
    shape: header.shape,
    dtype: dtype.name,
  };
}

function format(typedArray, shape){
  let dtype = null;
  for (let d in dtypes){
    if (dtypes[d].arrayConstructor == typedArray.constructor) dtype = d;
  }
  if (dtype === null) throw 'Invalid typedArray';

  const header = `{'descr': '${dtype}', 'fortran_order': False, 'shape': (${shape.join(',')},), }\n`;
  const spacepad = Array.from({length: 64 - (8 + header.length) % 64}, d => '\x20').join('');

  const hl = (header + spacepad).length;

  return Buffer.concat([
    Buffer.from('\x93NUMPY\x01\x00', 'latin1'),
    // convert to little-endian
    Buffer.from(new Uint8Array([hl % 256, hl/256 | 0])),
    Buffer.from(header + spacepad, 'latin1'),
    Buffer.from(typedArray.buffer)
  ]);
}

export default {parse, format};