omni-viewer-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +80 -0
- package/THIRD_PARTY_NOTICES.md +49 -0
- package/dist/assets/audio-engine/audio_engine.mjs +2 -0
- package/dist/assets/audio-engine/audio_engine.wasm +0 -0
- package/dist/assets/pdfjs/pdf.worker.min.mjs +21 -0
- package/dist/host/index.d.ts +133 -0
- package/dist/host/index.js +10 -0
- package/dist/i18n/catalog.en.d.ts +1 -0
- package/dist/i18n/catalog.en.js +411 -0
- package/dist/i18n/catalog.ko.d.ts +1 -0
- package/dist/i18n/catalog.ko.js +68 -0
- package/dist/i18n/index.d.ts +18 -0
- package/dist/i18n/index.js +41 -0
- package/dist/licenses/emscripten.txt +101 -0
- package/dist/licenses/kissfft.txt +28 -0
- package/dist/licenses/pdfjs-dist.txt +177 -0
- package/dist/parsers/a2l/index.d.ts +1 -0
- package/dist/parsers/a2l/index.js +1 -0
- package/dist/parsers/archive/index.d.ts +22 -0
- package/dist/parsers/archive/index.js +46 -0
- package/dist/parsers/archive/model.d.ts +29 -0
- package/dist/parsers/archive/model.js +1 -0
- package/dist/parsers/archive/worker.d.ts +21 -0
- package/dist/parsers/archive/worker.js +1 -0
- package/dist/parsers/arxml/index.d.ts +1 -0
- package/dist/parsers/arxml/index.js +1 -0
- package/dist/parsers/asc/index.d.ts +1 -0
- package/dist/parsers/asc/index.js +1 -0
- package/dist/parsers/audio/index.d.ts +9 -0
- package/dist/parsers/audio/index.js +46 -0
- package/dist/parsers/automotive/index.d.ts +33 -0
- package/dist/parsers/automotive/index.js +973 -0
- package/dist/parsers/blf/index.d.ts +1 -0
- package/dist/parsers/blf/index.js +1 -0
- package/dist/parsers/csv/delimiter.d.ts +31 -0
- package/dist/parsers/csv/delimiter.js +151 -0
- package/dist/parsers/csv/index.d.ts +30 -0
- package/dist/parsers/csv/index.js +108 -0
- package/dist/parsers/csv/parseText.d.ts +20 -0
- package/dist/parsers/csv/parseText.js +108 -0
- package/dist/parsers/csv/sort.d.ts +26 -0
- package/dist/parsers/csv/sort.js +224 -0
- package/dist/parsers/csv/statistics.d.ts +49 -0
- package/dist/parsers/csv/statistics.js +150 -0
- package/dist/parsers/dbc/index.d.ts +56 -0
- package/dist/parsers/dbc/index.js +94 -0
- package/dist/parsers/doc-binary/index.d.ts +35 -0
- package/dist/parsers/doc-binary/index.js +267 -0
- package/dist/parsers/doc-binary/vscode-parser.d.ts +142 -0
- package/dist/parsers/doc-binary/vscode-parser.js +3747 -0
- package/dist/parsers/excel/index.d.ts +17 -0
- package/dist/parsers/excel/index.js +406 -0
- package/dist/parsers/excel/model.d.ts +80 -0
- package/dist/parsers/excel/model.js +48 -0
- package/dist/parsers/excel/serialize.d.ts +3 -0
- package/dist/parsers/excel/serialize.js +78 -0
- package/dist/parsers/hdf5/index.d.ts +75 -0
- package/dist/parsers/hdf5/index.js +658 -0
- package/dist/parsers/hdf5/node.d.ts +21 -0
- package/dist/parsers/hdf5/node.js +95 -0
- package/dist/parsers/json/index.d.ts +15 -0
- package/dist/parsers/json/index.js +102 -0
- package/dist/parsers/json/model.d.ts +42 -0
- package/dist/parsers/json/model.js +140 -0
- package/dist/parsers/json/parse.d.ts +22 -0
- package/dist/parsers/json/parse.js +279 -0
- package/dist/parsers/json/tokenizer.d.ts +17 -0
- package/dist/parsers/json/tokenizer.js +175 -0
- package/dist/parsers/jsonl/index.d.ts +14 -0
- package/dist/parsers/jsonl/index.js +18 -0
- package/dist/parsers/jsonl/model.d.ts +23 -0
- package/dist/parsers/jsonl/model.js +1 -0
- package/dist/parsers/jsonl/parse.d.ts +7 -0
- package/dist/parsers/jsonl/parse.js +34 -0
- package/dist/parsers/markdown/index.d.ts +37 -0
- package/dist/parsers/markdown/index.js +59 -0
- package/dist/parsers/mat/index.d.ts +31 -0
- package/dist/parsers/mat/index.js +426 -0
- package/dist/parsers/mermaid/index.d.ts +5 -0
- package/dist/parsers/mermaid/index.js +3 -0
- package/dist/parsers/mf4/index.d.ts +1 -0
- package/dist/parsers/mf4/index.js +1 -0
- package/dist/parsers/parquet/index.d.ts +21 -0
- package/dist/parsers/parquet/index.js +68 -0
- package/dist/parsers/pcap/index.d.ts +1 -0
- package/dist/parsers/pcap/index.js +1 -0
- package/dist/parsers/pcapng/index.d.ts +1 -0
- package/dist/parsers/pcapng/index.js +1 -0
- package/dist/parsers/plantuml/index.d.ts +5 -0
- package/dist/parsers/plantuml/index.js +4 -0
- package/dist/parsers/ppt-binary/index.d.ts +5 -0
- package/dist/parsers/ppt-binary/index.js +62 -0
- package/dist/parsers/ppt-binary/pptBinaryBuildContext.d.ts +12 -0
- package/dist/parsers/ppt-binary/pptBinaryBuildContext.js +40 -0
- package/dist/parsers/ppt-binary/pptBinaryContainer.d.ts +4 -0
- package/dist/parsers/ppt-binary/pptBinaryContainer.js +174 -0
- package/dist/parsers/ppt-binary/pptBinaryTypes.d.ts +125 -0
- package/dist/parsers/ppt-binary/pptBinaryTypes.js +1 -0
- package/dist/parsers/ppt-binary/pptSlideLayouts.d.ts +9 -0
- package/dist/parsers/ppt-binary/pptSlideLayouts.js +510 -0
- package/dist/parsers/ppt-binary/vscode-parser.d.ts +116 -0
- package/dist/parsers/ppt-binary/vscode-parser.js +2853 -0
- package/dist/parsers/ppt-binary/vscode.d.ts +4 -0
- package/dist/parsers/ppt-binary/vscode.js +12 -0
- package/dist/parsers/pptx/index.d.ts +15 -0
- package/dist/parsers/pptx/index.js +88 -0
- package/dist/parsers/pptx/vscode-parser.d.ts +167 -0
- package/dist/parsers/pptx/vscode-parser.js +1503 -0
- package/dist/parsers/pptx/vscode.d.ts +4 -0
- package/dist/parsers/pptx/vscode.js +18 -0
- package/dist/parsers/pptx/zip-reader.d.ts +3 -0
- package/dist/parsers/pptx/zip-reader.js +20 -0
- package/dist/parsers/proto/index.d.ts +90 -0
- package/dist/parsers/proto/index.js +197 -0
- package/dist/parsers/psd/index.d.ts +16 -0
- package/dist/parsers/psd/index.js +10 -0
- package/dist/parsers/shapefile/dbf.d.ts +23 -0
- package/dist/parsers/shapefile/dbf.js +112 -0
- package/dist/parsers/shapefile/index.d.ts +44 -0
- package/dist/parsers/shapefile/index.js +119 -0
- package/dist/parsers/shapefile/reproject.d.ts +10 -0
- package/dist/parsers/shapefile/reproject.js +41 -0
- package/dist/parsers/slide-model.d.ts +106 -0
- package/dist/parsers/slide-model.js +1 -0
- package/dist/parsers/toml/index.d.ts +15 -0
- package/dist/parsers/toml/index.js +18 -0
- package/dist/parsers/toml/model.d.ts +15 -0
- package/dist/parsers/toml/model.js +1 -0
- package/dist/parsers/toml/parse.d.ts +16 -0
- package/dist/parsers/toml/parse.js +266 -0
- package/dist/parsers/types.d.ts +103 -0
- package/dist/parsers/types.js +106 -0
- package/dist/parsers/video/index.d.ts +6 -0
- package/dist/parsers/video/index.js +2 -0
- package/dist/parsers/yaml/index.d.ts +16 -0
- package/dist/parsers/yaml/index.js +58 -0
- package/dist/parsers/yaml/model.d.ts +22 -0
- package/dist/parsers/yaml/model.js +1 -0
- package/dist/parsers/yaml/self-loading.d.ts +3 -0
- package/dist/parsers/yaml/self-loading.js +19 -0
- package/dist/parsers/zip-scan.d.ts +8 -0
- package/dist/parsers/zip-scan.js +80 -0
- package/dist/registry/container.d.ts +17 -0
- package/dist/registry/container.js +49 -0
- package/dist/registry/index.d.ts +133 -0
- package/dist/registry/index.js +348 -0
- package/dist/registry/probe.d.ts +3 -0
- package/dist/registry/probe.js +177 -0
- package/dist/registry/sniff.d.ts +17 -0
- package/dist/registry/sniff.js +57 -0
- package/dist/styles/archive.css +10 -0
- package/dist/styles/audio.css +66 -0
- package/dist/styles/automotive.css +1 -0
- package/dist/styles/csv.css +262 -0
- package/dist/styles/excel.css +212 -0
- package/dist/styles/fallback.css +48 -0
- package/dist/styles/hdf5.css +3 -0
- package/dist/styles/hwp.css +12 -0
- package/dist/styles/image.css +115 -0
- package/dist/styles/json.css +342 -0
- package/dist/styles/jsonl.css +1 -0
- package/dist/styles/markdown.css +10 -0
- package/dist/styles/mat.css +2 -0
- package/dist/styles/mermaid.css +1 -0
- package/dist/styles/parquet.css +2 -0
- package/dist/styles/pdf.css +551 -0
- package/dist/styles/plantuml.css +1 -0
- package/dist/styles/ppt.css +1 -0
- package/dist/styles/proto.css +2 -0
- package/dist/styles/psd.css +1 -0
- package/dist/styles/shapefile.css +25 -0
- package/dist/styles/toml.css +1 -0
- package/dist/styles/video.css +74 -0
- package/dist/styles/word.css +685 -0
- package/dist/styles/yaml.css +1 -0
- package/dist/viewers/a2l/index.d.ts +15 -0
- package/dist/viewers/a2l/index.js +5 -0
- package/dist/viewers/archive/archive-preview-decoder.d.ts +4 -0
- package/dist/viewers/archive/archive-preview-decoder.js +223 -0
- package/dist/viewers/archive/controller.d.ts +24 -0
- package/dist/viewers/archive/controller.js +18 -0
- package/dist/viewers/archive/index.d.ts +25 -0
- package/dist/viewers/archive/index.js +230 -0
- package/dist/viewers/archive/preview.d.ts +2 -0
- package/dist/viewers/archive/preview.js +2 -0
- package/dist/viewers/archive/styles.d.ts +1 -0
- package/dist/viewers/archive/styles.js +11 -0
- package/dist/viewers/arxml/index.d.ts +15 -0
- package/dist/viewers/arxml/index.js +5 -0
- package/dist/viewers/asc/index.d.ts +15 -0
- package/dist/viewers/asc/index.js +5 -0
- package/dist/viewers/audio/controller.d.ts +33 -0
- package/dist/viewers/audio/controller.js +49 -0
- package/dist/viewers/audio/engine.d.ts +44 -0
- package/dist/viewers/audio/engine.js +108 -0
- package/dist/viewers/audio/index.d.ts +92 -0
- package/dist/viewers/audio/index.js +448 -0
- package/dist/viewers/audio/self-loading.d.ts +7 -0
- package/dist/viewers/audio/self-loading.js +42 -0
- package/dist/viewers/audio/styles.d.ts +1 -0
- package/dist/viewers/audio/styles.js +73 -0
- package/dist/viewers/automotive/index.d.ts +8 -0
- package/dist/viewers/automotive/index.js +152 -0
- package/dist/viewers/automotive/styles.d.ts +1 -0
- package/dist/viewers/automotive/styles.js +1 -0
- package/dist/viewers/avro/index.d.ts +15 -0
- package/dist/viewers/avro/index.js +5 -0
- package/dist/viewers/bag/index.d.ts +15 -0
- package/dist/viewers/bag/index.js +5 -0
- package/dist/viewers/blf/index.d.ts +15 -0
- package/dist/viewers/blf/index.js +5 -0
- package/dist/viewers/csv/controller.d.ts +126 -0
- package/dist/viewers/csv/controller.js +598 -0
- package/dist/viewers/csv/index.d.ts +48 -0
- package/dist/viewers/csv/index.js +846 -0
- package/dist/viewers/csv/styles.d.ts +1 -0
- package/dist/viewers/csv/styles.js +267 -0
- package/dist/viewers/db3/index.d.ts +15 -0
- package/dist/viewers/db3/index.js +5 -0
- package/dist/viewers/dbc/index.d.ts +15 -0
- package/dist/viewers/dbc/index.js +5 -0
- package/dist/viewers/diagram.d.ts +14 -0
- package/dist/viewers/diagram.js +95 -0
- package/dist/viewers/excel/controller.d.ts +108 -0
- package/dist/viewers/excel/controller.js +548 -0
- package/dist/viewers/excel/grid.d.ts +28 -0
- package/dist/viewers/excel/grid.js +99 -0
- package/dist/viewers/excel/index.d.ts +37 -0
- package/dist/viewers/excel/index.js +620 -0
- package/dist/viewers/excel/self-loading.d.ts +6 -0
- package/dist/viewers/excel/self-loading.js +13 -0
- package/dist/viewers/excel/styles.d.ts +1 -0
- package/dist/viewers/excel/styles.js +216 -0
- package/dist/viewers/fallback/index.d.ts +17 -0
- package/dist/viewers/fallback/index.js +130 -0
- package/dist/viewers/fallback/styles.d.ts +1 -0
- package/dist/viewers/fallback/styles.js +49 -0
- package/dist/viewers/hdf5/index.d.ts +19 -0
- package/dist/viewers/hdf5/index.js +170 -0
- package/dist/viewers/hdf5/styles.d.ts +1 -0
- package/dist/viewers/hdf5/styles.js +4 -0
- package/dist/viewers/hwp/controller.d.ts +19 -0
- package/dist/viewers/hwp/controller.js +22 -0
- package/dist/viewers/hwp/index.d.ts +39 -0
- package/dist/viewers/hwp/index.js +271 -0
- package/dist/viewers/hwp/self-loading.d.ts +2 -0
- package/dist/viewers/hwp/self-loading.js +6 -0
- package/dist/viewers/hwp/styles.d.ts +1 -0
- package/dist/viewers/hwp/styles.js +13 -0
- package/dist/viewers/image/controller.d.ts +210 -0
- package/dist/viewers/image/controller.js +336 -0
- package/dist/viewers/image/decode.d.ts +54 -0
- package/dist/viewers/image/decode.js +150 -0
- package/dist/viewers/image/index.d.ts +38 -0
- package/dist/viewers/image/index.js +749 -0
- package/dist/viewers/image/limits.d.ts +18 -0
- package/dist/viewers/image/limits.js +20 -0
- package/dist/viewers/image/styles.d.ts +1 -0
- package/dist/viewers/image/styles.js +119 -0
- package/dist/viewers/json/controller.d.ts +89 -0
- package/dist/viewers/json/controller.js +411 -0
- package/dist/viewers/json/index.d.ts +25 -0
- package/dist/viewers/json/index.js +503 -0
- package/dist/viewers/json/styles.d.ts +1 -0
- package/dist/viewers/json/styles.js +346 -0
- package/dist/viewers/json/transforms.d.ts +27 -0
- package/dist/viewers/json/transforms.js +253 -0
- package/dist/viewers/jsonl/controller.d.ts +48 -0
- package/dist/viewers/jsonl/controller.js +136 -0
- package/dist/viewers/jsonl/index.d.ts +22 -0
- package/dist/viewers/jsonl/index.js +184 -0
- package/dist/viewers/jsonl/styles.d.ts +1 -0
- package/dist/viewers/jsonl/styles.js +1 -0
- package/dist/viewers/markdown/controller.d.ts +50 -0
- package/dist/viewers/markdown/controller.js +62 -0
- package/dist/viewers/markdown/index.d.ts +64 -0
- package/dist/viewers/markdown/index.js +425 -0
- package/dist/viewers/markdown/math.d.ts +21 -0
- package/dist/viewers/markdown/math.js +100 -0
- package/dist/viewers/markdown/self-loading.d.ts +5 -0
- package/dist/viewers/markdown/self-loading.js +48 -0
- package/dist/viewers/markdown/styles.d.ts +1 -0
- package/dist/viewers/markdown/styles.js +11 -0
- package/dist/viewers/mat/index.d.ts +19 -0
- package/dist/viewers/mat/index.js +113 -0
- package/dist/viewers/mat/styles.d.ts +1 -0
- package/dist/viewers/mat/styles.js +3 -0
- package/dist/viewers/media.d.ts +9 -0
- package/dist/viewers/media.js +77 -0
- package/dist/viewers/mermaid/index.d.ts +17 -0
- package/dist/viewers/mermaid/index.js +6 -0
- package/dist/viewers/mermaid/self-loading.d.ts +2 -0
- package/dist/viewers/mermaid/self-loading.js +2 -0
- package/dist/viewers/mf4/index.d.ts +15 -0
- package/dist/viewers/mf4/index.js +5 -0
- package/dist/viewers/parquet/controller.d.ts +37 -0
- package/dist/viewers/parquet/controller.js +26 -0
- package/dist/viewers/parquet/index.d.ts +23 -0
- package/dist/viewers/parquet/index.js +134 -0
- package/dist/viewers/parquet/styles.d.ts +1 -0
- package/dist/viewers/parquet/styles.js +3 -0
- package/dist/viewers/pcap/index.d.ts +15 -0
- package/dist/viewers/pcap/index.js +5 -0
- package/dist/viewers/pcapng/index.d.ts +15 -0
- package/dist/viewers/pcapng/index.js +5 -0
- package/dist/viewers/pdf/controller.d.ts +120 -0
- package/dist/viewers/pdf/controller.js +191 -0
- package/dist/viewers/pdf/editing.d.ts +26 -0
- package/dist/viewers/pdf/editing.js +265 -0
- package/dist/viewers/pdf/index.d.ts +88 -0
- package/dist/viewers/pdf/index.js +1400 -0
- package/dist/viewers/pdf/self-loading.d.ts +6 -0
- package/dist/viewers/pdf/self-loading.js +14 -0
- package/dist/viewers/pdf/styles.d.ts +1 -0
- package/dist/viewers/pdf/styles.js +555 -0
- package/dist/viewers/plantuml/index.d.ts +17 -0
- package/dist/viewers/plantuml/index.js +6 -0
- package/dist/viewers/plantuml/self-loading.d.ts +2 -0
- package/dist/viewers/plantuml/self-loading.js +2 -0
- package/dist/viewers/ppt/chart.d.ts +8 -0
- package/dist/viewers/ppt/chart.js +83 -0
- package/dist/viewers/ppt/controller.d.ts +34 -0
- package/dist/viewers/ppt/controller.js +27 -0
- package/dist/viewers/ppt/index.d.ts +33 -0
- package/dist/viewers/ppt/index.js +101 -0
- package/dist/viewers/ppt/render.d.ts +5 -0
- package/dist/viewers/ppt/render.js +144 -0
- package/dist/viewers/ppt/self-loading.d.ts +9 -0
- package/dist/viewers/ppt/self-loading.js +4 -0
- package/dist/viewers/ppt/styles.d.ts +1 -0
- package/dist/viewers/ppt/styles.js +1 -0
- package/dist/viewers/proto/index.d.ts +17 -0
- package/dist/viewers/proto/index.js +170 -0
- package/dist/viewers/proto/styles.d.ts +1 -0
- package/dist/viewers/proto/styles.js +2 -0
- package/dist/viewers/psd/index.d.ts +27 -0
- package/dist/viewers/psd/index.js +39 -0
- package/dist/viewers/psd/self-loading.d.ts +10 -0
- package/dist/viewers/psd/self-loading.js +20 -0
- package/dist/viewers/psd/styles.d.ts +1 -0
- package/dist/viewers/psd/styles.js +1 -0
- package/dist/viewers/reqif/index.d.ts +15 -0
- package/dist/viewers/reqif/index.js +5 -0
- package/dist/viewers/shapefile/index.d.ts +35 -0
- package/dist/viewers/shapefile/index.js +336 -0
- package/dist/viewers/shapefile/self-loading.d.ts +7 -0
- package/dist/viewers/shapefile/self-loading.js +32 -0
- package/dist/viewers/shapefile/styles.d.ts +1 -0
- package/dist/viewers/shapefile/styles.js +28 -0
- package/dist/viewers/stp/index.d.ts +15 -0
- package/dist/viewers/stp/index.js +5 -0
- package/dist/viewers/structured-styles.d.ts +1 -0
- package/dist/viewers/structured-styles.js +1 -0
- package/dist/viewers/structured.d.ts +36 -0
- package/dist/viewers/structured.js +138 -0
- package/dist/viewers/toml/controller.d.ts +38 -0
- package/dist/viewers/toml/controller.js +61 -0
- package/dist/viewers/toml/index.d.ts +18 -0
- package/dist/viewers/toml/index.js +34 -0
- package/dist/viewers/types.d.ts +36 -0
- package/dist/viewers/types.js +9 -0
- package/dist/viewers/video/controller.d.ts +29 -0
- package/dist/viewers/video/controller.js +54 -0
- package/dist/viewers/video/index.d.ts +18 -0
- package/dist/viewers/video/index.js +305 -0
- package/dist/viewers/video/styles.d.ts +1 -0
- package/dist/viewers/video/styles.js +79 -0
- package/dist/viewers/word/controller.d.ts +19 -0
- package/dist/viewers/word/controller.js +22 -0
- package/dist/viewers/word/docx-preprocess.d.ts +63 -0
- package/dist/viewers/word/docx-preprocess.js +136 -0
- package/dist/viewers/word/index.d.ts +32 -0
- package/dist/viewers/word/index.js +187 -0
- package/dist/viewers/word/normalize-docx.d.ts +2 -0
- package/dist/viewers/word/normalize-docx.js +50 -0
- package/dist/viewers/word/paginate.d.ts +1 -0
- package/dist/viewers/word/paginate.js +1035 -0
- package/dist/viewers/word/self-loading.d.ts +2 -0
- package/dist/viewers/word/self-loading.js +7 -0
- package/dist/viewers/word/styles.d.ts +1 -0
- package/dist/viewers/word/styles.js +686 -0
- package/dist/viewers/yaml/controller.d.ts +42 -0
- package/dist/viewers/yaml/controller.js +41 -0
- package/dist/viewers/yaml/index.d.ts +21 -0
- package/dist/viewers/yaml/index.js +20 -0
- package/package.json +375 -0
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal HDF5 (.h5/.hdf5) structure reader.
|
|
3
|
+
*
|
|
4
|
+
* It validates the file signature, decodes the superblock and walks the object
|
|
5
|
+
* header hierarchy to enumerate the groups and datasets stored in the file.
|
|
6
|
+
*
|
|
7
|
+
* Only metadata (superblock, B-trees, local heaps, object headers) is read, and
|
|
8
|
+
* it is read on demand through an {@link Hdf5Reader} rather than by loading the
|
|
9
|
+
* whole file. This keeps inspection of multi-gigabyte/terabyte HDF5 files cheap
|
|
10
|
+
* because the large dataset payloads are never touched.
|
|
11
|
+
*
|
|
12
|
+
* Superblock versions 0 and 1 use the classic B-tree + local heap + symbol
|
|
13
|
+
* table layout and are traversed fully. Superblock versions 2 and 3 use version
|
|
14
|
+
* 2 object headers ("OHDR"); compact link messages are decoded, while groups
|
|
15
|
+
* that keep their links in a fractal heap (dense storage) are reported with a
|
|
16
|
+
* warning because that index is not walked here.
|
|
17
|
+
*/
|
|
18
|
+
const HDF5_SIGNATURE = [0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
19
|
+
const UNDEFINED_ADDRESS = -1;
|
|
20
|
+
const EMPTY = new Uint8Array(0);
|
|
21
|
+
const MAX_ENTRIES = 5000;
|
|
22
|
+
const MAX_DEPTH = 64;
|
|
23
|
+
const MAX_NODES = 20000;
|
|
24
|
+
// Object header message blocks are tiny; cap reads to guard against corrupt sizes.
|
|
25
|
+
const MAX_BLOCK_BYTES = 32 * 1024 * 1024;
|
|
26
|
+
/** Reader backed by an in-memory byte array. */
|
|
27
|
+
export class Hdf5Uint8ArrayReader {
|
|
28
|
+
buffer;
|
|
29
|
+
size;
|
|
30
|
+
constructor(buffer) {
|
|
31
|
+
this.buffer = buffer;
|
|
32
|
+
this.size = buffer.length;
|
|
33
|
+
}
|
|
34
|
+
read(offset, length) {
|
|
35
|
+
const start = Math.max(0, offset);
|
|
36
|
+
const end = Math.min(offset + length, this.size);
|
|
37
|
+
return end <= start ? EMPTY : this.buffer.subarray(start, end);
|
|
38
|
+
}
|
|
39
|
+
close() {
|
|
40
|
+
// Nothing to release for an in-memory buffer.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const DATATYPE_CLASS_NAMES = {
|
|
44
|
+
0: 'Integer',
|
|
45
|
+
1: 'Float',
|
|
46
|
+
2: 'Time',
|
|
47
|
+
3: 'String',
|
|
48
|
+
4: 'Bitfield',
|
|
49
|
+
5: 'Opaque',
|
|
50
|
+
6: 'Compound',
|
|
51
|
+
7: 'Reference',
|
|
52
|
+
8: 'Enum',
|
|
53
|
+
9: 'Variable-length',
|
|
54
|
+
10: 'Array'
|
|
55
|
+
};
|
|
56
|
+
// HDF5 object header message type identifiers.
|
|
57
|
+
const MSG_DATASPACE = 0x0001;
|
|
58
|
+
const MSG_DATATYPE = 0x0003;
|
|
59
|
+
const MSG_LINK = 0x0006;
|
|
60
|
+
const MSG_DATA_LAYOUT = 0x0008;
|
|
61
|
+
const MSG_CONTINUATION = 0x0010;
|
|
62
|
+
const MSG_SYMBOL_TABLE = 0x0011;
|
|
63
|
+
export class Hdf5Parser {
|
|
64
|
+
reader;
|
|
65
|
+
entries = [];
|
|
66
|
+
warnings = [];
|
|
67
|
+
superblock = null;
|
|
68
|
+
nodeBudget = MAX_NODES;
|
|
69
|
+
truncated = false;
|
|
70
|
+
constructor(reader) {
|
|
71
|
+
this.reader = reader;
|
|
72
|
+
}
|
|
73
|
+
/** Parses HDF5 metadata through a random-access reader without reading dataset payloads. */
|
|
74
|
+
static parseReader(reader, fileSize = formatFileSize(reader.size)) {
|
|
75
|
+
return new Hdf5Parser(reader).build(fileSize);
|
|
76
|
+
}
|
|
77
|
+
/** Parses an in-memory HDF5 byte array. */
|
|
78
|
+
static parse(buffer, fileSize = formatFileSize(buffer.byteLength)) {
|
|
79
|
+
return new Hdf5Parser(new Hdf5Uint8ArrayReader(buffer)).build(fileSize);
|
|
80
|
+
}
|
|
81
|
+
build(fileSize) {
|
|
82
|
+
const signatureOffset = this.findSignature();
|
|
83
|
+
if (signatureOffset < 0) {
|
|
84
|
+
return {
|
|
85
|
+
format: 'HDF5',
|
|
86
|
+
title: 'Hierarchical Data Format 5',
|
|
87
|
+
fileSize,
|
|
88
|
+
summary: [{ label: 'Signature', value: 'not found' }],
|
|
89
|
+
tables: [this.headerPreviewTable()],
|
|
90
|
+
warnings: ['The file does not start with the expected HDF5 signature (\\x89HDF\\r\\n\\x1a\\n).']
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
this.superblock = this.parseSuperblock(signatureOffset);
|
|
95
|
+
if (this.superblock.rootHeaderAddress !== UNDEFINED_ADDRESS) {
|
|
96
|
+
this.visitObject(this.superblock.rootHeaderAddress, '/', 0, new Set());
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
this.warnings.push(`Structure parsing stopped: ${error instanceof Error ? error.message : 'unknown error'}.`);
|
|
101
|
+
}
|
|
102
|
+
return this.toModel(fileSize);
|
|
103
|
+
}
|
|
104
|
+
toModel(fileSize) {
|
|
105
|
+
const sb = this.superblock;
|
|
106
|
+
const datasets = this.entries.filter(entry => entry.kind === 'Dataset');
|
|
107
|
+
const groups = this.entries.filter(entry => entry.kind === 'Group');
|
|
108
|
+
const summary = [
|
|
109
|
+
{ label: 'Superblock version', value: sb ? sb.version : '-' },
|
|
110
|
+
{ label: 'Groups', value: groups.length },
|
|
111
|
+
{ label: 'Datasets', value: datasets.length },
|
|
112
|
+
{ label: 'Size of offsets', value: sb ? sb.sizeOfOffsets : '-' }
|
|
113
|
+
];
|
|
114
|
+
const tables = [];
|
|
115
|
+
if (sb) {
|
|
116
|
+
tables.push({
|
|
117
|
+
title: 'Superblock',
|
|
118
|
+
headers: ['Field', 'Value'],
|
|
119
|
+
rows: [
|
|
120
|
+
['Version', sb.version],
|
|
121
|
+
['Size of offsets', sb.sizeOfOffsets],
|
|
122
|
+
['Size of lengths', sb.sizeOfLengths],
|
|
123
|
+
['Base address', formatAddress(sb.baseAddress)],
|
|
124
|
+
['End of file address', formatAddress(sb.endOfFile)],
|
|
125
|
+
['Root group header', formatAddress(sb.rootHeaderAddress)]
|
|
126
|
+
]
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
tables.push({
|
|
130
|
+
title: `Datasets (${datasets.length})`,
|
|
131
|
+
headers: ['Path', 'Shape', 'Type', 'Element bytes'],
|
|
132
|
+
rows: datasets.length > 0
|
|
133
|
+
? datasets.map(entry => [entry.path, entry.shape, entry.type, entry.elementSize])
|
|
134
|
+
: [['-', '-', 'No datasets were decoded from the file structure.', '-']]
|
|
135
|
+
});
|
|
136
|
+
tables.push({
|
|
137
|
+
title: `Groups (${groups.length})`,
|
|
138
|
+
headers: ['Path', 'Kind'],
|
|
139
|
+
rows: groups.length > 0
|
|
140
|
+
? groups.map(entry => [entry.path, entry.kind])
|
|
141
|
+
: [['/', 'Group']]
|
|
142
|
+
});
|
|
143
|
+
tables.push(this.headerPreviewTable());
|
|
144
|
+
const warnings = [...this.warnings];
|
|
145
|
+
if (this.truncated || this.nodeBudget <= 0) {
|
|
146
|
+
warnings.push('Traversal stopped early because the file structure exceeded the inspection limits.');
|
|
147
|
+
}
|
|
148
|
+
if (sb && (sb.version === 2 || sb.version === 3)) {
|
|
149
|
+
warnings.push('Superblock version 2/3 detected: links stored in dense (fractal heap) indexes are not enumerated.');
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
format: 'HDF5',
|
|
153
|
+
title: 'Hierarchical Data Format 5',
|
|
154
|
+
fileSize,
|
|
155
|
+
summary,
|
|
156
|
+
tables,
|
|
157
|
+
rawPreview: this.buildTreePreview(),
|
|
158
|
+
warnings
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
headerPreviewTable() {
|
|
162
|
+
const preview = this.reader.read(0, 256);
|
|
163
|
+
return {
|
|
164
|
+
title: 'Header preview',
|
|
165
|
+
headers: ['Offset', 'Hex', 'ASCII'],
|
|
166
|
+
rows: hexRows(preview, 0, preview.length)
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
buildTreePreview() {
|
|
170
|
+
if (this.entries.length === 0) {
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
const lines = this.entries
|
|
174
|
+
.slice()
|
|
175
|
+
.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)
|
|
176
|
+
.map(entry => {
|
|
177
|
+
const depth = entry.path === '/' ? 0 : entry.path.split('/').filter(Boolean).length;
|
|
178
|
+
const indent = ' '.repeat(Math.max(0, depth));
|
|
179
|
+
const name = entry.path === '/' ? '/' : entry.path.split('/').filter(Boolean).pop() ?? entry.path;
|
|
180
|
+
if (entry.kind === 'Dataset') {
|
|
181
|
+
return `${indent}${name} [${entry.shape}] ${entry.type}`;
|
|
182
|
+
}
|
|
183
|
+
return `${indent}${name}/`;
|
|
184
|
+
});
|
|
185
|
+
const text = lines.join('\n');
|
|
186
|
+
return text.length > 20000 ? `${text.slice(0, 20000)}\n\n... preview truncated ...` : text;
|
|
187
|
+
}
|
|
188
|
+
findSignature() {
|
|
189
|
+
let offset = 0;
|
|
190
|
+
while (offset + HDF5_SIGNATURE.length <= this.reader.size) {
|
|
191
|
+
const buf = this.reader.read(offset, HDF5_SIGNATURE.length);
|
|
192
|
+
if (buf.length === HDF5_SIGNATURE.length && HDF5_SIGNATURE.every((byte, index) => buf[index] === byte)) {
|
|
193
|
+
return offset;
|
|
194
|
+
}
|
|
195
|
+
// The signature may follow a user block placed at 0, 512, 1024, 2048, ...
|
|
196
|
+
offset = offset === 0 ? 512 : offset * 2;
|
|
197
|
+
}
|
|
198
|
+
return -1;
|
|
199
|
+
}
|
|
200
|
+
parseSuperblock(offset) {
|
|
201
|
+
const sb = this.reader.read(offset, 256);
|
|
202
|
+
if (sb.length < 12)
|
|
203
|
+
throw new Error('truncated superblock');
|
|
204
|
+
const version = byteAt(sb, 8);
|
|
205
|
+
if (version === 0 || version === 1) {
|
|
206
|
+
const sizeOfOffsets = byteAt(sb, 13);
|
|
207
|
+
const sizeOfLengths = byteAt(sb, 14);
|
|
208
|
+
this.validateAddressSizes(sizeOfOffsets, sizeOfLengths);
|
|
209
|
+
let p = 24;
|
|
210
|
+
if (version === 1) {
|
|
211
|
+
p += 4; // Indexed storage internal node K (2 bytes) + reserved (2 bytes)
|
|
212
|
+
}
|
|
213
|
+
const baseAddress = readOffsetBuf(sb, p, sizeOfOffsets);
|
|
214
|
+
p += sizeOfOffsets;
|
|
215
|
+
p += sizeOfOffsets; // Free-space info address
|
|
216
|
+
const endOfFile = readOffsetBuf(sb, p, sizeOfOffsets);
|
|
217
|
+
p += sizeOfOffsets;
|
|
218
|
+
p += sizeOfOffsets; // Driver information block address
|
|
219
|
+
p += sizeOfOffsets; // Root symbol table entry: link name offset
|
|
220
|
+
const rootHeaderAddress = readOffsetBuf(sb, p, sizeOfOffsets);
|
|
221
|
+
return { version, sizeOfOffsets, sizeOfLengths, baseAddress, endOfFile, rootHeaderAddress };
|
|
222
|
+
}
|
|
223
|
+
if (version === 2 || version === 3) {
|
|
224
|
+
const sizeOfOffsets = byteAt(sb, 9);
|
|
225
|
+
const sizeOfLengths = byteAt(sb, 10);
|
|
226
|
+
this.validateAddressSizes(sizeOfOffsets, sizeOfLengths);
|
|
227
|
+
let p = 12;
|
|
228
|
+
const baseAddress = readOffsetBuf(sb, p, sizeOfOffsets);
|
|
229
|
+
p += sizeOfOffsets;
|
|
230
|
+
p += sizeOfOffsets; // Superblock extension address
|
|
231
|
+
const endOfFile = readOffsetBuf(sb, p, sizeOfOffsets);
|
|
232
|
+
p += sizeOfOffsets;
|
|
233
|
+
const rootHeaderAddress = readOffsetBuf(sb, p, sizeOfOffsets);
|
|
234
|
+
return { version, sizeOfOffsets, sizeOfLengths, baseAddress, endOfFile, rootHeaderAddress };
|
|
235
|
+
}
|
|
236
|
+
throw new Error(`unsupported superblock version ${version}`);
|
|
237
|
+
}
|
|
238
|
+
validateAddressSizes(sizeOfOffsets, sizeOfLengths) {
|
|
239
|
+
if (sizeOfOffsets < 1 || sizeOfOffsets > 8 || sizeOfLengths < 1 || sizeOfLengths > 8) {
|
|
240
|
+
throw new Error('invalid HDF5 offset/length size');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
visitObject(headerAddress, path, depth, ancestors) {
|
|
244
|
+
if (headerAddress === UNDEFINED_ADDRESS || depth > MAX_DEPTH || this.entries.length >= MAX_ENTRIES) {
|
|
245
|
+
if (this.entries.length >= MAX_ENTRIES) {
|
|
246
|
+
this.truncated = true;
|
|
247
|
+
}
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (ancestors.has(headerAddress)) {
|
|
251
|
+
return; // Guard against cyclic links.
|
|
252
|
+
}
|
|
253
|
+
if (--this.nodeBudget <= 0) {
|
|
254
|
+
this.truncated = true;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const messages = this.readObjectHeader(headerAddress);
|
|
258
|
+
const dataspace = messages.find(message => message.type === MSG_DATASPACE);
|
|
259
|
+
const datatype = messages.find(message => message.type === MSG_DATATYPE);
|
|
260
|
+
const hasLayout = messages.some(message => message.type === MSG_DATA_LAYOUT);
|
|
261
|
+
const symbolTable = messages.find(message => message.type === MSG_SYMBOL_TABLE);
|
|
262
|
+
const linkMessages = messages.filter(message => message.type === MSG_LINK);
|
|
263
|
+
const isDataset = Boolean(datatype && (dataspace || hasLayout));
|
|
264
|
+
if (isDataset) {
|
|
265
|
+
this.entries.push({
|
|
266
|
+
path,
|
|
267
|
+
kind: 'Dataset',
|
|
268
|
+
shape: dataspace ? this.describeDataspace(dataspace.data) : 'scalar',
|
|
269
|
+
type: datatype ? this.describeDatatype(datatype.data) : 'unknown',
|
|
270
|
+
elementSize: datatype ? this.datatypeSize(datatype.data) : 0
|
|
271
|
+
});
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// Anything that is not a dataset is treated as a group node.
|
|
275
|
+
this.entries.push({ path, kind: 'Group', shape: '-', type: '-', elementSize: 0 });
|
|
276
|
+
const nextAncestors = new Set(ancestors).add(headerAddress);
|
|
277
|
+
const children = [];
|
|
278
|
+
if (symbolTable) {
|
|
279
|
+
children.push(...this.readSymbolTable(symbolTable.data));
|
|
280
|
+
}
|
|
281
|
+
for (const link of linkMessages) {
|
|
282
|
+
const decoded = this.decodeLinkMessage(link.data);
|
|
283
|
+
if (decoded) {
|
|
284
|
+
children.push(decoded);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
for (const child of children) {
|
|
288
|
+
const childPath = path === '/' ? `/${child.name}` : `${path}/${child.name}`;
|
|
289
|
+
this.visitObject(child.address, childPath, depth + 1, nextAncestors);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
readObjectHeader(address) {
|
|
293
|
+
const start = this.resolve(address);
|
|
294
|
+
if (start < 0 || start >= this.reader.size) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
if (this.matchesAscii(start, 'OHDR')) {
|
|
298
|
+
return this.readObjectHeaderV2(start);
|
|
299
|
+
}
|
|
300
|
+
return this.readObjectHeaderV1(start);
|
|
301
|
+
}
|
|
302
|
+
readObjectHeaderV1(start) {
|
|
303
|
+
const prefix = this.reader.read(start, 16);
|
|
304
|
+
if (prefix.length < 16) {
|
|
305
|
+
return [];
|
|
306
|
+
}
|
|
307
|
+
const totalMessages = readU16(prefix, 2);
|
|
308
|
+
// 16-byte prefix: version, reserved, message count, ref count, header size, padding.
|
|
309
|
+
const blocks = [{ offset: start + 16, length: readU32(prefix, 8) }];
|
|
310
|
+
return this.collectMessages(blocks, totalMessages + 256, { version2: false, creationOrderBytes: 0 });
|
|
311
|
+
}
|
|
312
|
+
readObjectHeaderV2(start) {
|
|
313
|
+
const head = this.reader.read(start, 64);
|
|
314
|
+
if (head.length < 6) {
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
const flags = byteAt(head, 5);
|
|
318
|
+
let p = 6;
|
|
319
|
+
if (flags & 0x20) {
|
|
320
|
+
p += 16; // Access, modification, change, and birth times.
|
|
321
|
+
}
|
|
322
|
+
if (flags & 0x10) {
|
|
323
|
+
p += 4; // Max compact / min dense attribute counts.
|
|
324
|
+
}
|
|
325
|
+
const chunkSizeBytes = 1 << (flags & 0x03);
|
|
326
|
+
const chunkSize = readLittleBuf(head, p, chunkSizeBytes);
|
|
327
|
+
p += chunkSizeBytes;
|
|
328
|
+
const trackOrder = (flags & 0x04) !== 0;
|
|
329
|
+
const blocks = [{ offset: start + p, length: chunkSize }];
|
|
330
|
+
return this.collectMessages(blocks, MAX_ENTRIES, { version2: true, creationOrderBytes: trackOrder ? 2 : 0 });
|
|
331
|
+
}
|
|
332
|
+
collectMessages(blocks, maxMessages, options) {
|
|
333
|
+
const messages = [];
|
|
334
|
+
const visited = new Set();
|
|
335
|
+
const headerSize = options.version2 ? 4 : 8;
|
|
336
|
+
let guard = 0;
|
|
337
|
+
while (blocks.length > 0 && messages.length < maxMessages && guard++ < 4096) {
|
|
338
|
+
const block = blocks.shift();
|
|
339
|
+
const region = this.reader.read(block.offset, Math.min(block.length, MAX_BLOCK_BYTES));
|
|
340
|
+
let p = 0;
|
|
341
|
+
while (p + headerSize <= region.length) {
|
|
342
|
+
const type = options.version2 ? byteAt(region, p) : readU16(region, p);
|
|
343
|
+
const size = options.version2 ? readU16(region, p + 1) : readU16(region, p + 2);
|
|
344
|
+
// Version 2 message headers add a creation-order field when the header tracks order.
|
|
345
|
+
const dataStart = p + headerSize + options.creationOrderBytes;
|
|
346
|
+
if (dataStart + size > region.length) {
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
const data = region.subarray(dataStart, dataStart + size);
|
|
350
|
+
if (type === MSG_CONTINUATION) {
|
|
351
|
+
this.queueContinuation(blocks, visited, data, options.version2);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
messages.push({ type, data });
|
|
355
|
+
}
|
|
356
|
+
p = dataStart + size;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return messages;
|
|
360
|
+
}
|
|
361
|
+
queueContinuation(blocks, visited, data, version2) {
|
|
362
|
+
const contAddress = readOffsetBuf(data, 0, this.sizeOfOffsets());
|
|
363
|
+
const contLength = readLittleBuf(data, this.sizeOfOffsets(), this.sizeOfLengths());
|
|
364
|
+
const contStart = this.resolve(contAddress);
|
|
365
|
+
if (contAddress === UNDEFINED_ADDRESS || contStart < 0 || visited.has(contStart)) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
visited.add(contStart);
|
|
369
|
+
if (version2) {
|
|
370
|
+
// Version 2 continuation blocks carry an "OCHK" signature (4 bytes) + trailing checksum (4 bytes).
|
|
371
|
+
if (!this.matchesAscii(contStart, 'OCHK')) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
blocks.push({ offset: contStart + 4, length: Math.max(0, contLength - 8) });
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
blocks.push({ offset: contStart, length: contLength });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
readSymbolTable(data) {
|
|
381
|
+
const sizeOfOffsets = this.sizeOfOffsets();
|
|
382
|
+
const btreeAddress = readOffsetBuf(data, 0, sizeOfOffsets);
|
|
383
|
+
const heapAddress = readOffsetBuf(data, sizeOfOffsets, sizeOfOffsets);
|
|
384
|
+
const heapDataStart = this.readLocalHeapDataStart(heapAddress);
|
|
385
|
+
if (heapDataStart < 0) {
|
|
386
|
+
return [];
|
|
387
|
+
}
|
|
388
|
+
const snodAddresses = [];
|
|
389
|
+
this.collectSymbolTableNodes(btreeAddress, snodAddresses, new Set());
|
|
390
|
+
const children = [];
|
|
391
|
+
for (const snodAddress of snodAddresses) {
|
|
392
|
+
children.push(...this.readSymbolTableNode(snodAddress, heapDataStart));
|
|
393
|
+
}
|
|
394
|
+
return children;
|
|
395
|
+
}
|
|
396
|
+
collectSymbolTableNodes(address, output, visited) {
|
|
397
|
+
const start = this.resolve(address);
|
|
398
|
+
if (address === UNDEFINED_ADDRESS || start < 0 || visited.has(start) || output.length > MAX_ENTRIES) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
visited.add(start);
|
|
402
|
+
if (!this.matchesAscii(start, 'TREE')) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const sizeOfOffsets = this.sizeOfOffsets();
|
|
406
|
+
const sizeOfLengths = this.sizeOfLengths();
|
|
407
|
+
// Header: signature(4) + node type(1) + level(1) + entries used(2) + left(off) + right(off).
|
|
408
|
+
const headerSize = 8 + sizeOfOffsets * 2;
|
|
409
|
+
const header = this.reader.read(start, headerSize);
|
|
410
|
+
if (header.length < 8) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const nodeLevel = byteAt(header, 5);
|
|
414
|
+
const entriesUsed = readU16(header, 6);
|
|
415
|
+
// Keys and children: key0, [child, key] * entriesUsed. Group-node keys are heap offsets.
|
|
416
|
+
const regionLength = sizeOfLengths + entriesUsed * (sizeOfOffsets + sizeOfLengths);
|
|
417
|
+
const region = this.reader.read(start + headerSize, regionLength);
|
|
418
|
+
let p = sizeOfLengths; // The first key precedes the first child pointer.
|
|
419
|
+
for (let i = 0; i < entriesUsed; i++) {
|
|
420
|
+
const childAddress = readOffsetBuf(region, p, sizeOfOffsets);
|
|
421
|
+
p += sizeOfOffsets + sizeOfLengths; // Child pointer + following key.
|
|
422
|
+
if (nodeLevel > 0) {
|
|
423
|
+
this.collectSymbolTableNodes(childAddress, output, visited);
|
|
424
|
+
}
|
|
425
|
+
else if (childAddress !== UNDEFINED_ADDRESS) {
|
|
426
|
+
output.push(childAddress);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
readSymbolTableNode(address, heapDataStart) {
|
|
431
|
+
const start = this.resolve(address);
|
|
432
|
+
if (start < 0 || !this.matchesAscii(start, 'SNOD')) {
|
|
433
|
+
return [];
|
|
434
|
+
}
|
|
435
|
+
const header = this.reader.read(start, 8);
|
|
436
|
+
if (header.length < 8) {
|
|
437
|
+
return [];
|
|
438
|
+
}
|
|
439
|
+
const count = readU16(header, 6);
|
|
440
|
+
const sizeOfOffsets = this.sizeOfOffsets();
|
|
441
|
+
const entrySize = sizeOfOffsets * 2 + 8 + 16; // Name offset + header address + cache type + reserved + scratch pad.
|
|
442
|
+
const region = this.reader.read(start + 8, count * entrySize);
|
|
443
|
+
const children = [];
|
|
444
|
+
for (let i = 0; i < count; i++) {
|
|
445
|
+
const base = i * entrySize;
|
|
446
|
+
const nameOffset = readOffsetBuf(region, base, sizeOfOffsets);
|
|
447
|
+
const headerAddress = readOffsetBuf(region, base + sizeOfOffsets, sizeOfOffsets);
|
|
448
|
+
const name = this.readHeapString(heapDataStart + nameOffset);
|
|
449
|
+
if (name && headerAddress !== UNDEFINED_ADDRESS) {
|
|
450
|
+
children.push({ name, address: headerAddress });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return children;
|
|
454
|
+
}
|
|
455
|
+
readLocalHeapDataStart(address) {
|
|
456
|
+
const start = this.resolve(address);
|
|
457
|
+
if (start < 0 || !this.matchesAscii(start, 'HEAP')) {
|
|
458
|
+
return -1;
|
|
459
|
+
}
|
|
460
|
+
const sizeOfOffsets = this.sizeOfOffsets();
|
|
461
|
+
const sizeOfLengths = this.sizeOfLengths();
|
|
462
|
+
// signature(4) + version(1) + reserved(3) + data segment size + free-list head + data segment address.
|
|
463
|
+
const buf = this.reader.read(start + 8 + sizeOfLengths * 2, sizeOfOffsets);
|
|
464
|
+
const dataSegmentAddress = readOffsetBuf(buf, 0, sizeOfOffsets);
|
|
465
|
+
return this.resolve(dataSegmentAddress);
|
|
466
|
+
}
|
|
467
|
+
decodeLinkMessage(data) {
|
|
468
|
+
if (data.length < 2 || data[0] !== 1) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
const flags = byteAt(data, 1);
|
|
472
|
+
let p = 2;
|
|
473
|
+
let linkType = 0;
|
|
474
|
+
if (flags & 0x08) {
|
|
475
|
+
linkType = byteAt(data, p);
|
|
476
|
+
p += 1;
|
|
477
|
+
}
|
|
478
|
+
if (flags & 0x04) {
|
|
479
|
+
p += 8; // Creation order.
|
|
480
|
+
}
|
|
481
|
+
if (flags & 0x10) {
|
|
482
|
+
p += 1; // Link name character set.
|
|
483
|
+
}
|
|
484
|
+
const lengthFieldSize = 1 << (flags & 0x03);
|
|
485
|
+
if (p + lengthFieldSize > data.length) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
const nameLength = readLittleBuf(data, p, lengthFieldSize);
|
|
489
|
+
p += lengthFieldSize;
|
|
490
|
+
if (p + nameLength > data.length) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
const name = decodeUtf8(data.subarray(p, p + nameLength));
|
|
494
|
+
p += nameLength;
|
|
495
|
+
if (linkType !== 0) {
|
|
496
|
+
return null; // Only hard links point straight at an object header.
|
|
497
|
+
}
|
|
498
|
+
const address = readOffsetBuf(data, p, this.sizeOfOffsets());
|
|
499
|
+
if (address === UNDEFINED_ADDRESS) {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
return { name, address };
|
|
503
|
+
}
|
|
504
|
+
describeDataspace(data) {
|
|
505
|
+
if (data.length < 2) {
|
|
506
|
+
return 'scalar';
|
|
507
|
+
}
|
|
508
|
+
const version = byteAt(data, 0);
|
|
509
|
+
const rank = byteAt(data, 1);
|
|
510
|
+
if (rank === 0) {
|
|
511
|
+
return 'scalar';
|
|
512
|
+
}
|
|
513
|
+
const sizeOfLengths = this.sizeOfLengths();
|
|
514
|
+
// v1: version, rank, flags, reserved, reserved(4). v2: version, rank, flags, type.
|
|
515
|
+
let p = version >= 2 ? 4 : 8;
|
|
516
|
+
const dims = [];
|
|
517
|
+
for (let i = 0; i < rank && p + sizeOfLengths <= data.length; i++) {
|
|
518
|
+
dims.push(readLittleBuf(data, p, sizeOfLengths));
|
|
519
|
+
p += sizeOfLengths;
|
|
520
|
+
}
|
|
521
|
+
return dims.length > 0 ? dims.join(' × ') : `rank ${rank}`;
|
|
522
|
+
}
|
|
523
|
+
describeDatatype(data) {
|
|
524
|
+
if (data.length < 1) {
|
|
525
|
+
return 'unknown';
|
|
526
|
+
}
|
|
527
|
+
const classId = byteAt(data, 0) & 0x0f;
|
|
528
|
+
const name = DATATYPE_CLASS_NAMES[classId] ?? `class ${classId}`;
|
|
529
|
+
const size = this.datatypeSize(data);
|
|
530
|
+
if (classId === 0 || classId === 1) {
|
|
531
|
+
return `${name}${size * 8}`;
|
|
532
|
+
}
|
|
533
|
+
return name;
|
|
534
|
+
}
|
|
535
|
+
datatypeSize(data) {
|
|
536
|
+
return data.length >= 8 ? readU32(data, 4) : 0;
|
|
537
|
+
}
|
|
538
|
+
readHeapString(offset) {
|
|
539
|
+
if (offset < 0 || offset >= this.reader.size) {
|
|
540
|
+
return '';
|
|
541
|
+
}
|
|
542
|
+
const buf = this.reader.read(offset, 1024);
|
|
543
|
+
let end = 0;
|
|
544
|
+
while (end < buf.length && buf[end] !== 0) {
|
|
545
|
+
end++;
|
|
546
|
+
}
|
|
547
|
+
return decodeUtf8(buf.subarray(0, end));
|
|
548
|
+
}
|
|
549
|
+
matchesAscii(offset, signature) {
|
|
550
|
+
if (offset < 0) {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
const buf = this.reader.read(offset, signature.length);
|
|
554
|
+
if (buf.length < signature.length) {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
for (let i = 0; i < signature.length; i++) {
|
|
558
|
+
if (buf[i] !== signature.charCodeAt(i)) {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
resolve(address) {
|
|
565
|
+
if (address === UNDEFINED_ADDRESS) {
|
|
566
|
+
return -1;
|
|
567
|
+
}
|
|
568
|
+
const base = this.superblock ? this.superblock.baseAddress : 0;
|
|
569
|
+
return address + (base > 0 ? base : 0);
|
|
570
|
+
}
|
|
571
|
+
sizeOfOffsets() {
|
|
572
|
+
return this.superblock ? this.superblock.sizeOfOffsets : 8;
|
|
573
|
+
}
|
|
574
|
+
sizeOfLengths() {
|
|
575
|
+
return this.superblock ? this.superblock.sizeOfLengths : 8;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function readOffsetBuf(buffer, offset, size) {
|
|
579
|
+
if (offset < 0 || offset + size > buffer.length) {
|
|
580
|
+
return UNDEFINED_ADDRESS;
|
|
581
|
+
}
|
|
582
|
+
let allOnes = true;
|
|
583
|
+
for (let i = 0; i < size; i++) {
|
|
584
|
+
if (buffer[offset + i] !== 0xff) {
|
|
585
|
+
allOnes = false;
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (allOnes) {
|
|
590
|
+
return UNDEFINED_ADDRESS;
|
|
591
|
+
}
|
|
592
|
+
return readLittleBuf(buffer, offset, size);
|
|
593
|
+
}
|
|
594
|
+
function readLittleBuf(buffer, offset, size) {
|
|
595
|
+
if (offset < 0 || offset + size > buffer.length) {
|
|
596
|
+
return 0;
|
|
597
|
+
}
|
|
598
|
+
if (size <= 6) {
|
|
599
|
+
let value = 0;
|
|
600
|
+
for (let i = size - 1; i >= 0; i--)
|
|
601
|
+
value = value * 256 + byteAt(buffer, offset + i);
|
|
602
|
+
return value;
|
|
603
|
+
}
|
|
604
|
+
let value = 0n;
|
|
605
|
+
for (let i = size - 1; i >= 0; i--) {
|
|
606
|
+
value = (value << 8n) | BigInt(byteAt(buffer, offset + i));
|
|
607
|
+
}
|
|
608
|
+
return value > BigInt(Number.MAX_SAFE_INTEGER) ? Number.MAX_SAFE_INTEGER : Number(value);
|
|
609
|
+
}
|
|
610
|
+
function byteAt(buffer, offset) {
|
|
611
|
+
return buffer[offset] ?? 0;
|
|
612
|
+
}
|
|
613
|
+
function readU16(buffer, offset) {
|
|
614
|
+
if (offset < 0 || offset + 2 > buffer.length)
|
|
615
|
+
return 0;
|
|
616
|
+
return (buffer[offset] ?? 0) | ((buffer[offset + 1] ?? 0) << 8);
|
|
617
|
+
}
|
|
618
|
+
function readU32(buffer, offset) {
|
|
619
|
+
if (offset < 0 || offset + 4 > buffer.length)
|
|
620
|
+
return 0;
|
|
621
|
+
return ((buffer[offset] ?? 0) | ((buffer[offset + 1] ?? 0) << 8) |
|
|
622
|
+
((buffer[offset + 2] ?? 0) << 16) | ((buffer[offset + 3] ?? 0) << 24)) >>> 0;
|
|
623
|
+
}
|
|
624
|
+
function decodeUtf8(buffer) {
|
|
625
|
+
return new TextDecoder('utf-8', { fatal: false }).decode(buffer);
|
|
626
|
+
}
|
|
627
|
+
export function formatFileSize(bytes) {
|
|
628
|
+
if (bytes < 1024)
|
|
629
|
+
return String(bytes) + ' bytes';
|
|
630
|
+
const units = ['KB', 'MB', 'GB', 'TB'];
|
|
631
|
+
let value = bytes / 1024;
|
|
632
|
+
let unit = units[0];
|
|
633
|
+
for (let i = 1; i < units.length && value >= 1024; i++) {
|
|
634
|
+
value /= 1024;
|
|
635
|
+
unit = units[i];
|
|
636
|
+
}
|
|
637
|
+
return value.toFixed(value >= 10 ? 1 : 2) + ' ' + unit;
|
|
638
|
+
}
|
|
639
|
+
function formatAddress(address) {
|
|
640
|
+
if (address === UNDEFINED_ADDRESS) {
|
|
641
|
+
return 'undefined';
|
|
642
|
+
}
|
|
643
|
+
return `0x${address.toString(16)}`;
|
|
644
|
+
}
|
|
645
|
+
function hexRows(buffer, start, end) {
|
|
646
|
+
const rows = [];
|
|
647
|
+
for (let offset = start; offset < end; offset += 16) {
|
|
648
|
+
const slice = buffer.subarray(offset, Math.min(offset + 16, end));
|
|
649
|
+
const hex = Array.from(slice).map(byte => byte.toString(16).padStart(2, '0')).join(' ');
|
|
650
|
+
const ascii = Array.from(slice).map(byte => (byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.')).join('');
|
|
651
|
+
rows.push([`0x${offset.toString(16).padStart(4, '0')}`, hex, ascii]);
|
|
652
|
+
}
|
|
653
|
+
return rows;
|
|
654
|
+
}
|
|
655
|
+
/** Convenience function for adapters and the built-in viewer. */
|
|
656
|
+
export function parseHdf5(input, fileSize = formatFileSize(input.byteLength)) {
|
|
657
|
+
return Hdf5Parser.parse(input, fileSize);
|
|
658
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Hdf5Document, type Hdf5Reader } from './index.js';
|
|
2
|
+
/**
|
|
3
|
+
* File-descriptor-backed random-access reader for Node hosts such as VS Code.
|
|
4
|
+
* A 4 MiB LRU page cache keeps metadata traversal cheap without loading HDF5
|
|
5
|
+
* dataset payloads or the complete file into memory.
|
|
6
|
+
*/
|
|
7
|
+
export declare class Hdf5FileReader implements Hdf5Reader {
|
|
8
|
+
private readonly fd;
|
|
9
|
+
readonly size: number;
|
|
10
|
+
private static readonly PAGE_BYTES;
|
|
11
|
+
private static readonly MAX_PAGES;
|
|
12
|
+
private readonly cache;
|
|
13
|
+
private closed;
|
|
14
|
+
private constructor();
|
|
15
|
+
static open(filePath: string): Hdf5FileReader;
|
|
16
|
+
read(offset: number, length: number): Uint8Array;
|
|
17
|
+
close(): void;
|
|
18
|
+
private getPage;
|
|
19
|
+
}
|
|
20
|
+
/** Parse an HDF5 file without loading dataset payloads into memory. */
|
|
21
|
+
export declare function parseHdf5File(filePath: string, fileSize?: string): Hdf5Document;
|