Files
Just-DataInspector/Utilities/Src/MatH5.cpp
T
DeepSeek-V4.1-Flash 5b24dacee0 悬停高亮恢复旧版外延观感 + 支持 MATLAB .mat(v7.3 / HDF5,嵌套分组)
一、悬停高亮「几乎看不到、只是曲线换了颜色」(对比提交 f845616 的旧版观感)
根因:调色板第 1 色就是主题强调色(0xFF268BD2),而上一轮为提速把稠密信号的光晕改成
「按包络带原始几何 FillPath」——与曲线**逐像素同形**,于是曲线上的那层半透明强调色
完全看不出变化;旧版观感来自「lineWidth+4 的描边笔向两侧各外扩 2~3px」。
修法(PlotViewPaint.cpp::PaintSignalHaloFor):
1) 逐列成对填四边形,上下左右都外扩 grow = (lineWidth + 4) * scale / 2
   (竖向跃变处旧版 5px 笔涂出的正是那条整高色带);
2) 不能把整条包络拼成一个多边形:满幅方波相邻列区间互相交叉,缠绕填充规则会在交叠处
   相互抵消 ⇒ 填充变成一条条竖缝(实测只覆盖约一半带宽);
3) alpha 从 0x55 调回 0x66(与旧版描边笔一致)。
代价仍只在「换信号/换视界/改样式」时付一次,此后每帧只 AlphaBlend。
三维对照(新增 haloshot 探针 + f845616 工作树构建):变化像素 旧 73726 / 上一轮 13529 /
本轮 74916,目视与旧版一致(跃变处的整高色带恢复)。

二、MATLAB .mat(v7.3 = HDF5)支持,含嵌套分组
新增只读 HDF5 子集(不引第三方库):Utilities/Inc/MatH5.hpp + Utilities/Src/MatH5.cpp
- 超级块 v0/v2;对象头 v1(含续块,主块长度以 headerSize 为准);符号表(B-tree v1 +
  本地堆)与 link 消息;数据空间 v1/v2;数据类型 v1;数据布局 v3(紧凑/连续/分块);
  分块数据集支持 deflate(zlib);地址一律相对基地址解析。
新增 MATLAB 语义层:Utilities/Inc/MatLoader.hpp + Utilities/Src/MatLoader.cpp
1) Simulink SDI / SimulationData.Dataset(识别 #refs# + #sigstream#):
   · #refs# 元素有 ElementType(signal/dataset)、Name、Values;
   · timeseries 的 Values/DataR2、Values/TimeR2 是 #sigstream#/<n> 的流编号;
     #data# 是 uint8 字节流,按 8 字节一组解释为 double(与 #length#/#sampleSize# 一致);
   · 子数据集的 Values 是对象引用数组(子元素)⇒ 还原嵌套树;
   · 分组名 = 从根到信号的各级数据集名用 - 连接(如 A/A1/XYZ → 组 A-A1、信号 XYZ),
     信号名取元素 Name(空则退回 timeseries 的 Name,再退回 信号N)。
2) 普通 v7.3 变量:顶层数值矩阵按「列 = 信号、行 = 采样」读取(时间轴 = 采样序号),
   结构体按字段递归。
加载管线扩成多分组:新增 LoadSignalSourceGroups()(DataLoader)→
AsyncLoadState::groups → Application::LoadFile 逐个 AddGroup;其它格式仍只有一个分组。
文件对话框与 IsSupportedSignalFile 增加 .mat。

三、验证(全部离屏,不占用鼠标/键盘/屏幕)
- 新增 haloshot(截图对照 f845616)、matcheck(15/15:4 组 8 信号、时间轴单调、采样数正确)、
  matui(11/11:Application::LoadFile 端到端把 4 个分组加进数据层与信号列表);
- 回归 verify 49/49、panelanim 158/158、gridanim 93/93、hoverdrag 20/20、lanescroll 23/23、
  datacheck 69/69、edgecheck 46/46、errcheck 19/19、halocheck 16/16、panflash 19/19、
  panelstutter 8/8、stress 6/6;
- make release / make debug 零警告;Makefile 与探针构建脚本补 -lz。

文档:Documents/agent_mem.md 追加本轮小节(含 4 条新规则:叠加层必须自带外扩、
缠绕填充不可用于自交包络带、HDF5 主消息块长度看 headerSize、
MATLAB 分块维度可能比数据空间多一维、按右侧补 1 对齐)。
2026-10-19 00:52:41 +08:00

