// HDF5 只读子集:MATLAB 7.3 `.mat` 解析用(见 MatH5.hpp 的说明)。 #include "MatH5.hpp" #include #include #include namespace jdi { namespace { constexpr std::uint16_t kMessageDataspace = 0x0001; constexpr std::uint16_t kMessageLink = 0x0006; constexpr std::uint16_t kMessageDatatype = 0x0003; constexpr std::uint16_t kMessageLayout = 0x0008; constexpr std::uint16_t kMessageFilter = 0x000B; constexpr std::uint16_t kMessageAttribute = 0x000C; constexpr std::uint16_t kMessageContinuation = 0x0010; constexpr std::uint16_t kMessageSymbolTable = 0x0011; std::uint64_t ReadLE(const std::uint8_t* data, std::size_t size) { std::uint64_t value = 0; for (std::size_t index = 0; index < size; ++index) { value |= static_cast(data[index]) << (8 * index); } return value; } // ================================================================== // 内置 inflate(RFC 1950 / 1951) // // ⚠️ **不能用 zlib(`-lz`)**:链接 libz.a 会让 Release 产物多出约 46KB, // 而这里只需要「解压 HDF5 分块」这一个功能,自带的紧凑实现只要几 KB。 // ⚠️ 更不能为了取文件大小包含 ``:它会连带拉进 locale / codecvt / // iostream / cp-demangle 等一整套 libstdc++ 代码,实测让产物从 787KB 涨到 1.7MB。 // ================================================================== /** @brief 位流读取器(LSB 优先,与 DEFLATE 一致)。 */ struct BitReader { const std::uint8_t* data = nullptr; std::size_t size = 0; std::size_t position = 0; std::uint32_t buffer = 0; int bits = 0; bool error = false; bool Fill(int count) { while (bits < count) { if (position >= size) { error = true; return false; } buffer |= static_cast(data[position++]) << bits; bits += 8; } return true; } std::uint32_t Read(int count) { if (count <= 0) { return 0; } if (!Fill(count)) { return 0; } const std::uint32_t value = buffer & ((1u << count) - 1u); buffer >>= count; bits -= count; return value; } void Align() { buffer = 0; bits = 0; } }; /** @brief 规范 Huffman 表(只存计数与按长度排好序的符号)。 */ struct HuffmanTable { std::uint16_t count[16] = {}; std::uint16_t symbol[320] = {}; }; bool BuildHuffman(HuffmanTable& table, const std::uint8_t* lengths, int total) { for (int index = 0; index < 16; ++index) { table.count[index] = 0; } for (int index = 0; index < total; ++index) { ++table.count[lengths[index]]; } if (table.count[0] == total) { return true; // 全部长度为 0:该表没有任何码(动态块里可能是距离表) } int left = 1; for (int length = 1; length < 16; ++length) { left <<= 1; left -= table.count[length]; if (left < 0) { return false; // 超订(over-subscribed) } } std::uint16_t offsets[16] = {}; offsets[1] = 0; for (int length = 1; length < 15; ++length) { offsets[length + 1] = static_cast(offsets[length] + table.count[length]); } for (int index = 0; index < total; ++index) { if (lengths[index] != 0) { table.symbol[offsets[lengths[index]]++] = static_cast(index); } } return true; } int DecodeSymbol(BitReader& reader, const HuffmanTable& table) { int code = 0; int first = 0; int index = 0; for (int length = 1; length < 16; ++length) { code |= static_cast(reader.Read(1)); if (reader.error) { return -1; } const int count = table.count[length]; if (code - first < count) { return table.symbol[index + (code - first)]; } index += count; first = (first + count) << 1; code <<= 1; } return -1; } const std::uint16_t kLengthBase[29] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258}; const std::uint8_t kLengthExtra[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; const std::uint16_t kDistanceBase[30] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; const std::uint8_t kDistanceExtra[30] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /** @brief 解压 zlib 流(含 2 字节头与 4 字节 Adler32 尾)。 */ bool Inflate(const std::uint8_t* source, std::size_t sourceSize, std::vector& out) { if (sourceSize < 6) { return false; } // zlib 头:CMF/FLG。只接受 deflate(CM = 8),且不接受预设字典。 const std::uint8_t cmf = source[0]; const std::uint8_t flg = source[1]; if ((cmf & 0x0F) != 8 || ((static_cast(cmf) * 256u + flg) % 31u) != 0 || (flg & 0x20) != 0) { return false; } BitReader reader; reader.data = source + 2; reader.size = sourceSize - 6; // 末尾 4 字节是 Adler32 out.clear(); HuffmanTable fixedLiterals; HuffmanTable fixedDistances; bool fixedReady = false; for (int block = 0; block < 100000; ++block) { const std::uint32_t final = reader.Read(1); const std::uint32_t type = reader.Read(2); if (reader.error) { return false; } if (type == 0) { // 存储块(未压缩):对齐后 4 字节长度 + 反码 + 原始字节。 reader.Align(); if (reader.position + 4 > reader.size) { return false; } const std::uint16_t length = static_cast( reader.data[reader.position] | (reader.data[reader.position + 1] << 8)); reader.position += 4; if (reader.position + length > reader.size) { return false; } out.insert(out.end(), reader.data + reader.position, reader.data + reader.position + length); reader.position += length; } else if (type == 1 || type == 2) { HuffmanTable literals; HuffmanTable distances; if (type == 1) { if (!fixedReady) { std::uint8_t lengths[288]; for (int index = 0; index < 144; ++index) { lengths[index] = 8; } for (int index = 144; index < 256; ++index) { lengths[index] = 9; } for (int index = 256; index < 280; ++index) { lengths[index] = 7; } for (int index = 280; index < 288; ++index) { lengths[index] = 8; } if (!BuildHuffman(fixedLiterals, lengths, 288)) { return false; } std::uint8_t distanceLengths[30]; for (int index = 0; index < 30; ++index) { distanceLengths[index] = 5; } if (!BuildHuffman(fixedDistances, distanceLengths, 30)) { return false; } fixedReady = true; } literals = fixedLiterals; distances = fixedDistances; } else { const int literalCount = static_cast(reader.Read(5)) + 257; const int distanceCount = static_cast(reader.Read(5)) + 1; const int codeCount = static_cast(reader.Read(4)) + 4; if (reader.error || literalCount > 286 || distanceCount > 30) { return false; } static const std::uint8_t kOrder[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; std::uint8_t codeLengths[19] = {}; for (int index = 0; index < codeCount; ++index) { codeLengths[kOrder[index]] = static_cast(reader.Read(3)); } HuffmanTable codeTable; if (reader.error || !BuildHuffman(codeTable, codeLengths, 19)) { return false; } std::uint8_t lengths[288 + 30] = {}; int produced = 0; while (produced < literalCount + distanceCount) { const int symbol = DecodeSymbol(reader, codeTable); if (symbol < 0) { return false; } if (symbol < 16) { lengths[produced++] = static_cast(symbol); continue; } int repeat = 0; std::uint8_t value = 0; if (symbol == 16) { if (produced == 0) { return false; } value = lengths[produced - 1]; repeat = 3 + static_cast(reader.Read(2)); } else if (symbol == 17) { repeat = 3 + static_cast(reader.Read(3)); } else { repeat = 11 + static_cast(reader.Read(7)); } if (reader.error || produced + repeat > literalCount + distanceCount) { return false; } for (int index = 0; index < repeat; ++index) { lengths[produced++] = value; } } if (!BuildHuffman(literals, lengths, literalCount) || !BuildHuffman(distances, lengths + literalCount, distanceCount)) { return false; } } for (int symbolIndex = 0; symbolIndex < 100000000; ++symbolIndex) { const int symbol = DecodeSymbol(reader, literals); if (symbol < 0) { return false; } if (symbol < 256) { out.push_back(static_cast(symbol)); continue; } if (symbol == 256) { break; // 块结束 } const int lengthCode = symbol - 257; if (lengthCode >= 29) { return false; } const std::size_t length = kLengthBase[lengthCode] + reader.Read(kLengthExtra[lengthCode]); const int distanceCode = DecodeSymbol(reader, distances); if (distanceCode < 0 || distanceCode >= 30 || reader.error) { return false; } const std::size_t distance = kDistanceBase[distanceCode] + reader.Read(kDistanceExtra[distanceCode]); if (reader.error || distance == 0 || distance > out.size()) { return false; } const std::size_t start = out.size() - distance; for (std::size_t index = 0; index < length; ++index) { out.push_back(out[start + index]); } } } else { return false; // 类型 3 非法 } if (final != 0) { return true; } } return false; } } // namespace std::uint8_t MatHdf5::U8(std::uint64_t offset) const { return (offset < m_data.size()) ? m_data[static_cast(offset)] : 0; } std::uint16_t MatHdf5::U16(std::uint64_t offset) const { return static_cast(U8(offset)) | (static_cast(U8(offset + 1)) << 8); } std::uint32_t MatHdf5::U32(std::uint64_t offset) const { return static_cast(U16(offset)) | (static_cast(U16(offset + 2)) << 16); } std::uint64_t MatHdf5::U64(std::uint64_t offset) const { return static_cast(U32(offset)) | (static_cast(U32(offset + 4)) << 32); } std::uint64_t MatHdf5::Addr(std::uint64_t offset) const { return (m_offsetSize == 8) ? U64(offset) : U32(offset); } std::uint64_t MatHdf5::Resolve(std::uint64_t address) const { if (address == 0 || address == 0xFFFFFFFFFFFFFFFFull) { return 0; } return address + m_base; } bool MatHdf5::Open(const std::wstring& path, std::wstring& error, const OpenProgress& progress) { error.clear(); Close(); // ⚠️ 取文件大小一律用 C 运行库(`_fseeki64` / `_ftelli64`),**不要用 ``**: // 后者会把 libstdc++ 的 locale / codecvt / iostream / cp-demangle 整套拉进产物, // 实测让 Release 二进制从 787KB 涨到 1.7MB(用户 2026-10-19 报告)。 FILE* file = nullptr; if (_wfopen_s(&file, path.c_str(), L"rb") != 0 || file == nullptr) { error = L"无法打开文件。"; return false; } if (_fseeki64(file, 0, SEEK_END) != 0) { std::fclose(file); error = L"无法读取文件大小。"; return false; } const long long size = _ftelli64(file); if (size <= 0 || _fseeki64(file, 0, SEEK_SET) != 0) { std::fclose(file); error = L"无法读取文件大小。"; return false; } m_data.resize(static_cast(size)); // 按块读入并回报进度(加载遮罩层的"已加载大小 / 总大小 / 百分比"由它驱动)。 constexpr std::size_t kChunk = 4u << 20; std::size_t offset = 0; while (offset < m_data.size()) { const std::size_t want = std::min(kChunk, m_data.size() - offset); const std::size_t got = std::fread(m_data.data() + offset, 1, want, file); offset += got; if (got != want) { break; } if (progress && !progress(offset, m_data.size())) { std::fclose(file); Close(); error.clear(); // 用户取消:不显示错误 return false; } } std::fclose(file); if (offset != m_data.size()) { error = L"读取文件失败。"; return false; } // 超级块:签名可能出现在 0、512、1024…(MATLAB 用 512 字节 user block)。 std::size_t signature = m_data.size(); for (std::size_t index = 0; index + 8 <= m_data.size() && index <= 4096; index += 512) { if (m_data[index] == 0x89 && m_data[index + 1] == 'H' && m_data[index + 2] == 'D' && m_data[index + 3] == 'F') { signature = index; break; } } if (signature == m_data.size()) { error = L"不是 HDF5(MATLAB 7.3)格式。"; return false; } const std::uint8_t version = U8(signature + 8); if (version == 0 || version == 1) { m_offsetSize = U8(signature + 13); m_lengthSize = U8(signature + 14); m_base = U64(signature + 24); const std::uint64_t rootEntry = signature + 24 + 32; m_root = Resolve(Addr(rootEntry + 8)); } else { m_offsetSize = U8(signature + 9); m_lengthSize = U8(signature + 10); m_base = U64(signature + 12); m_root = Resolve(U64(signature + 36)); } if (m_root == 0 || m_root >= m_data.size()) { error = L"HDF5 超级块损坏。"; return false; } return true; } void MatHdf5::Close() { m_data.clear(); m_base = 0; m_root = 0; m_heaps.clear(); } void MatHdf5::ReadMessages(std::uint64_t address, std::vector& messages) const { messages.clear(); if (address + 8 > m_data.size()) { return; } if (U8(address) == 1) { const std::uint16_t count = U16(address + 2); const std::uint32_t headerSize = U32(address + 8); // 先收集消息块:主块(长度 = 对象头里的 headerSize)+ 若干续块(续块里没有对象头前缀)。 // ⚠️ **主块长度必须用 headerSize 限定**:消息数超过主块容量时,其余消息在续块里, // 若在主块里继续按"消息数"往下读,就会读到块外的其它数据(字段全部解析失败)。 std::vector> blocks; blocks.emplace_back(address + 16, headerSize); std::size_t read = 0; for (std::size_t blockIndex = 0; blockIndex < blocks.size(); ++blockIndex) { std::uint64_t offset = blocks[blockIndex].first; const std::uint64_t end = (blocks[blockIndex].second > 0) ? (offset + blocks[blockIndex].second) : 0; while (read < count && offset + 8 <= m_data.size() && (end == 0 || offset + 8 <= end)) { Message message; message.type = U16(offset); const std::uint16_t messageSize = U16(offset + 2); offset += 8; if (offset + messageSize > m_data.size()) { break; } message.data.assign(m_data.begin() + static_cast(offset), m_data.begin() + static_cast(offset + messageSize)); offset += (messageSize + 7u) & ~static_cast(7u); ++read; if (message.type == kMessageContinuation && message.data.size() >= 16) { const std::uint64_t blockAddress = Resolve(ReadLE(message.data.data(), 8)); const std::uint64_t blockSize = ReadLE(message.data.data() + 8, 8); if (blockAddress != 0) { blocks.emplace_back(blockAddress, blockSize); } } messages.push_back(std::move(message)); } } } } void MatHdf5::WalkBtree(std::uint64_t node, std::uint64_t heapData, std::vector>& out) const { int guard = 0; while (node != 0 && node + 24 < m_data.size() && guard++ < 100000) { if (std::memcmp(&m_data[static_cast(node)], "TREE", 4) != 0) { return; } const std::uint8_t level = U8(node + 5); const std::uint16_t entries = U16(node + 6); std::uint64_t offset = node + 8 + 8 + 8; std::vector children; for (std::uint16_t index = 0; index < static_cast(entries + 1u); ++index) { offset += 8; // key(分组 B-tree 的 key 是堆偏移) if (index < entries) { children.push_back(Resolve(U64(offset))); offset += 8; } } for (const std::uint64_t child : children) { if (level > 0) { WalkBtree(child, heapData, out); } else { WalkSymbolNode(child, heapData, out); } } node = Resolve(U64(node + 8)); // 右兄弟 } } void MatHdf5::WalkSymbolNode(std::uint64_t node, std::uint64_t heapData, std::vector>& out) const { if (node == 0 || node + 8 > m_data.size() || std::memcmp(&m_data[static_cast(node)], "SNOD", 4) != 0 || heapData == 0) { return; } const std::uint16_t count = U16(node + 6); std::uint64_t offset = node + 8; for (std::uint16_t index = 0; index < count; ++index) { if (offset + 40 > m_data.size()) { return; } const std::uint64_t nameOffset = U64(offset); const std::uint64_t objectAddress = Resolve(U64(offset + 8)); offset += 40; if (objectAddress == 0 || heapData + nameOffset >= m_data.size()) { continue; } const char* name = reinterpret_cast(m_data.data() + heapData + nameOffset); out.emplace_back(std::string(name), objectAddress); } } std::vector> MatHdf5::Members(std::uint64_t address) const { std::vector> out; std::vector messages; ReadMessages(address, messages); for (const Message& message : messages) { if (message.type == kMessageSymbolTable && message.data.size() >= 16) { const std::uint64_t btree = Resolve(ReadLE(message.data.data(), 8)); const std::uint64_t heap = Resolve(ReadLE(message.data.data() + 8, 8)); std::uint64_t heapData = 0; if (heap + 32 <= m_data.size() && std::memcmp(&m_data[static_cast(heap)], "HEAP", 4) == 0) { heapData = Resolve(U64(heap + 24)); bool known = false; for (const auto& entry : m_heaps) { if (entry.first == heap) { known = true; break; } } if (!known) { m_heaps.emplace_back(heap, heapData); } } WalkBtree(btree, heapData, out); } else if (message.type == kMessageLink && message.data.size() >= 8) { std::size_t offset = 2; const std::uint8_t flags = message.data[1]; if (flags & 0x08) { offset += 8; } if (flags & 0x04) { offset += 8; } if (flags & 0x10) { ++offset; } if (offset >= message.data.size()) { continue; } const std::uint8_t lengthSize = message.data[offset++]; std::uint64_t nameLength = 0; for (std::uint8_t byte = 0; byte < lengthSize && offset < message.data.size(); ++byte) { nameLength |= static_cast(message.data[offset + byte]) << (8 * byte); } offset += lengthSize; if (offset + nameLength + 8 > message.data.size()) { continue; } std::string name(reinterpret_cast(message.data.data() + offset), static_cast(nameLength)); offset += static_cast(nameLength); const std::uint64_t target = Resolve(ReadLE(message.data.data() + offset, 8)); out.emplace_back(std::move(name), target); } } return out; } std::uint64_t MatHdf5::Member(std::uint64_t address, const std::string& name) const { for (const auto& entry : Members(address)) { if (entry.first == name) { return entry.second; } } return 0; } bool MatHdf5::IsDataset(std::uint64_t address) const { std::vector messages; ReadMessages(address, messages); for (const Message& message : messages) { if (message.type == kMessageDataspace) { return true; } } return false; } bool MatHdf5::ParseType(const std::uint8_t* data, std::size_t size, std::size_t offset, Type& out, std::size_t& consumed) const { if (offset + 8 > size) { return false; } out.version = data[offset] >> 4; out.cls = static_cast(data[offset] & 0x0F); const std::uint8_t bits0 = data[offset + 1]; out.size = ReadLE(data + offset + 4, 4); std::size_t cursor = offset + 8; if (out.cls == TypeClass::Integer) { out.bigEndian = (bits0 & 0x01) != 0; out.isSigned = (bits0 & 0x08) != 0; if (out.version >= 3) { cursor += 2; // bit offset cursor += 2; // precision } else { cursor += 4; } } else if (out.cls == TypeClass::Float) { out.bigEndian = (bits0 & 0x01) != 0; cursor += 12; } else if (out.cls == TypeClass::String) { // 无需额外属性 } else if (out.cls == TypeClass::Reference) { // 无需额外属性 } else if (out.cls == TypeClass::VariableLength) { out.isVariableString = (bits0 & 0x0F) == 1; std::size_t childConsumed = 0; Type child; if (ParseType(data, size, cursor, child, childConsumed)) { cursor += childConsumed; } } else if (out.cls == TypeClass::Compound) { const unsigned members = static_cast(bits0) | (static_cast(data[offset + 2]) << 8); for (unsigned index = 0; index < members; ++index) { if (cursor >= size) { break; } const std::size_t nameStart = cursor; while (cursor < size && data[cursor] != 0) { ++cursor; } std::string name(reinterpret_cast(data + nameStart), cursor - nameStart); ++cursor; // 结束符 cursor = (cursor + 7) & ~static_cast(7); if (out.version == 3) { // v3:先维度(1 字节 rank + 4 字节保留),再字节偏移(可变) } cursor += 4; // 字节偏移(v1/v2) std::size_t childConsumed = 0; Type child; if (ParseType(data, size, cursor, child, childConsumed)) { cursor += childConsumed; out.members.emplace_back(std::move(name), std::move(child)); } else { break; } } } else if (out.cls == TypeClass::Array) { if (cursor + 4 <= size) { const std::uint8_t rank = data[cursor]; cursor += 4; cursor += static_cast(rank) * 4; // 维度 cursor += static_cast(rank) * 4; // 排列 std::size_t childConsumed = 0; Type child; if (ParseType(data, size, cursor, child, childConsumed)) { cursor += childConsumed; } } } consumed = cursor - offset; return true; } bool MatHdf5::DecodeChunked(const Dataset& dataset, std::size_t elementSize, std::vector& out) const { if (dataset.chunkAddress == 0 || dataset.chunkDims.empty() || elementSize == 0 || dataset.dims.empty()) { return false; } // ⚠️ MATLAB 写出的分块维度可能与数据空间维度**维数不一致**(实测:1 维数据集的 // 分块维度是 `[N, 1]`)。把维度数组在**右侧补 1**(行优先的最快轴)对齐即可, // 二者的元素总数与遍历顺序都不变。 std::vector dims = dataset.dims; std::vector chunkDims = dataset.chunkDims; while (dims.size() < chunkDims.size()) { dims.push_back(1); } while (chunkDims.size() < dims.size()) { chunkDims.push_back(1); } const std::size_t rank = dims.size(); const std::uint64_t total = [&]() -> std::uint64_t { std::uint64_t value = 1; for (const std::size_t dim : dims) { value *= dim; } return value; }(); out.assign(static_cast(total) * elementSize, 0); std::vector chunk; std::vector inflated; // 遍历分块 B-tree:key = 分块大小 + 过滤器掩码 + 各维偏移。 std::vector stack{dataset.chunkAddress}; int guard = 0; while (!stack.empty() && guard++ < 1000000) { const std::uint64_t node = stack.back(); stack.pop_back(); if (node == 0 || node + 24 > m_data.size() || std::memcmp(&m_data[static_cast(node)], "TREE", 4) != 0) { continue; } const std::uint8_t level = U8(node + 5); const std::uint16_t entries = U16(node + 6); const std::size_t keySize = 8 + 8 * rank; std::uint64_t offset = node + 8 + 8 + 8; for (std::uint16_t index = 0; index < static_cast(entries + 1u); ++index) { const std::uint32_t chunkSize = (offset + 4 <= m_data.size()) ? U32(offset) : 0; std::vector chunkOffset(rank, 0); for (std::size_t axis = 0; axis < rank; ++axis) { chunkOffset[axis] = U64(offset + 8 + axis * 8); } offset += keySize; if (index < entries) { const std::uint64_t child = Resolve(U64(offset)); offset += 8; if (level > 0) { stack.push_back(child); } else if (child != 0 && chunkSize > 0) { chunk.assign(m_data.begin() + static_cast(child), m_data.begin() + static_cast(child + chunkSize)); const std::uint8_t* source = chunk.data(); std::size_t sourceSize = chunk.size(); if (dataset.deflate) { // 分块未压缩时大小 = 分块元素数 × 元素大小:此时不需要解压。 std::size_t expected = elementSize; for (const std::size_t dim : dataset.chunkDims) { expected *= dim; } if (chunk.size() != expected) { // 内置 inflate(不依赖 zlib,见文件头的说明)。 if (!Inflate(chunk.data(), chunk.size(), inflated)) { continue; } if (inflated.size() < expected) { inflated.resize(expected, 0); } source = inflated.data(); sourceSize = expected; } } // 把该块拷进输出缓冲区(按行优先展开)。 const std::size_t rowBytes = chunkDims[rank - 1] * elementSize; std::size_t rows = 1; for (std::size_t axis = 0; axis + 1 < rank; ++axis) { rows *= chunkDims[axis]; } for (std::size_t row = 0; row < rows; ++row) { std::vector coordinate(rank - 1, 0); std::size_t remainder = row; for (std::size_t axis = rank - 1; axis-- > 0;) { coordinate[axis] = remainder % chunkDims[axis]; remainder /= chunkDims[axis]; } std::uint64_t targetRow = 0; bool inside = true; std::uint64_t stride = 1; for (std::size_t axis = rank - 1; axis-- > 0;) { const std::uint64_t index = chunkOffset[axis] + coordinate[axis]; if (index >= dims[axis]) { inside = false; break; } targetRow += index * stride; stride *= dims[axis]; } if (!inside) { continue; } const std::size_t sourceOffset = row * rowBytes; const std::size_t targetOffset = static_cast(targetRow) * dims[rank - 1] * elementSize; if (sourceOffset + rowBytes > sourceSize || targetOffset + rowBytes > out.size()) { continue; } std::memcpy(out.data() + targetOffset, source + sourceOffset, rowBytes); } } } } } return true; } bool MatHdf5::ReadDataset(std::uint64_t address, Dataset& out) const { out = Dataset(); std::vector messages; ReadMessages(address, messages); bool haveSpace = false; bool haveType = false; std::size_t compactSize = 0; std::uint64_t dataAddress = 0; std::uint64_t dataSize = 0; for (const Message& message : messages) { if (message.type == kMessageDataspace && message.data.size() >= 8) { const std::uint8_t version = message.data[0]; const std::uint8_t rank = message.data[1]; std::size_t offset = (version == 1) ? 8 : 4; out.dims.assign(rank, 0); for (std::uint8_t axis = 0; axis < rank && offset + 8 <= message.data.size(); ++axis) { out.dims[axis] = static_cast(ReadLE(&message.data[offset], 8)); offset += 8; } haveSpace = true; } else if (message.type == kMessageDatatype) { std::size_t consumed = 0; haveType = ParseType(message.data.data(), message.data.size(), 0, out.type, consumed); } else if (message.type == kMessageLayout && message.data.size() >= 8) { const std::uint8_t version = message.data[0]; const std::uint8_t klass = message.data[1]; out.layout = static_cast(klass); if (klass == 0) { // 紧凑:数据就放在消息里 if (version == 3) { compactSize = static_cast(message.data[2]) | (static_cast(message.data[3]) << 8); } else { compactSize = static_cast(ReadLE(&message.data[2], 4)); } } else if (klass == 1) { dataAddress = Resolve(ReadLE(&message.data[2], 8)); dataSize = ReadLE(&message.data[10], 8); } else if (klass == 2) { if (version == 3) { const std::uint8_t rank = message.data[2]; out.chunkAddress = Resolve(ReadLE(&message.data[3], 8)); out.chunkDims.assign(rank, 0); for (std::uint8_t axis = 0; axis < rank; ++axis) { out.chunkDims[axis] = ReadLE(&message.data[11 + axis * 4], 4); } } else { const std::uint8_t rank = message.data[2]; out.chunkAddress = Resolve(ReadLE(&message.data[3], 8)); out.chunkDims.assign(rank, 0); for (std::uint8_t axis = 0; axis < rank; ++axis) { out.chunkDims[axis] = ReadLE(&message.data[11 + axis * 4], 4); } } } } else if (message.type == kMessageFilter && message.data.size() >= 2) { const int count = message.data[1]; std::size_t offset = 8; for (int index = 0; index < count && offset + 8 <= message.data.size(); ++index) { const std::uint16_t id = static_cast(message.data[offset]) | (static_cast(message.data[offset + 1]) << 8); const std::uint16_t nameLength = static_cast(message.data[offset + 2]) | (static_cast(message.data[offset + 3]) << 8); const std::uint16_t values = static_cast(message.data[offset + 6]) | (static_cast(message.data[offset + 7]) << 8); offset += 8; offset += (nameLength + 7) & ~static_cast(7); offset += static_cast(values) * 4; if (id == 1) { out.deflate = true; } } } } if (!haveSpace || !haveType || out.type.size == 0) { return false; } const std::size_t elementSize = out.type.size; if (out.layout == LayoutClass::Compact) { // 紧凑数据在消息里,重新扫描一次取出字节。 for (const Message& message : messages) { if (message.type == kMessageLayout) { const std::size_t offset = (message.data[0] == 3) ? 4 : 8; if (offset + compactSize <= message.data.size()) { out.data.assign(message.data.begin() + static_cast(offset), message.data.begin() + static_cast(offset + compactSize)); } } } } else if (out.layout == LayoutClass::Contiguous) { if (dataAddress != 0 && dataAddress + dataSize <= m_data.size()) { out.data.assign(m_data.begin() + static_cast(dataAddress), m_data.begin() + static_cast(dataAddress + dataSize)); } } else if (out.layout == LayoutClass::Chunked) { if (!DecodeChunked(out, elementSize, out.data)) { return false; } } out.ok = true; return true; } std::string MatHdf5::AttributeString(std::uint64_t address, const char* name) const { std::vector messages; ReadMessages(address, messages); for (const Message& message : messages) { if (message.type != kMessageAttribute || message.data.size() < 8) { continue; } const std::uint8_t version = message.data[0]; const std::size_t headerSize = (version == 1) ? 8 : (version == 2 ? 8 : 9); const std::uint16_t nameSize = static_cast(message.data[2]) | (static_cast(message.data[3]) << 8); const std::uint16_t typeSize = static_cast(message.data[4]) | (static_cast(message.data[5]) << 8); const std::uint16_t spaceSize = static_cast(message.data[6]) | (static_cast(message.data[7]) << 8); std::size_t offset = headerSize; if (offset + nameSize > message.data.size()) { continue; } std::string attributeName(reinterpret_cast(message.data.data() + offset), nameSize); while (!attributeName.empty() && attributeName.back() == '\0') { attributeName.pop_back(); } offset += (nameSize + 7) & ~static_cast(7); const std::size_t typeOffset = offset; offset += (typeSize + 7) & ~static_cast(7); const std::size_t spaceOffset = offset; offset += (spaceSize + 7) & ~static_cast(7); if (attributeName != name) { continue; } if (offset > message.data.size()) { return std::string(); } Type type; std::size_t consumed = 0; if (!ParseType(message.data.data(), message.data.size(), typeOffset, type, consumed)) { return std::string(); } std::size_t count = 1; if (spaceOffset + 4 <= message.data.size()) { const std::uint8_t spaceVersion = message.data[spaceOffset]; const std::uint8_t rank = message.data[spaceOffset + 1]; std::size_t dimOffset = spaceOffset + ((spaceVersion == 1) ? 8 : 4); count = 1; for (std::uint8_t axis = 0; axis < rank && dimOffset + 8 <= message.data.size(); ++axis) { count *= static_cast(ReadLE(&message.data[dimOffset], 8)); dimOffset += 8; } } std::string text; if (type.cls == TypeClass::String || type.cls == TypeClass::Integer) { text.assign(reinterpret_cast(message.data.data() + offset), std::min(message.data.size() - offset, std::max(count, 1))); while (!text.empty() && text.back() == '\0') { text.pop_back(); } } return text; } return std::string(); } bool MatHdf5::ReadText(std::uint64_t address, std::wstring& out) const { out.clear(); Dataset dataset; if (!ReadDataset(address, dataset)) { return false; } if (dataset.type.cls == TypeClass::String) { out.assign(dataset.data.begin(), dataset.data.end()); while (!out.empty() && out.back() == L'\0') { out.pop_back(); } return true; } if (dataset.type.size == 2) { for (std::size_t index = 0; index + 1 < dataset.data.size(); index += 2) { const wchar_t character = static_cast( static_cast(dataset.data[index]) | (static_cast(dataset.data[index + 1]) << 8)); if (character != 0) { out.push_back(character); } } return true; } if (dataset.type.size == 1) { for (const std::uint8_t byte : dataset.data) { if (byte != 0) { out.push_back(static_cast(byte)); } } return true; } return false; } bool MatHdf5::ReadNumbers(std::uint64_t address, std::vector& out) const { out.clear(); Dataset dataset; if (!ReadDataset(address, dataset) || !dataset.ok) { return false; } const std::size_t elementSize = dataset.type.size; if (elementSize == 0 || dataset.data.size() % elementSize != 0) { return false; } const std::size_t count = dataset.data.size() / elementSize; out.resize(count); const bool integer = (dataset.type.cls == TypeClass::Integer); const bool floating = (dataset.type.cls == TypeClass::Float); if (!integer && !floating) { return false; } for (std::size_t index = 0; index < count; ++index) { const std::uint8_t* source = dataset.data.data() + index * elementSize; if (floating && elementSize == 8) { double value = 0.0; std::memcpy(&value, source, sizeof(value)); out[index] = value; } else if (floating && elementSize == 4) { float value = 0.0f; std::memcpy(&value, source, sizeof(value)); out[index] = static_cast(value); } else if (integer && dataset.type.isSigned) { std::int64_t value = 0; for (std::size_t byte = 0; byte < elementSize; ++byte) { value |= static_cast(source[byte]) << (8 * byte); } // 符号扩展 const int bits = static_cast(elementSize) * 8; if (bits < 64 && (value & (1ll << (bits - 1))) != 0) { value |= ~((1ll << bits) - 1); } out[index] = static_cast(value); } else if (integer) { std::uint64_t value = 0; for (std::size_t byte = 0; byte < elementSize; ++byte) { value |= static_cast(source[byte]) << (8 * byte); } out[index] = static_cast(value); } } return true; } bool MatHdf5::ReadReferences(std::uint64_t address, std::vector& out) const { out.clear(); Dataset dataset; if (!ReadDataset(address, dataset) || !dataset.ok || dataset.type.cls != TypeClass::Reference) { return false; } const std::size_t count = dataset.data.size() / 8; out.reserve(count); for (std::size_t index = 0; index < count; ++index) { const std::uint64_t raw = ReadLE(dataset.data.data() + index * 8, 8); out.push_back(Resolve(raw)); } return true; } } // namespace jdi