hdf5.js 60 KB

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