落地映像读写:72 字节头、段布局、FNV-1a、.stb 与 sidecar。
- 指令与映像.md 冻结:头字段表、常量表/函数表行、段序、数据段对齐、sidecar 格式 - Types.h 补 TypeTag;Image.h/cpp:ImageView 校验、write_ret_main、文件与 sidecar - isa CMake 改 target_include_directories PUBLIC
This commit is contained in:
+54
-10
@@ -130,19 +130,51 @@ FB 实例固定布局,字段偏移编译期算死。
|
|||||||
|
|
||||||
## 映像
|
## 映像
|
||||||
|
|
||||||
魔数 `STSC`,版本 `1`。
|
魔数 `STSC`,版本 `1`。全部整数小端。文件 = 头 + 五个段,段序:常量表 → 函数表 → 字节码 → FB 布局 → 数据。偏移记在头里,段必须连续、偏移单调不减,末段终点不超过文件长度。
|
||||||
|
|
||||||
头:
|
### 头(72 字节)
|
||||||
|
|
||||||
- `cycle_limit`、`dt_ms`
|
| 偏移 | 宽 | 字段 |
|
||||||
- 工程哈希
|
|---|---|---|
|
||||||
- 全局槽数、I/Q/M 槽数
|
| 0 | 4 | 魔数 `STSC` |
|
||||||
- 常量表 / 函数表 / 字节码 / FB 布局的偏移
|
| 4 | 4 | 版本 `1` |
|
||||||
- 入口 `MAIN` 的 `fn_id`
|
| 8 | 4 | `cycle_limit` |
|
||||||
|
| 12 | 4 | `dt_ms` |
|
||||||
|
| 16 | 8 | 工程哈希(FNV-1a 64) |
|
||||||
|
| 24 | 4 | 入口 `entry_fn_id` |
|
||||||
|
| 28 | 4 | 全局槽数 `n_globals` |
|
||||||
|
| 32 | 4 | I 槽数 `n_i` |
|
||||||
|
| 36 | 4 | Q 槽数 `n_q` |
|
||||||
|
| 40 | 4 | M 槽数 `n_m` |
|
||||||
|
| 44 | 4 | 常量数 `n_consts` |
|
||||||
|
| 48 | 4 | 函数数 `n_funcs` |
|
||||||
|
| 52 | 4 | `offset_const` |
|
||||||
|
| 56 | 4 | `offset_funcs` |
|
||||||
|
| 60 | 4 | `offset_code` |
|
||||||
|
| 64 | 4 | `offset_fb` |
|
||||||
|
| 68 | 4 | `offset_data` |
|
||||||
|
|
||||||
函数表一行:`fn_id`、`nregs`、字节码偏移与长度。
|
### 常量表一行(12 字节)
|
||||||
|
|
||||||
工程哈希:收录源文件路径排序后对内容做 **FNV-1a 64**,只保证回放槽位一致,不是密码学哈希。
|
`[tag:u32][value:u64]`;`tag`:0=BOOL、1=INT、2=TIME。`value` 按 tag 解释,未用高位为 0。
|
||||||
|
|
||||||
|
### 函数表一行(12 字节)
|
||||||
|
|
||||||
|
`[nregs:u32][code_offset:u32][code_len:u32]`;行下标即 `fn_id`,`code_offset` 相对字节码段起点,`code_len` 为指令条数。
|
||||||
|
|
||||||
|
### 字节码段
|
||||||
|
|
||||||
|
指令按 `u32` 连续排;`CALL` 的 `fn_id` 是函数表行下标。
|
||||||
|
|
||||||
|
### FB 布局段
|
||||||
|
|
||||||
|
第一版为空;每类型一行 `[field_count:u32][field_count × (tag:u32, offset:u32)]`,offset 相对实例基址,12.8 落地 codegen 时冻精确表。
|
||||||
|
|
||||||
|
### 数据段
|
||||||
|
|
||||||
|
槽初值按序排:全局(`n_globals`)→ I → Q → M。槽宽与对齐:`BOOL` 1 字节 / `INT` 2 字节 / `TIME` 8 字节,每个槽起点对齐到自身宽度(需要时插 padding)。
|
||||||
|
|
||||||
|
工程哈希:收录源文件路径排序后对内容做 **FNV-1a 64**,只保证回放槽位一致,不是密码学哈希。FNV-1a 64:basis `0xcbf29ce484222325`,prime `0x100000001b3`。
|
||||||
|
|
||||||
`[[io.input]]` / `[[io.output]]` 不进映像。
|
`[[io.input]]` / `[[io.output]]` 不进映像。
|
||||||
|
|
||||||
@@ -153,7 +185,19 @@ FB 实例固定布局,字段偏移编译期算死。
|
|||||||
编译产物是单文件 `<name>.stb`:映像头 + 各段按本文布局连续排,魔数 `STSC`、版本 `1`、小端。`STCompiler` 写,`BytecodeExecutor` 读。
|
编译产物是单文件 `<name>.stb`:映像头 + 各段按本文布局连续排,魔数 `STSC`、版本 `1`、小端。`STCompiler` 写,`BytecodeExecutor` 读。
|
||||||
|
|
||||||
- `dt_ms` / `cycle_limit` 在映像头,执行器只认映像,不重复配置。
|
- `dt_ms` / `cycle_limit` 在映像头,执行器只认映像,不重复配置。
|
||||||
- I/O 绑定不进映像:`STCompiler` 另写 sidecar `<name>.runtime.toml`,内容是 `var`(已解析槽号)→ `channel` / `bit`;`BytecodeExecutor` 读 sidecar 做采样与写回,不创造变量。
|
- I/O 绑定不进映像:`STCompiler` 另写 sidecar `<name>.runtime.toml`,`BytecodeExecutor` 读 sidecar 做采样与写回,不创造变量。
|
||||||
|
|
||||||
|
sidecar 格式(TOML 子集,冻结):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[io.input]] # 或 [[io.output]]
|
||||||
|
var = "I0_0" # 已在 GVL 声明的名字
|
||||||
|
slot = 1 # 全局槽号(编译期已解析)
|
||||||
|
channel = 0
|
||||||
|
bit = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
每行一个绑定;`var` 顺序不要求与映像槽序一致(`slot` 为准)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -10,7 +10,8 @@ project(ISA
|
|||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
include_directories(./include)
|
|
||||||
|
|
||||||
add_library(isa STATIC
|
add_library(isa STATIC
|
||||||
./src/Encode.cpp)
|
./src/Encode.cpp
|
||||||
|
./src/Image.cpp)
|
||||||
|
|
||||||
|
target_include_directories(isa PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* @file Image.h
|
||||||
|
* @brief 映像头、段、FNV-1a 哈希、.stb 文件与 sidecar
|
||||||
|
* @author
|
||||||
|
* @date 2026-08-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "isa/Types.h"
|
||||||
|
|
||||||
|
namespace isa {
|
||||||
|
|
||||||
|
// 魔数 "STSC"(小端 u32)、版本
|
||||||
|
static const uint32_t kMagic = 0x43545353u;
|
||||||
|
static const uint32_t kVersion = 1;
|
||||||
|
|
||||||
|
// 头与表行宽(见 Doc/isa/指令与映像.md)
|
||||||
|
static const size_t kHeaderSize = 72;
|
||||||
|
static const size_t kConstEntrySize = 12;
|
||||||
|
static const size_t kFuncRowSize = 12;
|
||||||
|
|
||||||
|
// FNV-1a 64:basis / prime(见 Doc/isa/指令与映像.md)
|
||||||
|
static const uint64_t kFnvBasis = 0xcbf29ce484222325ull;
|
||||||
|
static const uint64_t kFnvPrime = 0x100000001b3ull;
|
||||||
|
|
||||||
|
// FNV-1a 64 增量与一次性
|
||||||
|
uint64_t fnv1a64_update(uint64_t h, const uint8_t* data, size_t len);
|
||||||
|
uint64_t fnv1a64(const uint8_t* data, size_t len);
|
||||||
|
|
||||||
|
// 映像头(字节布局见 Doc/isa/指令与映像.md)
|
||||||
|
struct ImageHeader {
|
||||||
|
uint32_t cycle_limit;
|
||||||
|
uint32_t dt_ms;
|
||||||
|
uint64_t project_hash;
|
||||||
|
uint32_t entry_fn_id;
|
||||||
|
uint32_t n_globals;
|
||||||
|
uint32_t n_i;
|
||||||
|
uint32_t n_q;
|
||||||
|
uint32_t n_m;
|
||||||
|
uint32_t n_consts;
|
||||||
|
uint32_t n_funcs;
|
||||||
|
uint32_t offset_const;
|
||||||
|
uint32_t offset_funcs;
|
||||||
|
uint32_t offset_code;
|
||||||
|
uint32_t offset_fb;
|
||||||
|
uint32_t offset_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 常量表一行
|
||||||
|
struct ConstEntry {
|
||||||
|
types::TypeTag tag;
|
||||||
|
uint64_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 函数表一行
|
||||||
|
struct FuncRow {
|
||||||
|
uint32_t nregs;
|
||||||
|
uint32_t code_offset; // 相对字节码段起点
|
||||||
|
uint32_t code_len; // 指令条数
|
||||||
|
};
|
||||||
|
|
||||||
|
// I/O 绑定(sidecar 一行)
|
||||||
|
struct IoBinding {
|
||||||
|
std::string var;
|
||||||
|
uint32_t slot;
|
||||||
|
uint32_t channel;
|
||||||
|
uint32_t bit;
|
||||||
|
bool is_input; // true = [[io.input]],false = [[io.output]]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 只读视图:校验过的映像
|
||||||
|
class ImageView {
|
||||||
|
public:
|
||||||
|
static ImageView from(const uint8_t* buf, size_t len);
|
||||||
|
static ImageView from(const std::vector<uint8_t>& buf);
|
||||||
|
|
||||||
|
bool ok() const;
|
||||||
|
const std::string& error() const;
|
||||||
|
const ImageHeader& header() const;
|
||||||
|
|
||||||
|
const uint8_t* raw() const;
|
||||||
|
size_t raw_len() const;
|
||||||
|
|
||||||
|
ConstEntry const_entry(size_t i) const;
|
||||||
|
FuncRow func_row(size_t i) const;
|
||||||
|
|
||||||
|
const uint8_t* code_bytes() const; // 字节码段起点
|
||||||
|
size_t code_len() const; // 字节数
|
||||||
|
const uint8_t* data_bytes() const; // 数据段起点
|
||||||
|
size_t data_len() const; // 字节数
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImageView();
|
||||||
|
|
||||||
|
const uint8_t* buf_;
|
||||||
|
size_t len_;
|
||||||
|
bool ok_;
|
||||||
|
std::string err_;
|
||||||
|
ImageHeader hdr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 写最小映像:空槽 + MAIN(fn_id=0)+ 一条 RET
|
||||||
|
std::vector<uint8_t> write_ret_main(uint32_t cycle_limit, uint32_t dt_ms,
|
||||||
|
uint64_t project_hash);
|
||||||
|
|
||||||
|
// .stb 文件读写(纯字节,校验交给 read_image / ImageView)
|
||||||
|
bool write_stb_file(const char* path, const std::vector<uint8_t>& image,
|
||||||
|
std::string* err);
|
||||||
|
bool read_stb_file(const char* path, std::vector<uint8_t>* image,
|
||||||
|
std::string* err);
|
||||||
|
|
||||||
|
// sidecar 生成与写文件(格式见 Doc/isa/指令与映像.md)
|
||||||
|
std::string make_sidecar(const std::vector<IoBinding>& bindings);
|
||||||
|
bool write_sidecar_file(const char* path,
|
||||||
|
const std::vector<IoBinding>& bindings,
|
||||||
|
std::string* err);
|
||||||
|
}
|
||||||
@@ -20,6 +20,13 @@ namespace isa {
|
|||||||
// 差值可负、为 DATE_AND_TIME 与 64 位定时器预留。槽位按 8 字节对齐。
|
// 差值可负、为 DATE_AND_TIME 与 64 位定时器预留。槽位按 8 字节对齐。
|
||||||
using TIME = int64_t;
|
using TIME = int64_t;
|
||||||
|
|
||||||
|
// 槽 / 常量表类型标记,数值冻结:0=BOOL、1=INT、2=TIME(见映像规范)
|
||||||
|
enum TypeTag : uint8_t {
|
||||||
|
Bool = 0,
|
||||||
|
Int = 1,
|
||||||
|
Time = 2,
|
||||||
|
};
|
||||||
|
|
||||||
// 饱和算术:夹到 INT 最小/最大,编译期与 VM 共用同一规则。
|
// 饱和算术:夹到 INT 最小/最大,编译期与 VM 共用同一规则。
|
||||||
|
|
||||||
inline INT sat_add(INT a, INT b) {
|
inline INT sat_add(INT a, INT b) {
|
||||||
|
|||||||
@@ -0,0 +1,321 @@
|
|||||||
|
/**
|
||||||
|
* @file Image.cpp
|
||||||
|
* @brief 映像头、段、FNV-1a 哈希、.stb 文件与 sidecar
|
||||||
|
* @author
|
||||||
|
* @date 2026-08-19
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "isa/Image.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "isa/Instr.h"
|
||||||
|
|
||||||
|
namespace isa {
|
||||||
|
|
||||||
|
// ---- 小端读写(只放 .cpp,不把 packed struct 当 ABI)----
|
||||||
|
|
||||||
|
static void put_le32(std::vector<uint8_t>& b, size_t off, uint32_t v) {
|
||||||
|
b[off + 0] = static_cast<uint8_t>(v & 0xFFu);
|
||||||
|
b[off + 1] = static_cast<uint8_t>((v >> 8) & 0xFFu);
|
||||||
|
b[off + 2] = static_cast<uint8_t>((v >> 16) & 0xFFu);
|
||||||
|
b[off + 3] = static_cast<uint8_t>((v >> 24) & 0xFFu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t get_le32(const uint8_t* p) {
|
||||||
|
return static_cast<uint32_t>(p[0])
|
||||||
|
| (static_cast<uint32_t>(p[1]) << 8)
|
||||||
|
| (static_cast<uint32_t>(p[2]) << 16)
|
||||||
|
| (static_cast<uint32_t>(p[3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_le64(std::vector<uint8_t>& b, size_t off, uint64_t v) {
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
b[off + i] = static_cast<uint8_t>((v >> (8 * i)) & 0xFFu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t get_le64(const uint8_t* p) {
|
||||||
|
uint64_t v = 0;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
v |= static_cast<uint64_t>(p[i]) << (8 * i);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- FNV-1a 64 ----
|
||||||
|
|
||||||
|
uint64_t fnv1a64_update(uint64_t h, const uint8_t* data, size_t len) {
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
h ^= data[i];
|
||||||
|
h *= kFnvPrime;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fnv1a64(const uint8_t* data, size_t len) {
|
||||||
|
return fnv1a64_update(kFnvBasis, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 最小映像:空槽 + MAIN + 一条 RET ----
|
||||||
|
|
||||||
|
std::vector<uint8_t> write_ret_main(uint32_t cycle_limit, uint32_t dt_ms,
|
||||||
|
uint64_t project_hash) {
|
||||||
|
std::vector<uint8_t> b(kHeaderSize + kFuncRowSize + 4, 0);
|
||||||
|
|
||||||
|
put_le32(b, 0, kMagic);
|
||||||
|
put_le32(b, 4, kVersion);
|
||||||
|
put_le32(b, 8, cycle_limit);
|
||||||
|
put_le32(b, 12, dt_ms);
|
||||||
|
put_le64(b, 16, project_hash);
|
||||||
|
put_le32(b, 24, 0); // entry_fn_id = MAIN = 0
|
||||||
|
// n_globals / n_i / n_q / n_m = 0
|
||||||
|
// n_consts = 0
|
||||||
|
put_le32(b, 48, 1); // n_funcs = 1
|
||||||
|
|
||||||
|
const size_t off_funcs = kHeaderSize; // 72
|
||||||
|
const size_t off_code = off_funcs + kFuncRowSize; // 84
|
||||||
|
const size_t off_end = off_code + 4; // 88
|
||||||
|
|
||||||
|
put_le32(b, 52, static_cast<uint32_t>(off_funcs)); // offset_const
|
||||||
|
put_le32(b, 56, static_cast<uint32_t>(off_funcs)); // offset_funcs
|
||||||
|
put_le32(b, 60, static_cast<uint32_t>(off_code)); // offset_code
|
||||||
|
put_le32(b, 64, static_cast<uint32_t>(off_end)); // offset_fb
|
||||||
|
put_le32(b, 68, static_cast<uint32_t>(off_end)); // offset_data
|
||||||
|
|
||||||
|
// 函数表一行:MAIN,无寄存器,代码 0 起 1 条
|
||||||
|
put_le32(b, off_funcs + 0, 0); // nregs
|
||||||
|
put_le32(b, off_funcs + 4, 0); // code_offset
|
||||||
|
put_le32(b, off_funcs + 8, 1); // code_len
|
||||||
|
|
||||||
|
// 字节码:RET
|
||||||
|
put_le32(b, off_code, pack(Op::RET, 0, 0, 0));
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 只读视图 ----
|
||||||
|
|
||||||
|
ImageView::ImageView()
|
||||||
|
: buf_(0), len_(0), ok_(false), err_("uninitialized"), hdr_() {}
|
||||||
|
|
||||||
|
ImageView ImageView::from(const uint8_t* buf, size_t len) {
|
||||||
|
ImageView v;
|
||||||
|
v.buf_ = buf;
|
||||||
|
v.len_ = len;
|
||||||
|
|
||||||
|
if (buf == 0) {
|
||||||
|
v.err_ = "null buffer";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if (len < kHeaderSize) {
|
||||||
|
v.err_ = "image too short";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if (get_le32(buf + 0) != kMagic) {
|
||||||
|
v.err_ = "bad magic";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if (get_le32(buf + 4) != kVersion) {
|
||||||
|
v.err_ = "bad version";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageHeader& h = v.hdr_;
|
||||||
|
h.cycle_limit = get_le32(buf + 8);
|
||||||
|
h.dt_ms = get_le32(buf + 12);
|
||||||
|
h.project_hash = get_le64(buf + 16);
|
||||||
|
h.entry_fn_id = get_le32(buf + 24);
|
||||||
|
h.n_globals = get_le32(buf + 28);
|
||||||
|
h.n_i = get_le32(buf + 32);
|
||||||
|
h.n_q = get_le32(buf + 36);
|
||||||
|
h.n_m = get_le32(buf + 40);
|
||||||
|
h.n_consts = get_le32(buf + 44);
|
||||||
|
h.n_funcs = get_le32(buf + 48);
|
||||||
|
h.offset_const = get_le32(buf + 52);
|
||||||
|
h.offset_funcs = get_le32(buf + 56);
|
||||||
|
h.offset_code = get_le32(buf + 60);
|
||||||
|
h.offset_fb = get_le32(buf + 64);
|
||||||
|
h.offset_data = get_le32(buf + 68);
|
||||||
|
|
||||||
|
// 段校验:连续、单调不减、末段终点不越界
|
||||||
|
const uint64_t offs[5] = {
|
||||||
|
h.offset_const, h.offset_funcs, h.offset_code, h.offset_fb, h.offset_data,
|
||||||
|
};
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
if (offs[i] < kHeaderSize || offs[i] > len) {
|
||||||
|
v.err_ = "segment offset out of range";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if (i > 0 && offs[i] < offs[i - 1]) {
|
||||||
|
v.err_ = "segment offsets not monotonic";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t const_len = h.offset_funcs - h.offset_const;
|
||||||
|
const uint64_t funcs_len = h.offset_code - h.offset_funcs;
|
||||||
|
if (const_len != static_cast<uint64_t>(h.n_consts) * kConstEntrySize) {
|
||||||
|
v.err_ = "const table size mismatch";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if (funcs_len != static_cast<uint64_t>(h.n_funcs) * kFuncRowSize) {
|
||||||
|
v.err_ = "function table size mismatch";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if ((h.offset_fb - h.offset_code) % 4 != 0) {
|
||||||
|
v.err_ = "code segment not 4-byte aligned";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if (h.entry_fn_id >= h.n_funcs && h.n_funcs != 0) {
|
||||||
|
v.err_ = "entry fn_id out of range";
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
v.ok_ = true;
|
||||||
|
v.err_.clear();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageView ImageView::from(const std::vector<uint8_t>& buf) {
|
||||||
|
return from(buf.data(), buf.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImageView::ok() const { return ok_; }
|
||||||
|
const std::string& ImageView::error() const { return err_; }
|
||||||
|
const ImageHeader& ImageView::header() const { return hdr_; }
|
||||||
|
const uint8_t* ImageView::raw() const { return buf_; }
|
||||||
|
size_t ImageView::raw_len() const { return len_; }
|
||||||
|
|
||||||
|
ConstEntry ImageView::const_entry(size_t i) const {
|
||||||
|
ConstEntry e;
|
||||||
|
e.tag = types::Bool;
|
||||||
|
e.value = 0;
|
||||||
|
if (ok_ && i < hdr_.n_consts) {
|
||||||
|
const uint8_t* p = buf_ + hdr_.offset_const + i * kConstEntrySize;
|
||||||
|
const uint32_t tag = get_le32(p);
|
||||||
|
if (tag <= static_cast<uint32_t>(types::Time)) {
|
||||||
|
e.tag = static_cast<types::TypeTag>(tag);
|
||||||
|
}
|
||||||
|
e.value = get_le64(p + 4);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
FuncRow ImageView::func_row(size_t i) const {
|
||||||
|
FuncRow r = {0, 0, 0};
|
||||||
|
if (ok_ && i < hdr_.n_funcs) {
|
||||||
|
const uint8_t* p = buf_ + hdr_.offset_funcs + i * kFuncRowSize;
|
||||||
|
r.nregs = get_le32(p + 0);
|
||||||
|
r.code_offset = get_le32(p + 4);
|
||||||
|
r.code_len = get_le32(p + 8);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* ImageView::code_bytes() const {
|
||||||
|
return ok_ ? buf_ + hdr_.offset_code : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ImageView::code_len() const {
|
||||||
|
return ok_ ? hdr_.offset_fb - hdr_.offset_code : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* ImageView::data_bytes() const {
|
||||||
|
return ok_ ? buf_ + hdr_.offset_data : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ImageView::data_len() const {
|
||||||
|
return ok_ ? len_ - hdr_.offset_data : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- .stb 文件读写 ----
|
||||||
|
|
||||||
|
bool write_stb_file(const char* path, const std::vector<uint8_t>& image,
|
||||||
|
std::string* err) {
|
||||||
|
FILE* f = std::fopen(path, "wb");
|
||||||
|
if (f == 0) {
|
||||||
|
if (err) {
|
||||||
|
*err = std::string("cannot open for write: ") + path;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const bool ok = image.empty() || std::fwrite(&image[0], 1, image.size(), f) == image.size();
|
||||||
|
std::fclose(f);
|
||||||
|
if (!ok && err) {
|
||||||
|
*err = std::string("write failed: ") + path;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool read_stb_file(const char* path, std::vector<uint8_t>* image,
|
||||||
|
std::string* err) {
|
||||||
|
FILE* f = std::fopen(path, "rb");
|
||||||
|
if (f == 0) {
|
||||||
|
if (err) {
|
||||||
|
*err = std::string("cannot open for read: ") + path;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::fseek(f, 0, SEEK_END);
|
||||||
|
const long size = std::ftell(f);
|
||||||
|
std::fseek(f, 0, SEEK_SET);
|
||||||
|
if (size < 0) {
|
||||||
|
std::fclose(f);
|
||||||
|
if (err) {
|
||||||
|
*err = "tell failed";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
image->resize(static_cast<size_t>(size));
|
||||||
|
const bool ok = size == 0 || std::fread(&(*image)[0], 1, static_cast<size_t>(size), f) == static_cast<size_t>(size);
|
||||||
|
std::fclose(f);
|
||||||
|
if (!ok && err) {
|
||||||
|
*err = std::string("read failed: ") + path;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- sidecar ----
|
||||||
|
|
||||||
|
std::string make_sidecar(const std::vector<IoBinding>& bindings) {
|
||||||
|
std::string out;
|
||||||
|
for (size_t i = 0; i < bindings.size(); ++i) {
|
||||||
|
const IoBinding& b = bindings[i];
|
||||||
|
char buf[64];
|
||||||
|
out += b.is_input ? "[[io.input]]\n" : "[[io.output]]\n";
|
||||||
|
out += "var = \"";
|
||||||
|
out += b.var;
|
||||||
|
out += "\"\n";
|
||||||
|
std::snprintf(buf, sizeof buf, "slot = %u\n", static_cast<unsigned>(b.slot));
|
||||||
|
out += buf;
|
||||||
|
std::snprintf(buf, sizeof buf, "channel = %u\n", static_cast<unsigned>(b.channel));
|
||||||
|
out += buf;
|
||||||
|
std::snprintf(buf, sizeof buf, "bit = %u\n", static_cast<unsigned>(b.bit));
|
||||||
|
out += buf;
|
||||||
|
out += "\n";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write_sidecar_file(const char* path,
|
||||||
|
const std::vector<IoBinding>& bindings,
|
||||||
|
std::string* err) {
|
||||||
|
FILE* f = std::fopen(path, "wb");
|
||||||
|
if (f == 0) {
|
||||||
|
if (err) {
|
||||||
|
*err = std::string("cannot open for write: ") + path;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::string s = make_sidecar(bindings);
|
||||||
|
const bool ok = s.empty() || std::fwrite(s.data(), 1, s.size(), f) == s.size();
|
||||||
|
std::fclose(f);
|
||||||
|
if (!ok && err) {
|
||||||
|
*err = std::string("write failed: ") + path;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace isa
|
||||||
Reference in New Issue
Block a user