- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
导读:bytes是 Wing 标准库中用于表示不可变二进制序列的内置类型,也是 SDK 测试集与 hangar 快照机制共同覆盖的核心数据结构。本文以仓库中的编译快照 bytes.test.w_compile_tf-aws.md 及其源测试文件 bytes.test.w 为主线,结合 SDK 源码 bytes.ts 与官方 API 文档 bytes.md,完整讲解 bytes 的五种构造方式、四种转换方法、索引与边界处理、拼接切片,以及和fs模块配合的字节级文件读写,并揭示 hangar 快照测试体系如何自动验证 preflight 与 inflight 两端的行为一致性。读完本文,你可以在 Wing 项目中熟练处理图片、音频等二进制数据,并理解这类测试快照的来源与含义。
一、这份编译快照说明了什么
bytes.test.w_compile_tf-aws.md是 hangar 测试工具自动生成的一份编译结果快照(snapshot),其核心内容是bytes.test.w以tf-aws平台编译后产出的 Terraform 配置:
{ "//": { "metadata": { "backend": "local", "stackName": "root" }, "outputs": {} }, "provider": { "aws": [ {} ] } }这份快照的关键信息在于:main.tf.json中除了 AWS provider 声明外不包含任何资源。也就是说,bytes.test.w中的全部字节操作都属于纯计算逻辑,不会在 tf-aws 目标下生成任何云资源(如 S3 桶、Lambda 函数等)。这与测试文件的设计完全吻合——该测试的test "bytes inflight"块虽然会在运行时进入 inflight 主机(模拟器或 AWS Lambda),但它只操作内存中的数据和本地临时文件,不依赖任何云端基础设施。
快照顶部的标题行[bytes.test.w](https://link.gitcode.com/i/391d9818e12738c0f3deddf0588f022b) | compile | tf-aws指明了它的三个属性:被测文件是 bytes.test.w,测试类型是compile(编译),目标平台是tf-aws。这是理解整个快照目录的命名范式:{测试文件名}_{compile|test}_{平台}.md。
二、bytes 类型的五种构造方式
在 Wing 中构造一个bytes值有五种途径,全部以静态方法形式挂在bytes命名空间下。SDK 源码 bytes.ts 中实现了这些方法,其内部统一以Uint8Array作为底层存储(readonly _data: Uint8Array),构造函数是私有的,用户只能通过静态工厂方法创建实例。
// 从原始字节数组构造 let rawData = bytes.fromRaw([104, 101, 108, 108, 111]); // 从字符串构造(按 UTF-8 编码) let rawString = bytes.fromString("hello"); // 从 Base64 编码字符串构造 let base64 = bytes.fromBase64("aGVsbG8="); // 从十六进制字符串构造 let hex = bytes.fromHex("68656c6c6f"); // 构造指定长度的全零字节序列 let zeros = bytes.zeros(5);这五种方式都指向同一份内容hello(十进制 104/101/108/108/111),因此测试文件用expect.equal断言它们完全相等,并验证zeros的长度也是 5:
expect.equal(rawData, rawString); expect.equal(rawData, base64); expect.equal(rawData, hex); expect.equal(rawData.length, zeros.length);各构造方法的实现细节(来自 bytes.ts):
| 方法 | 签名 | 实现要点 |
|---|---|---|
fromRaw | (values: Array<num>) => bytes | 直接用new Uint8Array(values)包装输入数组 |
fromString | (value: str) => bytes | 通过TextEncoder().encode(value)按 UTF-8 编码 |
fromBase64 | (base64: str) => bytes | 先用atob解码为二进制字符串,再逐字符转成Uint8Array |
fromHex | (hex: str) => bytes | 每两个十六进制字符为一组,parseInt(..., 16)解析为字节 |
zeros | (length: num) => bytes | 直接分配new Uint8Array(length),内容默认为 0 |
三、字节的转换:toRaw、toString、toBase64、toHex
构造之外,bytes 还提供了四个转换方向的方法,测试文件逐一验证了往返一致性:
let asString: str = rawData.toString(); let asRaw: Array<num> = rawData.toRaw(); let asBase64: str = rawData.toBase64(); let asHex: str = rawData.toHex(); expect.equal(asString, "hello"); expect.equal(asRaw, [104, 101, 108, 108, 111]); expect.equal(asBase64, "aGVsbG8="); expect.equal(asHex, "68656c6c6f");从源码看(bytes.ts):
toRaw()通过Array.from(this._data)展开为Array<num>;toString()用TextDecoder解码(编码可配置,默认utf-8,见下节);toBase64()用btoa(String.fromCharCode(...this._data))编码;toHex()将每个字节toString(16)后不足两位补零,再拼接为连续字符串。
值得注意的是toString()是唯一带参数的方法,其参数类型是 BytesToStringOptions 结构,包含一个encoding字段:
// 默认使用 "utf-8" 解码 let someBytes = bytes.fromRaw([195, 169]); let someString = someBytes.toString(); // 默认是 "utf-8" // 也可以显式指定其它编码 let someString2 = someBytes.toString(encoding: "windows-1252"); expect.equal(someString, "é"); expect.equal(someString2, "é");同一个字节序列[195, 169]在 UTF-8 下解码为é,在windows-1252下则被解读为两个字符é,这直观展示了编码选择对解码结果的实质影响。支持的编码集合同TextDecoder能力,源码注释中给出了 MDN Encoding API 的参考(默认值"utf-8"定义在 bytes.ts 的BytesToStringOptions接口中)。
四、索引访问与越界处理
bytes 支持下标访问,直接取第 n 个字节值(0–255):
expect.equal(rawData[0], 104); expect.equal(rawData[1], 101); expect.equal(rawData[2], 108); expect.equal(rawData[3], 108); expect.equal(rawData[4], 111);越界时会被显式拦截。测试文件用try/catch验证了这一点,并断言错误信息中包含"out of bounds":
try { rawData[5]; expect.fail("Expected error"); } catch err { expect.ok(err.contains("out of bounds")); }源码中对应的安全访问方法有两个(bytes.ts):
at(index):越界时直接throw new Error("Index out of bounds");tryAt(index):越界时返回nil(对应 JS 的undefined),不抛异常,适合需要"取不到就算了"的防御式写法。
五、拼接与切片
bytes.concat(...values)是静态方法,可将多个 bytes 值按顺序拼成一个新序列;slice(startIndex, endIndex?)是实例方法,返回从起始下标到结束下标(左闭右开)的切片:
let concated = bytes.concat(rawData, rawData, rawData); expect.equal(concated.length, rawData.length * 3); expect.equal(concated.toString(), "hellohellohello"); let sliced = rawData.slice(1, 3); expect.equal(sliced.length, 2); expect.equal(sliced.toString(), "el");实现上(bytes.ts),concat先累加所有输入的长度并分配一个总长的Uint8Array,再依次set进结果;slice直接委托给底层Uint8Array.prototype.slice,因此语义与 JavaScript 的 slice 完全一致(endIndex可省略,默认取到末尾)。两者都返回新的 bytes 实例,不会修改原数据,这符合 bytes 不可变的类型定位。
六、与 fs 模块协同:字节级文件读写
bytes 的典型实战场景是读写二进制文件,测试文件演示了完整的"写 → 读 → 校验 → 清理"闭环:
let tmpdir = fs.mkdtemp("bytes-test-"); let path = fs.join(tmpdir, "hello.txt"); fs.writeBytes(path, rawData); let readData = fs.readBytes(path); expect.equal(readData.toString(), "hello"); fs.remove(tmpdir);对应 SDK 实现位于 fs.ts:readBytes通过fs.readFileSync读入缓冲区后包装为Bytes(第 396-399 行),writeBytes则把Bytes底层的Uint8Array直接写入文件(第 458-464 行)。此外还有tryReadBytes(文件不存在时返回undefined而非抛错)等配套方法。
测试还覆盖了大文件场景——在非 Windows 平台上生成并读回一个 10MB 的随机二进制文件:
if util.os() != "win32" { let tenMB = 10 * 1024 * 1024; // 10MB 以字节为单位 let largeTmpdir = fs.mkdtemp("large-bytes-test-"); let largePath = fs.join(largeTmpdir, "large-file.bin"); // 用 dd 生成 10MB 随机数据 util.exec("sh", ["-c", "dd if=/dev/urandom of={largePath} bs=1M count=10 2>/dev/null"]); let largeBytes = fs.readBytes(largePath); expect.equal(largeBytes.length, tenMB); fs.remove(largeTmpdir); }这段代码验证了 bytes 在较大数据量下也能正确读取、length精确反映文件大小,并展示了util.exec与fs模块的组合使用。
七、preflight 与 inflight 的一致性验证
bytes.test.w的结构很特殊:文件顶层(preflight 阶段)完整演示了全部 API,随后又在test "bytes inflight" { ... }块中几乎原样重复了一遍。测试文件注释明确说明了原因:
all of the code above is repeated here to ensure that the code behaves the same on inflight hosts (like AWS Lambda, or the simulator)
这是 Wing "一语言两阶段"模型的直接体现:bytes 这种纯数据结构应保证在编译期 preflight和运行时 inflight(如 AWS Lambda)两端行为一致。hangar 的test快照 bytes.test.w_test_sim.md 记录了这一验证在模拟器目标下的执行结果:
pass ─ bytes.test.wsim » root/Default/test:bytes inflight Tests 1 passed (1)该快照表明bytes inflight测试在 sim 平台下全部通过,从测试执行层面印证了 preflight/inflight 语义的一致性。
八、快照是如何生成的:hangar 测试机制
这两份快照(compile 与 test)并非手工编写,而是由 hangar 测试工具自动生成与校验。其生成链路为:
- generate_tests.ts 遍历
tests/sdk_tests下的全部.w文件,为每个文件生成两个 Vitest 用例:wing compile -t tf-aws和wing test -t sim; - generated_test_targets.ts 中的
compileTest实际执行wing compile -t tf-aws,读取产物目录里的main.tf.json与.wing目录内容,再交给createMarkdownSnapshot落盘为 Markdown 快照(其中的sanitize_json_paths会把本地绝对路径、资产哈希等不稳定内容替换为占位符,保证快照可复现); - utils.ts 的
createMarkdownSnapshot按{文件名}_{compile|test}_{平台}.md的命名规则把内容写入tools/hangar/__snapshots__/test_corpus目录,并用 Vitest 的toMatchFileSnapshot与既有快照比对——若编译输出发生变化,快照测试会失败并提示更新。
这正是为什么 compile 快照里的main.tf.json会被"净化"成我们看到的极简形态:任何随环境变化的路径、哈希、版本号都被归一化,快照中保留的仅是确定性产物——对于纯逻辑的 bytes 测试来说,就是一份只有 provider 声明的空 Terraform 模板。
九、API 速查:Bytes 类全量成员
综合 API 文档与源码,Bytes类的完整成员如下:
静态方法(构造与组合)
| 方法 | 签名 | 说明 |
|---|---|---|
fromRaw | (values: Array<num>) => bytes | 从字节数组构造 |
fromString | (value: str) => bytes | 从 UTF-8 字符串构造 |
fromBase64 | (base64: str) => bytes | 从 Base64 字符串解码构造 |
fromHex | (hex: str) => bytes | 从十六进制字符串解码构造 |
zeros | (length: num) => bytes | 构造指定长度的全零序列 |
concat | (...values: Array<bytes>) => bytes | 拼接多个 bytes 值 |
实例方法(访问与转换)
| 方法 | 签名 | 说明 |
|---|---|---|
at | (index: num) => num | 取下标字节,越界抛错 |
tryAt | (index: num) => num? | 取下标字节,越界返回 nil |
copy | () => bytes | 深拷贝一份 |
slice | (startIndex: num, endIndex?: num) => bytes | 切片,左闭右开 |
toString | (options?: { encoding: str }) => str | 解码为字符串,默认 utf-8 |
toRaw | () => Array<num> | 转为字节数组 |
toBase64 | () => str | 转为 Base64 字符串 |
toHex | () => str | 转为十六进制字符串 |
属性:length: num(字节长度)。
十、总结与实践要点
通过这份快照与其源测试,可以总结出 bytes 类型在 Wing 项目中的实践要点:
- 优先使用语义化构造:同源数据用
fromString,传输/存储用fromBase64,调试打印用fromHex,二进制协议拼装用fromRaw,占位缓冲用zeros; - 明确解码编码:
toString()默认 UTF-8,遇到非 UTF-8 数据(如遗留系统文本、单字节编码)时务必显式传入encoding; - 善用边界安全 API:不确定下标是否越界时,优先
tryAt而非裸下标访问; - 大文件也无需担心:
fs.readBytes/fs.writeBytes直接以字节为单位读写,配合length可精确校验文件大小; - 理解快照的价值:看到
__snapshots__下的.md文件时,应意识到它们是 hangar 对 Wing 程序编译/运行产物的确定性记录,可用于快速判断某个 SDK 行为是否回归。
若要在本地复现这些验证,可进入仓库后对tests/sdk_tests/std目录运行 hangar 测试(wing compile -t tf-aws与wing test -t sim),即可重新生成并核对上述两份快照。
- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
相关推荐
Wing 语言标准库 Bytes 完全指南:不可变二进制数据的构造、转换与文件读写
Wing 语言标准库 Bytes 完全指南:不可变二进制数据的构造、转换与文件读写 本文以 Wing 语言标准库( @winglang/sdk.std )中 B
编程语言编译器云原生语言运行时Wing 语言 bytes 类型实战:二进制数据的构造、转换与处理全指南
Wing 语言 bytes 类型实战:二进制数据的构造、转换与处理全指南 当你在 Wing 中处理图片、音频或其他二进制格式文件时,往往需要在字节(byte)级
编程语言编译器云原生语言运行时Wing 云编程语言 fs 标准库 JSON 文件读写与 Hangar 快照测试验证
Wing 云编程语言 fs 标准库 JSON 文件读写与 Hangar 快照测试验证 Wing 是一门面向云的编程语言,它的标准库 fs 模块提供了在 pref
编程语言编译器云原生语言运行时
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考