numpy.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /* jshint esversion: 6 */
  2. var numpy = numpy || {};
  3. numpy.Array = class {
  4. constructor(buffer) {
  5. if (buffer) {
  6. const reader = new numpy.Reader(buffer);
  7. const signature = [ 0x93, 0x4E, 0x55, 0x4D, 0x50, 0x59 ];
  8. if (!reader.bytes(6).every((v, i) => v == signature[i])) {
  9. throw new numpy.Error('Invalid signature.');
  10. }
  11. const major = reader.byte();
  12. const minor = reader.byte();
  13. if (major !== 1 && minor !== 0) {
  14. throw new numpy.Error("Invalid version '" + [ major, minor ].join('.') + "'.");
  15. }
  16. const header = JSON.parse(reader.string().trim().replace(/'/g, '"').replace("False", "false").replace("(", "[").replace(/,*\),*/g, "]"));
  17. if (header.fortran_order) {
  18. throw new numpy.Error("Fortran order is not supported.'");
  19. }
  20. if (!header.descr || header.descr.length < 2) {
  21. throw new numpy.Error("Missing property 'descr'.");
  22. }
  23. if (!header.shape) {
  24. throw new numpy.Error("Missing property 'shape'.");
  25. }
  26. this._shape = header.shape;
  27. this._byteOrder = header.descr[0];
  28. switch (this._byteOrder) {
  29. case '|': {
  30. this._dataType = header.descr.substring(1);
  31. this._data = reader.bytes(reader.size - reader.position);
  32. break;
  33. }
  34. case '>':
  35. case '<': {
  36. if (header.descr.length !== 3) {
  37. throw new numpy.Error("Unsupported data type '" + header.descr + "'.");
  38. }
  39. this._dataType = header.descr.substring(1);
  40. const size = parseInt(header.descr[2], 10) * this._shape.reduce((a, b) => a * b, 1);
  41. this._data = reader.bytes(size);
  42. break;
  43. }
  44. default:
  45. throw new numpy.Error("Unsupported data type '" + header.descr + "'.");
  46. }
  47. }
  48. }
  49. get data() {
  50. return this._data;
  51. }
  52. set data(value) {
  53. this._data = value;
  54. }
  55. get dataType() {
  56. return this._dataType;
  57. }
  58. set dataType(value) {
  59. this._dataType = value;
  60. }
  61. get shape() {
  62. return this._shape;
  63. }
  64. set shape(value) {
  65. this._shape = value;
  66. }
  67. get byteOrder() {
  68. return this._byteOrder;
  69. }
  70. set byteOrder(value) {
  71. this._byteOrder = value;
  72. }
  73. toBuffer() {
  74. const writer = new numpy.Writer();
  75. writer.bytes([ 0x93, 0x4E, 0x55, 0x4D, 0x50, 0x59 ]); // '\\x93NUMPY'
  76. writer.byte(1); // major
  77. writer.byte(0); // minor
  78. const context = {
  79. itemSize: 1,
  80. position: 0,
  81. dataType: this._dataType,
  82. byteOrder: this._byteOrder || '<',
  83. shape: this._shape,
  84. descr: '',
  85. };
  86. if (context.byteOrder !== '<' && context.byteOrder !== '>') {
  87. throw new numpy.Error("Unknown byte order '" + this._byteOrder + "'.");
  88. }
  89. if (context.dataType.length !== 2 || (context.dataType[0] !== 'f' && context.dataType[0] !== 'i' && context.dataType[0] !== 'u')) {
  90. throw new numpy.Error("Unsupported data type '" + this._dataType + "'.");
  91. }
  92. context.itemSize = parseInt(context.dataType[1], 10);
  93. let shape = '';
  94. switch (this._shape.length) {
  95. case 0:
  96. throw new numpy.Error('Invalid shape.');
  97. case 1:
  98. shape = '(' + this._shape[0].toString() + ',)';
  99. break;
  100. default:
  101. shape = '(' + this._shape.map((dimension) => dimension.toString()).join(', ') + ')';
  102. break;
  103. }
  104. const properties = [
  105. "'descr': '" + context.byteOrder + context.dataType + "'",
  106. "'fortran_order': False",
  107. "'shape': " + shape
  108. ];
  109. let header = '{ ' + properties.join(', ') + ' }';
  110. header += ' '.repeat(16 - ((header.length + 2 + 8 + 1) & 0x0f)) + '\n';
  111. writer.string(header);
  112. const size = context.itemSize * this._shape.reduce((a, b) => a * b);
  113. context.data = new Uint8Array(size);
  114. context.dataView = new DataView(context.data.buffer, context.data.byteOffset, size);
  115. numpy.Array._encodeDimension(context, this._data, 0);
  116. writer.bytes(context.data);
  117. return writer.toBuffer();
  118. }
  119. static _encodeDimension(context, data, dimension) {
  120. const size = context.shape[dimension];
  121. const littleEndian = context.byteOrder === '<';
  122. if (dimension == context.shape.length - 1) {
  123. for (let i = 0; i < size; i++) {
  124. switch (context.dataType) {
  125. case 'f2':
  126. context.dataView.setFloat16(context.position, data[i], littleEndian);
  127. break;
  128. case 'f4':
  129. context.dataView.setFloat32(context.position, data[i], littleEndian);
  130. break;
  131. case 'f8':
  132. context.dataView.setFloat64(context.position, data[i], littleEndian);
  133. break;
  134. case 'i1':
  135. context.dataView.setInt8(context.position, data[i], littleEndian);
  136. break;
  137. case 'i2':
  138. context.dataView.setInt16(context.position, data[i], littleEndian);
  139. break;
  140. case 'i4':
  141. context.dataView.setInt32(context.position, data[i], littleEndian);
  142. break;
  143. case 'i8':
  144. context.data.set(data[i].toBytes(littleEndian), context.position);
  145. break;
  146. case 'u1':
  147. context.dataView.setUint8(context.position, data[i], littleEndian);
  148. break;
  149. case 'u2':
  150. context.dataView.setUint16(context.position, data[i], littleEndian);
  151. break;
  152. case 'u4':
  153. context.dataView.setUint32(context.position, data[i], littleEndian);
  154. break;
  155. case 'u8':
  156. context.data.set(data[i].toBytes(littleEndian), context.position);
  157. break;
  158. }
  159. context.position += context.itemSize;
  160. }
  161. }
  162. else {
  163. for (let j = 0; j < size; j++) {
  164. numpy.Array._encodeDimension(context, data[j], dimension + 1);
  165. }
  166. }
  167. }
  168. };
  169. numpy.Reader = class {
  170. constructor(buffer) {
  171. this._buffer = buffer;
  172. this._position = 0;
  173. }
  174. get position() {
  175. return this._position;
  176. }
  177. get size() {
  178. return this._buffer.length;
  179. }
  180. byte() {
  181. return this._buffer[this._position++];
  182. }
  183. bytes(size) {
  184. const value = this._buffer.slice(this._position, this._position + size);
  185. this._position += size;
  186. return value;
  187. }
  188. uint16() {
  189. return this.byte() | (this.byte() << 8);
  190. }
  191. string() {
  192. const size = this.uint16();
  193. let value = '';
  194. for (let i = 0; i < size; i++) {
  195. value += String.fromCharCode(this.byte());
  196. }
  197. return value;
  198. }
  199. };
  200. numpy.Writer = class {
  201. constructor() {
  202. this._length = 0;
  203. this._head = null;
  204. this._tail = null;
  205. }
  206. byte(value) {
  207. this.bytes([ value ]);
  208. }
  209. uint16(value) {
  210. this.bytes([ value & 0xff, (value >> 8) & 0xff ]);
  211. }
  212. bytes(values) {
  213. const array = new Uint8Array(values.length);
  214. for (let i = 0; i < values.length; i++) {
  215. array[i] = values[i];
  216. }
  217. this._write(array);
  218. }
  219. string(value) {
  220. this.uint16(value.length);
  221. const array = new Uint8Array(value.length);
  222. for (let i = 0; i < value.length; i++) {
  223. array[i] = value.charCodeAt(i);
  224. }
  225. this._write(array);
  226. }
  227. _write(array) {
  228. const node = { buffer: array, next: null };
  229. if (this._tail) {
  230. this._tail.next = node;
  231. }
  232. else {
  233. this._head = node;
  234. }
  235. this._tail = node;
  236. this._length += node.buffer.length;
  237. }
  238. toBuffer() {
  239. const array = new Uint8Array(this._length);
  240. let position = 0;
  241. let head = this._head;
  242. while (head != null) {
  243. array.set(head.buffer, position);
  244. position += head.buffer.length;
  245. head = head.next;
  246. }
  247. return array;
  248. }
  249. };
  250. numpy.Error = class extends Error {
  251. constructor(message) {
  252. super(message);
  253. this.name = 'NumPy Error';
  254. }
  255. };
  256. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  257. module.exports.Array = numpy.Array;
  258. }