Repository navigation
v0.8.0 - header first, streams, rows, UTF-16 text, async, and Android
HyperTabular 0.8.0 answers the first downstream feedback (#8–#12, filed against the C# binding) in every binding the language allows, and adds Android for C#, Go and Swift. It is built on HyperCast 0.8.0, the first HyperCast with Android. The native ABI is unchanged: the core's calls were always two-phase. The full list is in CHANGELOG.md.
Install
| Language | Package |
|---|---|
| Rust | cargo add hypertabular |
| C# (.NET 11) | dotnet add package HyperTabular |
| Java (JDK 25) | implementation("io.github.skunkwerkx:hypertabular:0.8.0") |
| Go | go get github.com/SkunkWerkx/HyperTabular/go@v0.8.0 |
| Swift | .package(url: "https://github.com/SkunkWerkx/HyperTabular", from: "0.8.0") |
| Python (3.11+) | pip install hypertabular |
| Ruby (3.3+) | gem install hypertabular |
| PHP (8.2+) | composer require skunkwerkx/hypertabular |
Highlights
- Header first, plan after (#8). A delimited reader or a sheet opens without a plan and reads its header. You resolve column names to ordinals, then bind the plan once.
- Name lookup is exact and case-sensitive, and returns the first match. A missing name is an error that names the column.
- This works over a forward-only stream, with no second pass.
- A plan known by position can still be given up front, as before.
- Workbooks from a stream (#9). Embedded resources, HTTP responses, any stream: it is read to the end into memory the workbook owns. The format is detected from the bytes.
- Rows (#10). A batch can also be read across, a row at a time, and every reader and sheet iterates rows across batches. The storage stays column-major. In C#,
Rowis aref structand allocates nothing. - Text without strings (#11).
- C#
GetCharsand Javacharsdecode a text cell to UTF-16 in a batch-owned arena, ready for span-based parsers, without making a string. - Go
TextStringviews the cell as astringwithout copying it. - Rust
text_strborrows the cell as astr.
- C#
- Async with cancellation (#12).
- C#:
ReadAsync,OpenAsyncandIAsyncDisposable. These work on Blazor WebAssembly, whose browser streams cannot be read synchronously. - Swift:
readAsyncand async initializers. - Python: asyncio.
- Rust: the
asyncfeature. - A cancelled read loses nothing, and the next read continues from where it was.
- C#:
- Android: arm64 and x86_64, 16 KB pages.
- C#: CoreCLR and Native AOT, for .NET MAUI.
- Go:
GOOS=android. - Swift: the Swift SDK for Android.
- CI builds the APKs and checks them with
zipalign -P 16, then runs the x86_64 builds and the Go and Swift suites in an emulator.
By binding
| Header lookup | Plan-less open | Rows | Native text | Async | |
|---|---|---|---|---|---|
| Rust | require / ordinal |
*_unbound |
for row in batch |
text_str |
async feature |
| C# | Ordinal / TryOrdinal |
overloads + Bind |
foreach, Rows() |
GetChars |
ReadAsync, OpenAsync |
| Java | ordinal / findOrdinal |
overloads + bind |
Iterable<Row>, forEachRow |
chars |
— |
| Go | Ordinal / Find |
*Unbound + Bind |
All() iterators |
TextString |
— |
| Swift | ordinal(of:) / firstIndex(of:) |
overloads + bind |
Collection of Row |
— | readAsync |
| Python | ordinal / get |
plan=None + bind |
iter(batch), rows() |
— | asyncio |
| Ruby | ordinal / find_ordinal |
plan = nil + bind |
Enumerable |
— | — |
| PHP | ordinal / find |
?array $plan = null + bind |
foreach, rows() |
— | — |
A dash means the language has no use for the feature, not that it is missing. For example, Python, Ruby and PHP strings are already native, Go cancels through its io.Reader, and Java's virtual threads make a blocking read the norm. docs/design.md gives the reason for each.
Upgrading from 0.7
- HyperCast 0.8.0 is the floor in every package.
- C#, Java, Swift: the header property is now a
Header, which is still a list of strings.- Code compiled against 0.7 needs recompiling.
- In Swift, wrap it in
Array(_:)where a[String]is expected.
- Go:
Header()returnsHeader, a named[]string. Only reflection-based comparisons, such asreflect.DeepEqualor testify'sEqual, need[]string(header). - Rust:
ErrorgainsNoColumn,UnboundandAlreadyBound. Amatchthat names every variant needs updating. - Ruby: a
nilplan now means "bind later". Pass[]for an empty plan.
Verifying
Every artifact is built in CI, and all but one are attested, as in 0.7.0:
gh attestation verify <file> --repo SkunkWerkx/HyperTabular
The one exception, found after this release went out: the browser-wasm archive inside the NuGet package, runtimes/browser-wasm/nativeassets/net10.0/libhypertabular.a, the one a Blazor WebAssembly app links. It was built by the same CI workflow as the rest but never attested, so gh attestation verify finds no provenance for it. Everything else in the package, and every other artifact of this release, verifies. 0.9.0 attests the archive where it is built and checks every native before packing.