Bun text assets report their stored UTF-16LE encoding (R3.11) (#435)

Bun's stored-text encoding 2 is labelled utf16le, as Bun writes it, and member bytes are kept as stored. Fixes #257.
This commit is contained in:
Afloat
2026-11-02 11:41:30 -05:00
committed by GitHub
parent a094ed23a9
commit 0ce375bcba
5 changed files with 43 additions and 3 deletions
+2 -2
View File
@@ -283,7 +283,7 @@ impl PackedFormat for BunProgram {
let kind = format!(
"loader={loader} format={} encoding={} side={} sourcemap_bytes={} bytecode_bytes={}",
label(&["none", "esm", "cjs"], m.format),
label(&["binary", "latin1", "utf8"], m.encoding),
label(&["binary", "latin1", "utf16le"], m.encoding),
label(&["server", "client"], m.side),
m.sourcemap.len,
m.bytecode.len,
@@ -345,7 +345,7 @@ mod tests {
let app = &listing.entries[0];
assert_eq!(app.path, "root/app.js");
assert_eq!(BunProgram.read(data, app, 1000).unwrap(), b"console.log('app')");
assert_eq!(app.kind.as_deref(), Some("loader=js format=esm encoding=utf8 side=server sourcemap_bytes=9 bytecode_bytes=10"));
assert_eq!(app.kind.as_deref(), Some("loader=js format=esm encoding=latin1 side=server sourcemap_bytes=9 bytecode_bytes=10"));
let logo = &listing.entries[1];
assert_eq!(logo.path, "root/logo.png");
assert_eq!(BunProgram.read(data, logo, 1000).unwrap(), b"\x89PNG");
+32
View File
@@ -0,0 +1,32 @@
use craft_anything_formats::bun_program::BunProgram;
use craft_anything_formats::packed::PackedFormat;
use craft_anything_testkit::packed::{BunModule, bun_appended, bun_graph, bun_module};
#[test]
fn reports_stored_text_encodings_without_transcoding_bytes() {
let text = "café 🍵\n";
let wide: Vec<u8> = text.encode_utf16().flat_map(u16::to_le_bytes).collect();
let modules = [
BunModule { encoding: 2, loader: 13, format: 0, ..bun_module("/$bunfs/root/wide.txt", &wide) },
BunModule { encoding: 1, loader: 13, format: 0, ..bun_module("/$bunfs/root/latin.txt", b"caf\xe9") },
BunModule { encoding: 0, loader: 5, format: 0, ..bun_module("/$bunfs/root/raw.bin", b"\x00\xff\x80") },
];
for record_bytes in [36, 52] {
for offsets_bytes in [24, 32] {
let bytes = bun_appended(&bun_graph(&modules, 0, "", record_bytes, offsets_bytes));
let listing = BunProgram.list(&bytes).unwrap();
assert_eq!(listing.entries.len(), 3);
for (entry, (encoding, expected)) in listing.entries.iter().zip([("utf16le", wide.as_slice()), ("latin1", b"caf\xe9"), ("binary", b"\x00\xff\x80")])
{
assert!(entry.kind.as_deref().unwrap().split_whitespace().any(|part| part == format!("encoding={encoding}")), "{:?}", entry.kind);
assert_eq!(BunProgram.read(&bytes, entry, 1024).unwrap(), expected);
}
let raw = BunProgram.read(&bytes, &listing.entries[0], 1024).unwrap();
assert!(std::str::from_utf8(&raw).is_err());
let (pairs, remainder) = raw.as_chunks::<2>();
assert!(remainder.is_empty());
let units: Vec<u16> = pairs.iter().copied().map(u16::from_le_bytes).collect();
assert_eq!(String::from_utf16(&units).unwrap(), text);
}
}
}
+1 -1
View File
@@ -353,7 +353,7 @@ pub struct BunModule<'a> {
/// A JavaScript module as `bun build --compile` stores one.
pub fn bun_module<'a>(name: &'a str, contents: &'a [u8]) -> BunModule<'a> {
BunModule { name, contents, sourcemap: b"", bytecode: b"", encoding: 2, loader: 1, format: 1, side: 0 }
BunModule { name, contents, sourcemap: b"", bytecode: b"", encoding: 1, loader: 1, format: 1, side: 0 }
}
fn bun_put(o: &mut Vec<u8>, b: &[u8]) -> (u32, u32) {
+1
View File
@@ -1124,6 +1124,7 @@ should give away more of how it is set up.
Owner: open · Status: in progress
Read what real apps and games ship as (one file carrying the rest), and let a rebuilt website run
against what its original was recorded answering.
- [x] Identify Bun UTF-16LE text members (#257). — claimed: Afloat16, branch bugfix/r3.11-bun-text-encoding-20261102
- [x] One seam for files packed inside a file (`formats::packed`), with `core_packed_files_list`
and `core_packed_files_unpack`; a new kind is one reader and one table line
- [x] Readers: Bun and Node.js single-file programs, .NET single-file apps, PyInstaller, Godot PCK.
+7
View File
@@ -29,6 +29,13 @@ workspace clippy, layers, fmt and the secret scan of all history were clean. Chi
| CLI | Every tool, `--deep` or `--clean-room` for one run (never saved), `rebuild` (including `--continue-in`, and `--resume` to carry on a stopped build), `mcp --inbox`, `ai`, `setup`, `doctor`, `report` | CLI integration tests |
| SQLite database files (`db_sqlite_show`) | A rowid alias column (`INTEGER PRIMARY KEY`, including the table-constraint form) shows the rowid; `DESC`, non-`INTEGER` and composite keys keep their stored values | Hand-built fixtures; generated files compared with the official SQLite library |
### Bun stored-text encoding
Packed Bun members with encoding value 2 are labelled `utf16le`, matching their stored UTF-16LE
bytes. Reading or unpacking preserves those bytes. Synthetic graphs cover UTF-16LE (including a
surrogate pair), Latin1 and binary members across both supported module-record and offsets-block
sizes. A freshly compiled Bun program and the CLI/MCP transport were not run for this correction.
### Linux Unreal setup
Unreal installations registered in `~/.config/Epic/UnrealEngine/Install.ini` are found on Linux.