hdf5.js 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644
  1. // Experimental HDF5 JavaScript reader
  2. var hdf5 = hdf5 || {};
  3. var zip = zip || require('./zip');
  4. hdf5.File = class {
  5. static open(data) {
  6. if (data && data.length >= 8) {
  7. const buffer = data instanceof Uint8Array ? data : data.peek(8);
  8. const signature = [ 0x89, 0x48, 0x44, 0x46, 0x0D, 0x0A, 0x1A, 0x0A ]; // \x89HDF\r\n\x1A\n
  9. if (signature.every((value, index) => value === buffer[index])) {
  10. return new hdf5.File(data);
  11. }
  12. }
  13. return null;
  14. }
  15. constructor(data) {
  16. // https://support.hdfgroup.org/HDF5/doc/H5.format.html
  17. const reader = data instanceof Uint8Array ? new hdf5.BinaryReader(data) : (data.length < 0x10000000 ? new hdf5.BinaryReader(data.peek()) : new hdf5.StreamReader(data)); reader.skip(8);
  18. this._globalHeap = new hdf5.GlobalHeap(reader);
  19. const version = reader.byte();
  20. switch (version) {
  21. case 0:
  22. case 1: {
  23. this._freeSpaceStorageVersion = reader.byte();
  24. this._rootGroupEntryVersion = reader.byte();
  25. reader.skip(1);
  26. this._sharedHeaderMessageVersionFormat = reader.byte();
  27. reader.initialize();
  28. reader.skip(1);
  29. this._groupLeafNodeK = reader.uint16(); // 0x04?
  30. this._groupInternalNodeK = reader.uint16(); // 0x10?
  31. reader.skip(4);
  32. if (version > 0) {
  33. this._indexedStorageInternalNodeK = reader.uint16();
  34. this.skip(2); // Reserved
  35. }
  36. this._baseAddress = reader.offset();
  37. reader.offset(); // Address of File Free space Info
  38. this._endOfFileAddress = reader.offset();
  39. reader.offset(); // Driver Information Block Address
  40. if (this._baseAddress != 0) {
  41. throw new hdf5.Error('Base address is not zero.');
  42. }
  43. const rootGroupEntry = new hdf5.SymbolTableEntry(reader);
  44. this._rootGroup = new hdf5.Group(reader, rootGroupEntry, null, this._globalHeap, '', '');
  45. break;
  46. }
  47. case 2:
  48. case 3: {
  49. reader.initialize();
  50. reader.byte();
  51. this._baseAddress = reader.offset();
  52. this._superBlockExtensionAddress = reader.offset();
  53. this._endOfFileAddress = reader.offset();
  54. const rootGroupObjectHeader = new hdf5.DataObjectHeader(reader.at(reader.offset()));
  55. this._rootGroup = new hdf5.Group(reader, null, rootGroupObjectHeader, this._globalHeap, '', '');
  56. break;
  57. }
  58. default:
  59. throw new hdf5.Error('Unsupported Superblock version ' + version + '.');
  60. }
  61. }
  62. get rootGroup() {
  63. return this._rootGroup;
  64. }
  65. };
  66. hdf5.Group = class {
  67. constructor(reader, entry, objectHeader, globalHeap, parentPath, name) {
  68. this._reader = reader;
  69. this._entry = entry;
  70. this._dataObjectHeader = objectHeader;
  71. this._globalHeap = globalHeap;
  72. this._name = name;
  73. this._path = parentPath == '/' ? (parentPath + name) : (parentPath + '/' + name);
  74. }
  75. get name() {
  76. return this._name;
  77. }
  78. get path() {
  79. return this._path;
  80. }
  81. group(path) {
  82. this._decodeGroups();
  83. if (this._groups.has(path)) {
  84. return this._groups.get(path);
  85. }
  86. const index = path.indexOf('/');
  87. if (index !== -1) {
  88. const group = this.group(path.substring(0, index));
  89. if (group) {
  90. return group.group(path.substring(index + 1));
  91. }
  92. }
  93. return null;
  94. }
  95. get groups() {
  96. this._decodeGroups();
  97. return this._groups;
  98. }
  99. get attributes() {
  100. this._decodeDataObject();
  101. return this._attributes;
  102. }
  103. get value() {
  104. this._decodeDataObject();
  105. return this._value;
  106. }
  107. _decodeDataObject() {
  108. if (!this._dataObjectHeader) {
  109. const reader = this._reader.at(this._entry.objectHeaderAddress);
  110. this._dataObjectHeader = new hdf5.DataObjectHeader(reader);
  111. }
  112. if (!this._attributes) {
  113. this._attributes = new Map();
  114. for (const attribute of this._dataObjectHeader.attributes) {
  115. const name = attribute.name;
  116. const value = attribute.decodeValue(this._globalHeap);
  117. this._attributes.set(name, value);
  118. }
  119. this._value = null;
  120. const datatype = this._dataObjectHeader.datatype;
  121. const dataspace = this._dataObjectHeader.dataspace;
  122. const dataLayout = this._dataObjectHeader.dataLayout;
  123. const filterPipeline = this._dataObjectHeader.filterPipeline;
  124. if (datatype && dataspace && dataLayout) {
  125. this._value = new hdf5.Variable(this._reader, this._globalHeap, datatype, dataspace, dataLayout, filterPipeline);
  126. }
  127. }
  128. }
  129. _decodeGroups() {
  130. if (!this._groups) {
  131. this._groups = new Map();
  132. if (this._entry) {
  133. if (this._entry.treeAddress || this._entry.heapAddress) {
  134. const heap = new hdf5.Heap(this._reader.at(this._entry.heapAddress));
  135. const tree = new hdf5.Tree(this._reader.at(this._entry.treeAddress));
  136. for (const node of tree.nodes) {
  137. for (const entry of node.entries) {
  138. const name = heap.getString(entry.linkNameOffset);
  139. const group = new hdf5.Group(this._reader, entry, null, this._globalHeap, this._path, name);
  140. this._groups.set(name, group);
  141. }
  142. }
  143. }
  144. }
  145. else {
  146. this._decodeDataObject();
  147. for (const link of this._dataObjectHeader.links) {
  148. if (Object.prototype.hasOwnProperty.call(link, 'objectHeaderAddress')) {
  149. const name = link.name;
  150. const objectHeader = new hdf5.DataObjectHeader(this._reader.at(link.objectHeaderAddress));
  151. const linkGroup = new hdf5.Group(this._reader, null, objectHeader, this._globalHeap, this._path, name);
  152. this._groups.set(name, linkGroup);
  153. }
  154. }
  155. }
  156. }
  157. }
  158. };
  159. hdf5.Variable = class {
  160. constructor(reader, globalHeap, datatype, dataspace, dataLayout, filterPipeline) {
  161. this._reader = reader;
  162. this._globalHeap = globalHeap;
  163. this._datatype = datatype;
  164. this._dataspace = dataspace;
  165. this._dataLayout = dataLayout;
  166. this._filterPipeline = filterPipeline;
  167. }
  168. get type () {
  169. return this._datatype.type;
  170. }
  171. get littleEndian() {
  172. return this._datatype.littleEndian;
  173. }
  174. get shape() {
  175. return this._dataspace.shape;
  176. }
  177. get value() {
  178. const data = this.data;
  179. if (data) {
  180. const reader = new hdf5.BinaryReader(data);
  181. const array = this._dataspace.read(this._datatype, reader);
  182. return this._dataspace.decode(this._datatype, array, array, this._globalHeap);
  183. }
  184. return null;
  185. }
  186. get data() {
  187. switch (this._dataLayout.layoutClass) {
  188. case 1: // Contiguous
  189. if (this._dataLayout.address) {
  190. return this._reader.at(this._dataLayout.address).stream(this._dataLayout.size);
  191. }
  192. break;
  193. case 2: { // Chunked
  194. const dimensionality = this._dataLayout.dimensionality;
  195. const tree = new hdf5.Tree(this._reader.at(this._dataLayout.address), dimensionality);
  196. const item_size = this._dataLayout.datasetElementSize;
  197. const chunk_shape = this._dataLayout.dimensionSizes;
  198. const data_shape = this._dataspace.shape;
  199. const chunk_size = chunk_shape.reduce((a, b) => a * b, 1);
  200. const data_size = data_shape.reduce((a, b) => a * b, 1);
  201. const max_dim = data_shape.length - 1;
  202. let data_stride = 1;
  203. const data_strides = data_shape.slice().reverse().map((d2) => {
  204. const s = data_stride;
  205. data_stride *= d2;
  206. return s;
  207. }).reverse();
  208. const data = new Uint8Array(data_size * item_size);
  209. for (const node of tree.nodes) {
  210. if (node.filterMask !== 0) {
  211. return null;
  212. }
  213. let chunk = node.data;
  214. if (this._filterPipeline) {
  215. for (const filter of this._filterPipeline.filters) {
  216. chunk = filter.decode(chunk);
  217. }
  218. }
  219. const chunk_offset = node.fields;
  220. var data_pos = chunk_offset.slice();
  221. var chunk_pos = data_pos.map(() => 0);
  222. for (let chunk_index = 0; chunk_index < chunk_size; chunk_index++) {
  223. for (let i = max_dim; i >= 0; i--) {
  224. if (chunk_pos[i] >= chunk_shape[i]) {
  225. chunk_pos[i] = 0;
  226. data_pos[i] = chunk_offset[i];
  227. if (i > 0) {
  228. chunk_pos[i - 1]++;
  229. data_pos[i - 1]++;
  230. }
  231. }
  232. else {
  233. break;
  234. }
  235. }
  236. let index = 0;
  237. let inbounds = true;
  238. const length = data_pos.length - 1;
  239. for (let i = 0; i < length; i++) {
  240. const pos = data_pos[i];
  241. inbounds = inbounds && pos < data_shape[i];
  242. index += pos * data_strides[i];
  243. }
  244. if (inbounds) {
  245. let chunk_offset = chunk_index * item_size;
  246. let target_offset = index * item_size;
  247. const target_end = target_offset + item_size;
  248. while (target_offset < target_end) {
  249. data[target_offset++] = chunk[chunk_offset++];
  250. }
  251. }
  252. chunk_pos[max_dim]++;
  253. data_pos[max_dim]++;
  254. }
  255. }
  256. return data;
  257. }
  258. default: {
  259. throw new hdf5.Error("Unsupported data layout class '" + this.layoutClass + "'.");
  260. }
  261. }
  262. return null;
  263. }
  264. };
  265. hdf5.Reader = class {
  266. constructor() {
  267. }
  268. initialize() {
  269. this._offsetSize = this.byte();
  270. this._lengthSize = this.byte();
  271. }
  272. int8() {
  273. const position = this.take(1);
  274. return this._view.getInt8(position);
  275. }
  276. byte() {
  277. const position = this.take(1);
  278. return this._view.getUint8(position);
  279. }
  280. int16() {
  281. const position = this.take(2);
  282. return this._view.getInt16(position, true);
  283. }
  284. uint16() {
  285. const position = this.take(2);
  286. return this._view.getUint16(position, true);
  287. }
  288. int32() {
  289. const position = this.take(4);
  290. return this._view.getInt32(position, true);
  291. }
  292. uint32() {
  293. const position = this.take(4);
  294. return this._view.getUint32(position, true);
  295. }
  296. int64() {
  297. const position = this.take(8);
  298. return this._view.getInt64(position, true).toNumber();
  299. }
  300. uint64() {
  301. const position = this.take(8);
  302. return this._view.getUint64(position, true).toNumber();
  303. }
  304. uint(size) {
  305. switch (size) {
  306. case 0: return this.byte();
  307. case 1: return this.uint16();
  308. case 2: return this.uint32();
  309. case 3: return this.uint64();
  310. default: throw new hdf5.Error("Unsupported uint size '" + size + "'.");
  311. }
  312. }
  313. float16() {
  314. const position = this.take(2);
  315. const value = this._view.getUint16(position, true);
  316. // decode float16 value
  317. const s = (value & 0x8000) >> 15;
  318. const e = (value & 0x7C00) >> 10;
  319. const f = value & 0x03FF;
  320. if(e == 0) {
  321. return (s ? -1 : 1) * Math.pow(2, -14) * (f / Math.pow(2, 10));
  322. }
  323. else if (e == 0x1F) {
  324. return f ? NaN : ((s ? -1 : 1) * Infinity);
  325. }
  326. return (s ? -1 : 1) * Math.pow(2, e-15) * (1 + (f / Math.pow(2, 10)));
  327. }
  328. float32() {
  329. const position = this.take(4);
  330. return this._view.getFloat32(position, true);
  331. }
  332. float64() {
  333. const position = this.take(8);
  334. return this._view.getFloat64(position, true);
  335. }
  336. offset() {
  337. switch (this._offsetSize) {
  338. case 8: {
  339. const position = this.take(8);
  340. const value = this._view.getUint64(position, true);
  341. if (value.low === -1 && value.high === -1) {
  342. return undefined;
  343. }
  344. return value.toNumber();
  345. }
  346. case 4: {
  347. const value = this.uint32();
  348. if (value === 0xffffffff) {
  349. return undefined;
  350. }
  351. return value;
  352. }
  353. default: {
  354. throw new hdf5.Error('Unsupported offset size \'' + this._offsetSize + '\'.');
  355. }
  356. }
  357. }
  358. length() {
  359. switch (this._lengthSize) {
  360. case 8: {
  361. const position = this.take(8);
  362. const value = this._view.getUint64(position, true);
  363. if (value.low === -1 && value.high === -1) {
  364. return undefined;
  365. }
  366. return value.toNumber();
  367. }
  368. case 4: {
  369. const value = this.uint32();
  370. if (value === 0xffffffff) {
  371. return undefined;
  372. }
  373. return value;
  374. }
  375. default: {
  376. throw new hdf5.Error('Unsupported length size \'' + this._lengthSize + '\'.');
  377. }
  378. }
  379. }
  380. string(size, encoding) {
  381. if (!size || size == -1) {
  382. size = this.size(0x00);
  383. }
  384. const data = this.read(size);
  385. if (encoding == 'utf-8') {
  386. hdf5.Reader._utf8Decoder = hdf5.Reader._utf8Decoder || new TextDecoder('utf-8');
  387. return hdf5.Reader._utf8Decoder.decode(data).replace(/\0/g, '');
  388. }
  389. hdf5.Reader._asciiDecoder = hdf5.Reader._asciiDecoder = new TextDecoder('ascii');
  390. return hdf5.Reader._asciiDecoder.decode(data).replace(/\0/g, '');
  391. }
  392. match(text) {
  393. if (this.position + text.length > this._length) {
  394. return false;
  395. }
  396. const buffer = this.read(text.length);
  397. for (let i = 0; i < text.length; i++) {
  398. if (text.charCodeAt(i) != buffer[i]) {
  399. this.skip(-text.length);
  400. return false;
  401. }
  402. }
  403. return true;
  404. }
  405. };
  406. hdf5.BinaryReader = class extends hdf5.Reader {
  407. constructor(buffer, view, offset, position, offsetSize, lengthSize) {
  408. super();
  409. this._buffer = buffer;
  410. this._view = view || new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  411. this._offset = offset || 0;
  412. this._position = position || 0;
  413. this._offsetSize = offsetSize;
  414. this._lengthSize = lengthSize;
  415. }
  416. get position() {
  417. return this._position + this._offset;
  418. }
  419. take(offset) {
  420. const position = this._offset + this._position;
  421. this.skip(offset);
  422. return position;
  423. }
  424. skip(offset) {
  425. this._position += offset;
  426. if (this._offset + this._position > this._buffer.length) {
  427. throw new hdf5.Error('Expected ' + (this._offset + this._position - this._buffer.length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
  428. }
  429. }
  430. align(mod) {
  431. if (this._position % mod != 0) {
  432. this._position = (Math.floor(this._position / mod) + 1) * mod;
  433. }
  434. }
  435. peek(length) {
  436. const position = this._offset + this._position;
  437. length = length !== undefined ? length : this._buffer.length - position;
  438. this.take(length);
  439. const buffer = this._buffer.subarray(position, position + length);
  440. this._position = position - this._offset;
  441. return buffer;
  442. }
  443. read(length) {
  444. const position = this.take(length);
  445. return this._buffer.subarray(position, position + length);
  446. }
  447. stream(length) {
  448. const position = this.take(length);
  449. const buffer = this._buffer.subarray(position, position + length);
  450. return new hdf5.BinaryReader(buffer);
  451. }
  452. size(terminator) {
  453. let position = this._offset + this._position;
  454. while (this._buffer[position] !== terminator) {
  455. position++;
  456. }
  457. return position - this._offset - this._position + 1;
  458. }
  459. at(offset) {
  460. return new hdf5.BinaryReader(this._buffer, this._view, offset, 0, this._offsetSize, this._lengthSize);
  461. }
  462. clone() {
  463. return new hdf5.BinaryReader(this._buffer, this._view, this._offset, this._position, this._offsetSize, this._lengthSize);
  464. }
  465. };
  466. hdf5.StreamReader = class extends hdf5.Reader {
  467. constructor(stream, view, window, offset, position, offsetSize, lengthSize) {
  468. super();
  469. this._stream = stream;
  470. this._length = stream.length;
  471. this._view = view;
  472. this._window = window || 0;
  473. this._offset = offset || 0;
  474. this._position = position || 0;
  475. this._offsetSize = offsetSize;
  476. this._lengthSize = lengthSize;
  477. }
  478. get position() {
  479. return this._offset + this._position;
  480. }
  481. skip(offset) {
  482. this._position += offset;
  483. if (this._position > this._length) {
  484. throw new hdf5.Error('Expected ' + (this._position - this._length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
  485. }
  486. }
  487. align(mod) {
  488. if (this._position % mod != 0) {
  489. this._position = (Math.floor(this._position / mod) + 1) * mod;
  490. }
  491. }
  492. read(length) {
  493. this._stream.seek(this._offset + this._position);
  494. this.skip(length);
  495. return this._stream.read(length);
  496. }
  497. stream(length) {
  498. this._stream.seek(this._offset + this._position);
  499. this.skip(length);
  500. return this._stream.stream(length);
  501. }
  502. byte() {
  503. const position = this.take(1);
  504. return this._view.getUint8(position);
  505. }
  506. uint16() {
  507. const position = this.take(2);
  508. return this._view.getUint16(position, true);
  509. }
  510. int32() {
  511. const position = this.take(4);
  512. return this._view.getInt32(position, true);
  513. }
  514. uint32() {
  515. const position = this.take(4);
  516. return this._view.getUint32(position, true);
  517. }
  518. int64() {
  519. const position = this.take(8);
  520. return this._view.getInt64(position, true).toNumber();
  521. }
  522. float32() {
  523. const position = this.take(4);
  524. return this._view.getFloat32(position, true);
  525. }
  526. float64() {
  527. const position = this.take(8);
  528. return this._view.getFloat64(position, true);
  529. }
  530. at(offset) {
  531. return new hdf5.StreamReader(this._stream, this._view, this._window, offset, 0, this._offsetSize, this._lengthSize);
  532. }
  533. clone() {
  534. return new hdf5.StreamReader(this._stream, this._view, this._window, this._offset, this._position, this._offsetSize, this._lengthSize);
  535. }
  536. size(terminator) {
  537. const position = this._position;
  538. let size = 0;
  539. while (this.byte() != terminator) {
  540. size++;
  541. }
  542. this._position = position;
  543. return size;
  544. }
  545. take(length) {
  546. const position = this.position;
  547. if (position + length > this._length) {
  548. throw new Error('Expected ' + (position + length - this._length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
  549. }
  550. if (!this._buffer || position < this._window || position + length > this._window + this._buffer.length) {
  551. this._window = position;
  552. this._stream.seek(this._window);
  553. this._buffer = this._stream.read(Math.min(0x1000, this._length - this._window));
  554. this._view = new DataView(this._buffer.buffer, this._buffer.byteOffset, this._buffer.byteLength);
  555. }
  556. this._position += length;
  557. return position - this._window;
  558. }
  559. };
  560. hdf5.SymbolTableNode = class {
  561. constructor(reader) {
  562. if (!reader.match('SNOD')) {
  563. throw new hdf5.Error("Not a valid 'SNOD' block.");
  564. }
  565. const version = reader.byte();
  566. if (version == 1) {
  567. reader.skip(1);
  568. const entriesUsed = reader.uint16();
  569. this.entries = [];
  570. for (let i = 0; i < entriesUsed; i++) {
  571. const entry = new hdf5.SymbolTableEntry(reader);
  572. this.entries.push(entry);
  573. }
  574. }
  575. else {
  576. throw new hdf5.Error('Unsupported symbol table node version \'' + version + '\'.');
  577. }
  578. }
  579. };
  580. hdf5.SymbolTableEntry = class {
  581. constructor(reader) {
  582. this.linkNameOffset = reader.offset();
  583. this.objectHeaderAddress = reader.offset();
  584. const cacheType = reader.uint32();
  585. reader.skip(4); // Reserved
  586. switch (cacheType) {
  587. case 0:
  588. break;
  589. case 1: {
  590. const scratchReader = reader.clone();
  591. this.treeAddress = scratchReader.offset();
  592. this.heapAddress = scratchReader.offset();
  593. break;
  594. }
  595. default:
  596. throw new hdf5.Error('Unsupported cache type \'' + cacheType + '\'.');
  597. }
  598. reader.skip(16); // Scratch-pad space
  599. }
  600. };
  601. hdf5.DataObjectHeader = class {
  602. constructor(reader) {
  603. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#ObjectHeader
  604. this.attributes = [];
  605. this.links = [];
  606. this.continuations = [];
  607. reader.match('OHDR');
  608. const version = reader.byte();
  609. switch (version) {
  610. case 1: {
  611. reader.skip(1);
  612. const count = reader.uint16();
  613. reader.uint32();
  614. const objectHeaderSize = reader.uint32();
  615. reader.align(8);
  616. let end = reader.position + objectHeaderSize;
  617. for (let i = 0; i < count; i++) {
  618. const type = reader.uint16();
  619. const size = reader.uint16();
  620. const flags = reader.byte();
  621. reader.skip(3);
  622. reader.align(8);
  623. const next = this._readMessage(reader, type, size, flags);
  624. if ((!next || reader.position >= end) && this.continuations.length > 0) {
  625. const continuation = this.continuations.shift();
  626. reader = reader.at(continuation.offset);
  627. end = continuation.offset + continuation.length;
  628. }
  629. else {
  630. reader.align(8);
  631. }
  632. }
  633. break;
  634. }
  635. case 2: {
  636. const flags = reader.byte();
  637. if ((flags & 0x20) != 0) {
  638. reader.uint32(); // access time
  639. reader.uint32(); // modification time
  640. reader.uint32(); // change time
  641. reader.uint32(); // birth time
  642. }
  643. if ((flags & 0x10) != 0) {
  644. reader.uint16(); // max compact attributes
  645. reader.uint16(); // min compact attributes
  646. }
  647. const order = (flags & 0x04) != 0;
  648. const size = reader.uint(flags & 0x03);
  649. let next = true;
  650. let end = reader.position + size;
  651. while (next && reader.position < end) {
  652. const type = reader.byte();
  653. const size = reader.uint16();
  654. const flags = reader.byte();
  655. if (reader.position < end) {
  656. if (order) {
  657. reader.uint16(); // creation order
  658. }
  659. next = this._readMessage(reader, type, size, flags);
  660. }
  661. if ((!next || reader.position >= end) && this.continuations.length > 0) {
  662. const continuation = this.continuations.shift();
  663. reader = reader.at(continuation.offset);
  664. end = continuation.offset + continuation.length;
  665. if (!reader.match('OCHK')) {
  666. throw new hdf5.Error('Invalid continuation block signature.');
  667. }
  668. next = true;
  669. }
  670. }
  671. break;
  672. }
  673. default: {
  674. throw new hdf5.Error("Unsupported data object header version '" + version + "'.");
  675. }
  676. }
  677. }
  678. _readMessage(reader, type, size, flags) {
  679. switch(type) {
  680. case 0x0000: // NIL
  681. return false;
  682. case 0x0001: // Dataspace
  683. this.dataspace = (size != 4 || flags != 1) ? new hdf5.Dataspace(reader.clone()) : null;
  684. break;
  685. case 0x0002: // Link Info
  686. this.linkInfo = new hdf5.LinkInfo(reader.clone());
  687. break;
  688. case 0x0003: // Datatype
  689. this.datatype = new hdf5.Datatype(reader.clone());
  690. break;
  691. case 0x0004:
  692. case 0x0005: // Fill Value
  693. this.fillValue = new hdf5.FillValue(reader.clone(), type);
  694. break;
  695. case 0x0006: // Link
  696. this.links.push(new hdf5.Link(reader.clone()));
  697. break;
  698. case 0x0008: // Data Layout
  699. this.dataLayout = new hdf5.DataLayout(reader.clone());
  700. break;
  701. case 0x000A: // Group Info
  702. this.groupInfo = new hdf5.GroupInfo(reader.clone());
  703. break;
  704. case 0x000B: // Filter Pipeline
  705. this.filterPipeline = new hdf5.FilterPipeline(reader.clone());
  706. break;
  707. case 0x000C: // Attribute
  708. this.attributes.push(new hdf5.Attribute(reader.clone()));
  709. break;
  710. case 0x000D: // Object Comment Message
  711. this.comment = reader.string(-1, 'ascii');
  712. break;
  713. case 0x0010: // Object Header Continuation
  714. this.continuations.push(new hdf5.ObjectHeaderContinuation(reader.clone()));
  715. break;
  716. case 0x0011: // Symbol Table
  717. this.symbolTable = new hdf5.SymbolTable(reader.clone());
  718. break;
  719. case 0x000E: // Object Modification Time (Old)
  720. case 0x0012: // Object Modification Time
  721. this.objectModificationTime = new hdf5.ObjectModificationTime(reader.clone(), type);
  722. break;
  723. case 0x0015: // Attribute Info
  724. this.attributeInfo = new hdf5.AttributeInfo(reader.clone());
  725. break;
  726. default:
  727. throw new hdf5.Error('Unsupported message type \'' + type + '\'.');
  728. }
  729. reader.skip(size);
  730. return true;
  731. }
  732. };
  733. hdf5.Message = class {
  734. constructor(type, data, flags) {
  735. this._type = type;
  736. this._data = data;
  737. this._flags = flags;
  738. }
  739. };
  740. hdf5.Dataspace = class {
  741. constructor(reader) {
  742. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#DataspaceMessage
  743. this._sizes = [];
  744. const version = reader.byte();
  745. switch (version) {
  746. case 1:
  747. this._dimensions = reader.byte();
  748. this._flags = reader.byte();
  749. reader.skip(1);
  750. reader.skip(4);
  751. for (let i = 0; i < this._dimensions; i++) {
  752. this._sizes.push(reader.length());
  753. }
  754. if ((this._flags & 0x01) != 0) {
  755. this._maxSizes = [];
  756. for (let j = 0; j < this._dimensions; j++) {
  757. this._maxSizes.push(reader.length());
  758. if (this._maxSizes[j] != this._sizes[j]) {
  759. throw new hdf5.Error('Max size is not supported.');
  760. }
  761. }
  762. }
  763. if ((this._flags & 0x02) != 0) {
  764. throw new hdf5.Error('Permutation indices not supported.');
  765. }
  766. break;
  767. case 2:
  768. this._dimensions = reader.byte();
  769. this._flags = reader.byte();
  770. this._type = reader.byte(); // 0 scalar, 1 simple, 2 null
  771. for (let k = 0; k < this._dimensions; k++) {
  772. this._sizes.push(reader.length());
  773. }
  774. if ((this._flags & 0x01) != 0) {
  775. this._maxSizes = [];
  776. for (let l = 0; l < this._dimensions; l++) {
  777. this._maxSizes.push(reader.length());
  778. }
  779. }
  780. break;
  781. default:
  782. throw new hdf5.Error("Unsupported dataspace message version '" + version + "'.");
  783. }
  784. }
  785. get shape() {
  786. return this._sizes;
  787. }
  788. read(datatype, reader) {
  789. if (this._dimensions == 0) {
  790. return datatype.read(reader);
  791. }
  792. return this._readArray(datatype, reader, this._sizes, 0);
  793. }
  794. _readArray(datatype, reader, shape, dimension) {
  795. const array = [];
  796. const size = shape[dimension];
  797. if (dimension == shape.length - 1) {
  798. for (let i = 0; i < size; i++) {
  799. array.push(datatype.read(reader));
  800. }
  801. }
  802. else {
  803. for (let j = 0; j < size; j++) {
  804. array.push(this._readArray(datatype, reader, shape, dimension + 1));
  805. }
  806. }
  807. return array;
  808. }
  809. decode(datatype, data, globalHeap) {
  810. if (this._dimensions == 0) {
  811. return datatype.decode(data, globalHeap);
  812. }
  813. return this._decodeArray(datatype, data, globalHeap, this._sizes, 0);
  814. }
  815. _decodeArray(datatype, data, globalHeap, shape, dimension) {
  816. const size = shape[dimension];
  817. if (dimension == shape.length - 1) {
  818. for (let i = 0; i < size; i++) {
  819. data[i] = datatype.decode(data[i], globalHeap);
  820. }
  821. }
  822. else {
  823. for (let j = 0; j < size; j++) {
  824. data[j] = this._decodeArray(datatype, data[j], shape, dimension + 1);
  825. }
  826. }
  827. return data;
  828. }
  829. };
  830. hdf5.LinkInfo = class {
  831. constructor(reader) {
  832. const version = reader.byte();
  833. switch (version) {
  834. case 0: {
  835. const flags = reader.byte();
  836. if ((flags & 1) != 0) {
  837. this.maxCreationIndex = reader.uint64();
  838. }
  839. this.fractalHeapAddress = reader.offset();
  840. this.nameIndexTreeAddress = reader.offset();
  841. if ((flags & 2) != 0) {
  842. this.creationOrderIndexTreeAddress = reader.offset();
  843. }
  844. break;
  845. }
  846. default:
  847. throw new hdf5.Error("Unsupported link info message version '" + version + "'.");
  848. }
  849. }
  850. };
  851. hdf5.Datatype = class {
  852. constructor(reader) {
  853. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#DatatypeMessage
  854. const format = reader.byte();
  855. const version = format >> 4;
  856. this._class = format & 0xf;
  857. switch (version) {
  858. case 1:
  859. case 2: {
  860. this._flags = reader.byte() | reader.byte() << 8 | reader.byte() << 16;
  861. this._size = reader.uint32();
  862. switch (this._class) {
  863. case 0: { // fixed-Point
  864. this._bitOffset = reader.uint16();
  865. this._bitPrecision = reader.uint16();
  866. break;
  867. }
  868. case 8: { // enumerated
  869. this._base = new hdf5.Datatype(reader);
  870. this._names = [];
  871. this._values = [];
  872. const count = this._flags & 0xffff;
  873. for (let i = 0; i < count; i++) {
  874. const name = reader.clone().string(-1, 'ascii');
  875. this._names.push(name);
  876. reader.skip(Math.round((name.length + 1) / 8) * 8);
  877. }
  878. for (let i = 0; i < count; i++) {
  879. this._values.push(this._base.read(reader));
  880. }
  881. break;
  882. }
  883. default: {
  884. break;
  885. }
  886. }
  887. break;
  888. }
  889. default: {
  890. throw new hdf5.Error('Unsupported datatype version \'' + version + '\'.');
  891. }
  892. }
  893. }
  894. get type() {
  895. switch (this._class) {
  896. case 0: // fixed-point
  897. if ((this._flags & 0xfff6) === 0) {
  898. if ((this._flags && 0x08) !== 0) {
  899. switch (this._size) {
  900. case 1: return 'int8';
  901. case 2: return 'int16';
  902. case 4: return 'int32';
  903. case 8: return 'int64';
  904. default: throw new hdf5.Error("Unsupported int size '" + this._size + "'.");
  905. }
  906. }
  907. else {
  908. switch (this._size) {
  909. case 1: return 'uint8';
  910. case 2: return 'uint16';
  911. case 4: return 'uint32';
  912. case 8: return 'uint64';
  913. default: throw new hdf5.Error("Unsupported uint size '" + this._size + "'.");
  914. }
  915. }
  916. }
  917. break;
  918. case 1: // floating-point
  919. if (this._size == 2 && this._flags == 0x0f20) {
  920. return 'float16';
  921. }
  922. else if (this._size == 4 && this._flags == 0x1f20) {
  923. return 'float32';
  924. }
  925. else if (this._size == 8 && this._flags == 0x3f20) {
  926. return 'float64';
  927. }
  928. break;
  929. case 3: // string
  930. return 'string';
  931. case 5: // opaque
  932. return 'uint8[]';
  933. case 6: // compound
  934. return 'compound';
  935. case 8: // enumerated
  936. if (this._base.type === 'int8' &&
  937. this._names.length === 2 && this._names[0] === 'FALSE' && this._names[1] === 'TRUE' &&
  938. this._values.length === 2 && this._values[0] === 0 && this._values[1] === 1) {
  939. return 'boolean';
  940. }
  941. break;
  942. case 9: // variable-length
  943. if ((this._flags & 0x0f) == 1) { // type
  944. return 'char[]';
  945. }
  946. break;
  947. default:
  948. break;
  949. }
  950. throw new hdf5.Error("Unsupported datatype class '" + this._class + "'.");
  951. }
  952. get littleEndian() {
  953. switch (this._class) {
  954. case 0: // fixed-point
  955. case 1: // floating-point
  956. return (this.flags & 0x01) == 0;
  957. default:
  958. return true;
  959. }
  960. }
  961. read(reader) {
  962. switch (this._class) {
  963. case 0: // fixed-point
  964. if (this._size == 1) {
  965. return ((this._flags & 0x8) != 0) ? reader.int8() : reader.byte();
  966. }
  967. else if (this._size == 2) {
  968. return ((this._flags & 0x8) != 0) ? reader.int16() : reader.uint16();
  969. }
  970. else if (this._size == 4) {
  971. return ((this._flags & 0x8) != 0) ? reader.int32() : reader.uint32();
  972. }
  973. else if (this._size == 8) {
  974. return ((this._flags & 0x8) != 0) ? reader.int64() : reader.uint64();
  975. }
  976. throw new hdf5.Error('Unsupported fixed-point datatype.');
  977. case 1: // floating-point
  978. if (this._size == 2 && this._flags == 0x0f20) {
  979. return reader.float16();
  980. }
  981. else if (this._size == 4 && this._flags == 0x1f20) {
  982. return reader.float32();
  983. }
  984. else if (this._size == 8 && this._flags == 0x3f20) {
  985. return reader.float64();
  986. }
  987. throw new hdf5.Error('Unsupported floating-point datatype.');
  988. case 3: // string
  989. switch ((this._flags >> 8) & 0x0f) { // character set
  990. case 0:
  991. return reader.string(this._size, 'ascii');
  992. case 1:
  993. return reader.string(this._size, 'utf-8');
  994. default:
  995. throw new hdf5.Error('Unsupported character encoding.');
  996. }
  997. case 5: // opaque
  998. return reader.read(this._size);
  999. case 8: // enumerated
  1000. return reader.read(this._size);
  1001. case 9: // variable-length
  1002. return {
  1003. length: reader.uint32(),
  1004. globalHeapID: new hdf5.GlobalHeapID(reader)
  1005. };
  1006. default:
  1007. throw new hdf5.Error('Unsupported datatype class \'' + this._class + '\'.');
  1008. }
  1009. }
  1010. decode(data, globalHeap) {
  1011. switch (this._class) {
  1012. case 0: // fixed-point
  1013. return data;
  1014. case 1: // floating-point
  1015. return data;
  1016. case 3: // string
  1017. return data;
  1018. case 5: // opaque
  1019. return data;
  1020. case 8: // enumerated
  1021. return data;
  1022. case 9: { // variable-length
  1023. const globalHeapObject = globalHeap.get(data.globalHeapID);
  1024. if (globalHeapObject != null) {
  1025. const characterSet = (this._flags >> 8) & 0x0f;
  1026. const reader = globalHeapObject.reader();
  1027. switch (characterSet) {
  1028. case 0:
  1029. return reader.string(reader.length(), 'ascii');
  1030. case 1:
  1031. return reader.string(reader.length(), 'utf-8');
  1032. default:
  1033. throw new hdf5.Error('Unsupported character encoding.');
  1034. }
  1035. }
  1036. break;
  1037. }
  1038. default:
  1039. throw new hdf5.Error('Unsupported datatype class \'' + this._class + '\'.');
  1040. }
  1041. return null;
  1042. }
  1043. };
  1044. hdf5.FillValue = class {
  1045. constructor(reader, type) {
  1046. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#FillValueMessage
  1047. switch (type) {
  1048. case 0x0004: {
  1049. const size = reader.uint32();
  1050. this.data = reader.read(size);
  1051. break;
  1052. }
  1053. case 0x0005:
  1054. default: {
  1055. const version = reader.byte();
  1056. switch (version) {
  1057. case 1:
  1058. case 2: {
  1059. reader.byte();
  1060. reader.byte();
  1061. const valueDefined = reader.byte();
  1062. if (version === 1 || valueDefined === 1) {
  1063. const size = reader.uint32();
  1064. this.data = reader.read(size);
  1065. }
  1066. break;
  1067. }
  1068. case 3: {
  1069. const flags = reader.byte();
  1070. if ((flags & 0x20) !== 0) {
  1071. const size = reader.uint32();
  1072. this.data = reader.read(size);
  1073. }
  1074. break;
  1075. }
  1076. default:
  1077. throw new hdf5.Error('Unsupported fill value version \'' + version + '\'.');
  1078. }
  1079. break;
  1080. }
  1081. }
  1082. }
  1083. };
  1084. hdf5.Link = class {
  1085. constructor(reader) {
  1086. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#FillValueMessage
  1087. const version = reader.byte();
  1088. switch (version) {
  1089. case 1: {
  1090. const flags = reader.byte();
  1091. this.type = (flags & 0x08) != 0 ? reader.byte() : 0;
  1092. if ((flags & 0x04) != 0) {
  1093. this.creationOrder = reader.uint32();
  1094. }
  1095. const encoding = ((flags & 0x10) != 0 && reader.byte() == 1) ? 'utf-8' : 'ascii';
  1096. this.name = reader.string(reader.uint(flags & 0x03), encoding);
  1097. switch (this.type) {
  1098. case 0: // hard link
  1099. this.objectHeaderAddress = reader.offset();
  1100. break;
  1101. case 1: // soft link
  1102. break;
  1103. default:
  1104. throw new hdf5.Error('Unsupported link message type \'' + this.type + '\'.');
  1105. }
  1106. break;
  1107. }
  1108. default:
  1109. throw new hdf5.Error('Unsupported link message version \'' + version + '\'.');
  1110. }
  1111. }
  1112. };
  1113. hdf5.DataLayout = class {
  1114. constructor(reader) {
  1115. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#LayoutMessage
  1116. const version = reader.byte();
  1117. switch (version) {
  1118. case 1:
  1119. case 2: {
  1120. this.dimensionality = reader.byte();
  1121. this.layoutClass = reader.byte();
  1122. reader.skip(5);
  1123. switch (this.layoutClass) {
  1124. case 1:
  1125. this.address = reader.offset();
  1126. this.dimensionSizes = [];
  1127. for (let i = 0; i < this.dimensionality - 1; i++) {
  1128. this.dimensionSizes.push(reader.int32());
  1129. }
  1130. break;
  1131. case 2: // Chunked
  1132. this.address = reader.offset();
  1133. this.dimensionSizes = [];
  1134. for (let i = 0; i < this.dimensionality - 1; i++) {
  1135. this.dimensionSizes.push(reader.int32());
  1136. }
  1137. this.datasetElementSize = reader.int32();
  1138. break;
  1139. default:
  1140. throw new hdf5.Error('Unsupported data layout class \'' + this.layoutClass + '\'.');
  1141. }
  1142. break;
  1143. }
  1144. case 3: {
  1145. this.layoutClass = reader.byte();
  1146. switch (this.layoutClass) {
  1147. case 0: // Compact
  1148. this.size = reader.uint16();
  1149. reader.skip(2);
  1150. this.address = reader.position;
  1151. break;
  1152. case 1: // Contiguous
  1153. this.address = reader.offset();
  1154. this.size = reader.length();
  1155. break;
  1156. case 2: // Chunked
  1157. this.dimensionality = reader.byte();
  1158. this.address = reader.offset();
  1159. this.dimensionSizes = [];
  1160. for (let i = 0; i < this.dimensionality - 1; i++) {
  1161. this.dimensionSizes.push(reader.int32());
  1162. }
  1163. this.datasetElementSize = reader.int32();
  1164. break;
  1165. default:
  1166. throw new hdf5.Error('Unsupported data layout class \'' + this.layoutClass + '\'.');
  1167. }
  1168. break;
  1169. }
  1170. default: {
  1171. throw new hdf5.Error('Unsupported data layout version \'' + version + '\'.');
  1172. }
  1173. }
  1174. }
  1175. };
  1176. hdf5.GroupInfo = class {
  1177. constructor(reader) {
  1178. const version = reader.byte();
  1179. switch (version) {
  1180. case 0: {
  1181. const flags = reader.byte();
  1182. if ((flags & 0x01) != 0) {
  1183. this.maxCompactLinks = reader.uint16();
  1184. this.minDenseLinks = reader.uint16();
  1185. }
  1186. if ((flags & 0x02) != 0) {
  1187. this.estimatedEntriesNumber = reader.uint16();
  1188. this.estimatedLinkNameLengthEntires = reader.uint16();
  1189. }
  1190. break;
  1191. }
  1192. default:
  1193. throw new hdf5.Error('Unsupported group info version \'' + version + '\'.');
  1194. }
  1195. }
  1196. };
  1197. hdf5.FilterPipeline = class {
  1198. constructor(reader) {
  1199. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#FilterMessage
  1200. const version = reader.byte();
  1201. switch (version) {
  1202. case 1: {
  1203. this.filters = [];
  1204. const numberOfFilters = reader.byte();
  1205. reader.skip(2);
  1206. reader.skip(4);
  1207. for (let i = 0; i < numberOfFilters; i++) {
  1208. this.filters.push(new hdf5.Filter(reader));
  1209. reader.align(8);
  1210. }
  1211. break;
  1212. }
  1213. default:
  1214. throw new hdf5.Error('Unsupported filter pipeline message version \'' + version + '\'.');
  1215. }
  1216. }
  1217. };
  1218. hdf5.Filter = class {
  1219. constructor(reader) {
  1220. this.id = reader.int16();
  1221. const nameLength = reader.int16();
  1222. this.flags = reader.int16();
  1223. const clientDataSize = reader.int16();
  1224. this.name = reader.string(nameLength, 'ascii');
  1225. this.clientData = reader.read(clientDataSize * 4);
  1226. }
  1227. decode(data) {
  1228. switch (this.id) {
  1229. case 1: { // gzip
  1230. const archive = zip.Archive.open(data);
  1231. return archive.entries.get('').peek();
  1232. }
  1233. default: {
  1234. throw new hdf5.Error("Unsupported filter '" + this.name + "'.");
  1235. }
  1236. }
  1237. }
  1238. };
  1239. hdf5.Attribute = class {
  1240. constructor(reader) {
  1241. const version = reader.byte();
  1242. switch (version) {
  1243. case 1: {
  1244. reader.skip(1);
  1245. const nameSize = reader.uint16();
  1246. const datatypeSize = reader.uint16();
  1247. const dataspaceSize = reader.uint16();
  1248. this.name = reader.string(nameSize, 'utf-8');
  1249. reader.align(8);
  1250. this._datatype = new hdf5.Datatype(reader.clone());
  1251. reader.skip(datatypeSize);
  1252. reader.align(8);
  1253. this._dataspace = new hdf5.Dataspace(reader.clone());
  1254. reader.skip(dataspaceSize);
  1255. reader.align(8);
  1256. this._data = this._dataspace.read(this._datatype, reader);
  1257. break;
  1258. }
  1259. case 3: {
  1260. reader.byte();
  1261. const nameSize = reader.uint16();
  1262. const datatypeSize = reader.uint16();
  1263. const dataspaceSize = reader.uint16();
  1264. const encoding = reader.byte() == 1 ? 'utf-8' : 'ascii';
  1265. this.name = reader.string(nameSize, encoding);
  1266. this._datatype = new hdf5.Datatype(reader.clone());
  1267. reader.skip(datatypeSize);
  1268. this._dataspace = new hdf5.Dataspace(reader.clone());
  1269. reader.skip(dataspaceSize);
  1270. this._data = this._dataspace.read(this._datatype, reader);
  1271. break;
  1272. }
  1273. default:
  1274. throw new hdf5.Error('Unsupported attribute message version \'' + version + '\'.');
  1275. }
  1276. }
  1277. decodeValue(globalHeap) {
  1278. if (this._data) {
  1279. return this._dataspace.decode(this._datatype, this._data, globalHeap);
  1280. }
  1281. return null;
  1282. }
  1283. };
  1284. hdf5.ObjectHeaderContinuation = class {
  1285. constructor(reader) {
  1286. this.offset = reader.offset();
  1287. this.length = reader.length();
  1288. }
  1289. };
  1290. hdf5.SymbolTable = class {
  1291. constructor(reader) {
  1292. this.treeAddress = reader.offset(); // hdf5.Tree pointer
  1293. this.heapAddress = reader.offset(); // hdf5.Heap pointer
  1294. }
  1295. };
  1296. hdf5.ObjectModificationTime = class {
  1297. constructor(reader, type) {
  1298. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#ModificationTimeMessage
  1299. switch (type) {
  1300. case 0x000E: {
  1301. this.year = reader.uint32();
  1302. this.month = reader.uint16();
  1303. this.day = reader.uint16();
  1304. this.hour = reader.uint16();
  1305. this.minute = reader.uint16();
  1306. this.second = reader.uint16();
  1307. reader.skip(2);
  1308. break;
  1309. }
  1310. case 0x0012: {
  1311. const version = reader.byte();
  1312. reader.skip(3);
  1313. switch (version) {
  1314. case 1:
  1315. this.timestamp = reader.uint32();
  1316. break;
  1317. default:
  1318. throw new hdf5.Error('Unsupported object modification time message version \'' + version + '\'.');
  1319. }
  1320. break;
  1321. }
  1322. default: {
  1323. throw new hdf5.Error('Unsupported object modification time message type \'' + type + '\'.');
  1324. }
  1325. }
  1326. }
  1327. };
  1328. hdf5.AttributeInfo = class {
  1329. constructor(reader) {
  1330. const version = reader.byte();
  1331. switch (version) {
  1332. case 0: {
  1333. const flags = reader.byte();
  1334. if ((flags & 1) != 0) {
  1335. this.maxCreationIndex = reader.uint64();
  1336. }
  1337. this.fractalHeapAddress = reader.offset();
  1338. this.attributeNameTreeAddress = reader.offset();
  1339. if ((flags & 2) != 0) {
  1340. this.attributeCreationOrderTreeAddress = reader.offset();
  1341. }
  1342. break;
  1343. }
  1344. default:
  1345. throw new hdf5.Error('Unsupported attribute info message version \'' + version + '\'.');
  1346. }
  1347. }
  1348. };
  1349. hdf5.Tree = class {
  1350. constructor(reader, dimensionality) {
  1351. // https://support.hdfgroup.org/HDF5/doc/H5.format.html#V1Btrees
  1352. if (!reader.match('TREE')) {
  1353. throw new hdf5.Error("Not a valid 'TREE' block.");
  1354. }
  1355. this.type = reader.byte();
  1356. this.level = reader.byte();
  1357. const entries = reader.uint16();
  1358. reader.offset(); // address of left sibling
  1359. reader.offset(); // address of right sibling
  1360. this.nodes = [];
  1361. switch (this.type) {
  1362. case 0: { // Group nodes
  1363. for (let i = 0; i < entries; i++) {
  1364. reader.length();
  1365. const childPointer = reader.offset();
  1366. if (this.level == 0) {
  1367. const node = new hdf5.SymbolTableNode(reader.at(childPointer));
  1368. this.nodes.push(node);
  1369. }
  1370. else {
  1371. const tree = new hdf5.Tree(reader.at(childPointer));
  1372. this.nodes.push(...tree.nodes);
  1373. }
  1374. }
  1375. break;
  1376. }
  1377. case 1: { // Raw data chunk nodes
  1378. for (let i = 0; i < entries; i++) {
  1379. const size = reader.int32();
  1380. const filterMask = reader.int32();
  1381. const fields = [];
  1382. for (let j = 0; j < dimensionality; j++) {
  1383. fields.push(reader.uint64());
  1384. }
  1385. const childPointer = reader.offset();
  1386. if (this.level == 0) {
  1387. const data = reader.at(childPointer).read(size);
  1388. this.nodes.push({ data: data, fields: fields, filterMask: filterMask });
  1389. }
  1390. else {
  1391. const tree = new hdf5.Tree(reader.at(childPointer), dimensionality);
  1392. this.nodes.push(...tree.nodes);
  1393. }
  1394. }
  1395. break;
  1396. }
  1397. default: {
  1398. throw new hdf5.Error('Unsupported B-Tree node type \'' + this.type + '\'.');
  1399. }
  1400. }
  1401. }
  1402. };
  1403. hdf5.Heap = class {
  1404. constructor(reader) {
  1405. this._reader = reader;
  1406. if (!reader.match('HEAP')) {
  1407. throw new hdf5.Error("Not a valid 'HEAP' block.");
  1408. }
  1409. const version = reader.byte();
  1410. switch (version) {
  1411. case 0: {
  1412. reader.skip(3);
  1413. this._dataSize = reader.length();
  1414. this._offsetToHeadOfFreeList = reader.length();
  1415. this._dataAddress = reader.offset();
  1416. break;
  1417. }
  1418. default: {
  1419. throw new hdf5.Error('Unsupported Local Heap version \'' + version + '\'.');
  1420. }
  1421. }
  1422. }
  1423. getString(offset) {
  1424. const reader = this._reader.at(this._dataAddress + offset);
  1425. return reader.string(-1, 'utf-8');
  1426. }
  1427. };
  1428. hdf5.GlobalHeap = class {
  1429. constructor(reader) {
  1430. this._reader = reader;
  1431. this._collections = new Map();
  1432. }
  1433. get(globalHeapID) {
  1434. const address = globalHeapID.address;
  1435. if (!this._collections.has(address)) {
  1436. this._collections.set(address, new hdf5.GlobalHeapCollection(this._reader.at(address)));
  1437. }
  1438. return this._collections.get(globalHeapID.address).getObject(globalHeapID.objectIndex);
  1439. }
  1440. };
  1441. hdf5.GlobalHeapCollection = class {
  1442. constructor(reader) {
  1443. const startPosition = reader.position;
  1444. if (!reader.match('GCOL')) {
  1445. throw new hdf5.Error("Not a valid 'GCOL' block.");
  1446. }
  1447. const version = reader.byte();
  1448. switch (version) {
  1449. case 1: {
  1450. reader.skip(3);
  1451. this._objects = new Map();
  1452. const size = reader.length();
  1453. const endPosition = startPosition + size;
  1454. while (reader.position < endPosition) {
  1455. const index = reader.uint16();
  1456. if (index == 0) {
  1457. break;
  1458. }
  1459. this._objects.set(index, new hdf5.GlobalHeapObject(reader));
  1460. reader.align(8);
  1461. }
  1462. break;
  1463. }
  1464. default: {
  1465. throw new hdf5.Error('Unsupported global heap collection version \'' + version + '\'.');
  1466. }
  1467. }
  1468. }
  1469. getObject(objectIndex) {
  1470. if (this._objects.has(objectIndex)) {
  1471. return this._objects.get(objectIndex);
  1472. }
  1473. return null;
  1474. }
  1475. };
  1476. hdf5.GlobalHeapObject = class {
  1477. constructor(reader) {
  1478. reader.uint16();
  1479. reader.skip(4);
  1480. this._position = reader.position;
  1481. this._reader = reader;
  1482. const length = reader.length();
  1483. reader.skip(length);
  1484. }
  1485. reader() {
  1486. return this._reader.at(this._position);
  1487. }
  1488. };
  1489. hdf5.GlobalHeapID = class {
  1490. constructor(reader) {
  1491. this.address = reader.offset();
  1492. this.objectIndex = reader.uint32();
  1493. }
  1494. };
  1495. hdf5.Error = class extends Error {
  1496. constructor(message) {
  1497. super(message);
  1498. this.name = 'HDF5 Error';
  1499. }
  1500. };
  1501. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  1502. module.exports.File = hdf5.File;
  1503. }