954 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// HDF5 只读子集:MATLAB 7.3 `.mat` 解析用(见 MatH5.hpp 的说明)。
#include "MatH5.hpp"
#include <zlib.h>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <filesystem>
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<std::uint64_t>(data[index]) << (8 * index);
}
return value;
}
} // namespace
std::uint8_t MatHdf5::U8(std::uint64_t offset) const
{
return (offset < m_data.size()) ? m_data[static_cast<std::size_t>(offset)] : 0;
}
std::uint16_t MatHdf5::U16(std::uint64_t offset) const
{
return static_cast<std::uint16_t>(U8(offset)) | (static_cast<std::uint16_t>(U8(offset + 1)) << 8);
}
std::uint32_t MatHdf5::U32(std::uint64_t offset) const
{
return static_cast<std::uint32_t>(U16(offset)) | (static_cast<std::uint32_t>(U16(offset + 2)) << 16);
}
std::uint64_t MatHdf5::U64(std::uint64_t offset) const
{
return static_cast<std::uint64_t>(U32(offset)) | (static_cast<std::uint64_t>(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)
{
error.clear();
Close();
std::error_code code;
const std::uintmax_t size = std::filesystem::file_size(path, code);
if (code || size == 0)
{
error = L"无法读取文件大小。";
return false;
}
m_data.resize(static_cast<std::size_t>(size));
FILE* file = nullptr;
if (_wfopen_s(&file, path.c_str(), L"rb") != 0 || file == nullptr)
{
error = L"无法打开文件。";
return false;
}
const std::size_t read = std::fread(m_data.data(), 1, m_data.size(), file);
std::fclose(file);
if (read != 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<Message>& 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<std::pair<std::uint64_t, std::uint64_t>> 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<std::ptrdiff_t>(offset),
m_data.begin() + static_cast<std::ptrdiff_t>(offset + messageSize));
offset += (messageSize + 7u) & ~static_cast<std::uint16_t>(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<std::pair<std::string, std::uint64_t>>& out) const
{
int guard = 0;
while (node != 0 && node + 24 < m_data.size() && guard++ < 100000)
{
if (std::memcmp(&m_data[static_cast<std::size_t>(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<std::uint64_t> children;
for (std::uint16_t index = 0; index < static_cast<std::uint16_t>(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<std::pair<std::string, std::uint64_t>>& out) const
{
if (node == 0 || node + 8 > m_data.size() ||
std::memcmp(&m_data[static_cast<std::size_t>(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<const char*>(m_data.data() + heapData + nameOffset);
out.emplace_back(std::string(name), objectAddress);
}
}
std::vector<std::pair<std::string, std::uint64_t>> MatHdf5::Members(std::uint64_t address) const
{
std::vector<std::pair<std::string, std::uint64_t>> out;
std::vector<Message> 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<std::size_t>(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<std::uint64_t>(message.data[offset + byte]) << (8 * byte);
}
offset += lengthSize;
if (offset + nameLength + 8 > message.data.size())
{
continue;
}
std::string name(reinterpret_cast<const char*>(message.data.data() + offset),
static_cast<std::size_t>(nameLength));
offset += static_cast<std::size_t>(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<Message> 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<TypeClass>(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<unsigned>(bits0) | (static_cast<unsigned>(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<const char*>(data + nameStart), cursor - nameStart);
++cursor; // 结束符
cursor = (cursor + 7) & ~static_cast<std::size_t>(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<std::size_t>(rank) * 4; // 维度
cursor += static_cast<std::size_t>(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<std::uint8_t>& out) const
{
if (dataset.chunkAddress == 0 || dataset.chunkDims.empty() || elementSize == 0 ||
dataset.dims.empty())
{
return false;
}
// ⚠️ MATLAB 写出的分块维度可能与数据空间维度**维数不一致**(实测:1 维数据集的
// 分块维度是 `[N, 1]`)。把维度数组在**右侧补 1**(行优先的最快轴)对齐即可,
// 二者的元素总数与遍历顺序都不变。
std::vector<std::size_t> dims = dataset.dims;
std::vector<std::size_t> 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<std::size_t>(total) * elementSize, 0);
std::vector<std::uint8_t> chunk;
std::vector<std::uint8_t> inflated;
// 遍历分块 B-tree:key = 分块大小 + 过滤器掩码 + 各维偏移。
std::vector<std::uint64_t> 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<std::size_t>(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<std::uint16_t>(entries + 1u); ++index)
{
const std::uint32_t chunkSize = (offset + 4 <= m_data.size()) ? U32(offset) : 0;
std::vector<std::uint64_t> 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<std::ptrdiff_t>(child),
m_data.begin() + static_cast<std::ptrdiff_t>(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)
{
inflated.assign(expected, 0);
uLongf destLength = static_cast<uLongf>(expected);
if (uncompress(inflated.data(), &destLength, chunk.data(),
static_cast<uLong>(chunk.size())) != Z_OK)
{
continue;
}
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<std::size_t> 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<std::size_t>(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<Message> 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<std::size_t>(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<LayoutClass>(klass);
if (klass == 0)
{
// 紧凑:数据就放在消息里
if (version == 3)
{
compactSize = static_cast<std::size_t>(message.data[2]) |
(static_cast<std::size_t>(message.data[3]) << 8);
}
else
{
compactSize = static_cast<std::size_t>(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<std::uint16_t>(message.data[offset]) |
(static_cast<std::uint16_t>(message.data[offset + 1]) << 8);
const std::uint16_t nameLength = static_cast<std::uint16_t>(message.data[offset + 2]) |
(static_cast<std::uint16_t>(message.data[offset + 3]) << 8);
const std::uint16_t values =
static_cast<std::uint16_t>(message.data[offset + 6]) |
(static_cast<std::uint16_t>(message.data[offset + 7]) << 8);
offset += 8;
offset += (nameLength + 7) & ~static_cast<std::size_t>(7);
offset += static_cast<std::size_t>(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<std::ptrdiff_t>(offset),
message.data.begin() +
static_cast<std::ptrdiff_t>(offset + compactSize));
}
}
}
}
else if (out.layout == LayoutClass::Contiguous)
{
if (dataAddress != 0 && dataAddress + dataSize <= m_data.size())
{
out.data.assign(m_data.begin() + static_cast<std::ptrdiff_t>(dataAddress),
m_data.begin() + static_cast<std::ptrdiff_t>(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<Message> 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<std::uint16_t>(message.data[2]) |
(static_cast<std::uint16_t>(message.data[3]) << 8);
const std::uint16_t typeSize = static_cast<std::uint16_t>(message.data[4]) |
(static_cast<std::uint16_t>(message.data[5]) << 8);
const std::uint16_t spaceSize = static_cast<std::uint16_t>(message.data[6]) |
(static_cast<std::uint16_t>(message.data[7]) << 8);
std::size_t offset = headerSize;
if (offset + nameSize > message.data.size())
{
continue;
}
std::string attributeName(reinterpret_cast<const char*>(message.data.data() + offset),
nameSize);
while (!attributeName.empty() && attributeName.back() == '\0')
{
attributeName.pop_back();
}
offset += (nameSize + 7) & ~static_cast<std::size_t>(7);
const std::size_t typeOffset = offset;
offset += (typeSize + 7) & ~static_cast<std::size_t>(7);
const std::size_t spaceOffset = offset;
offset += (spaceSize + 7) & ~static_cast<std::size_t>(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<std::size_t>(ReadLE(&message.data[dimOffset], 8));
dimOffset += 8;
}
}
std::string text;
if (type.cls == TypeClass::String || type.cls == TypeClass::Integer)
{
text.assign(reinterpret_cast<const char*>(message.data.data() + offset),
std::min(message.data.size() - offset, std::max<std::size_t>(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<wchar_t>(
static_cast<std::uint16_t>(dataset.data[index]) |
(static_cast<std::uint16_t>(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<wchar_t>(byte));
}
}
return true;
}
return false;
}
bool MatHdf5::ReadNumbers(std::uint64_t address, std::vector<double>& 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<double>(value);
}
else if (integer && dataset.type.isSigned)
{
std::int64_t value = 0;
for (std::size_t byte = 0; byte < elementSize; ++byte)
{
value |= static_cast<std::int64_t>(source[byte]) << (8 * byte);
}
// 符号扩展
const int bits = static_cast<int>(elementSize) * 8;
if (bits < 64 && (value & (1ll << (bits - 1))) != 0)
{
value |= ~((1ll << bits) - 1);
}
out[index] = static_cast<double>(value);
}
else if (integer)
{
std::uint64_t value = 0;
for (std::size_t byte = 0; byte < elementSize; ++byte)
{
value |= static_cast<std::uint64_t>(source[byte]) << (8 * byte);
}
out[index] = static_cast<double>(value);
}
}
return true;
}
bool MatHdf5::ReadReferences(std::uint64_t address, std::vector<std::uint64_t>& 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