# isbn.zig
A Zig library for parsing, validating, and converting International
Standard Book Numbers (ISBNs) in both the 10-digit and 13-digit
formats.
The API documentation is generated from the doc comments in the source
and published at . To read it
locally instead, `zig build docs` builds it into `zig-out/docs` and
`zig build docs-serve` serves it on . A server
is needed rather than opening the files directly, because the viewer
fetches `sources.tar` and `main.wasm` at runtime and a browser refuses
those from a `file://` page.
## Features
- Parse ISBNs from text, with or without `-` separators.
- Validate check digits during parsing (including the `X` check digit
used by ISBN-10).
- Compute check digits, optionally using SIMD vector operations.
- Upgrade an ISBN-10 to its equivalent ISBN-13.
- Compare ISBNs across formats: an ISBN-10 is equal to its 13-digit
equivalent.
- Format ISBNs with proper hyphenation, using the International ISBN
Agency's registration group and registrant range data.
## Requirements
- Zig 0.16.0 or later
## Installation
Add the dependency to your project:
```sh
zig fetch --save git+https://tangled.org/jcollie.dev/isbn.zig
```
Then wire it up in your `build.zig`:
```zig
const isbn = b.dependency("isbn", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("isbn", isbn.module("isbn"));
```
## Usage
```zig
const ISBN = @import("isbn").ISBN;
// Parsing validates the check digit.
const ten: ISBN = try .parse("0-306-40615-2");
const thirteen: ISBN = try .parse("978-0-306-40615-7");
// Upgrade an ISBN-10 to an ISBN-13.
const upgraded = ten.upgrade();
// Comparison works across formats.
std.debug.assert(ten.eql(&thirteen));
// Hyphenate explicitly, or format with {f}.
var buf: [ISBN.max_hyphenated_len]u8 = undefined;
const hyphenated = try upgraded.hyphenate(&buf);
std.debug.print("{s}\n", .{hyphenated}); // 978-0-306-40615-7
std.debug.print("{f}\n", .{ten}); // 0-306-40615-2
```
`parse` returns `error.ParseError` for malformed input and
`error.InvalidCheckDigit` when the check digit doesn't match.
## Build options
- `-Dsimd=[bool]` — use SIMD vector operations for computing check
digits (default: `false`). It is off because it does not pay: `zig
build bench` measures both implementations, and nine or twelve lanes
of multiply-and-add do not earn back the cost of widening the digits
and assembling the vector.
- `-Ddocs-port=[u16]` — port for `zig build docs-serve` (default:
`8000`).
## Standards
| Standard | Title | Support in isbn.zig |
| --- | --- | --- |
| [ISO 2108](https://www.iso.org/standard/65483.html) | International Standard Book Number | Parsing and validating both the 10- and 13-digit forms, the two check digit algorithms, and conversion from 10 to 13 |
| [ISBN range message](https://www.isbn-international.org/range_file_generation) | Registration group and registrant ranges | Hyphenation, from the International ISBN Agency's published ranges, generated into `src/ranges.zig` |
| [EAN/UPC (GS1)](https://www.gs1.org/standards/barcodes/ean-upc) | Article numbering | The 978 and 979 prefixes an ISBN-13 is built on |
ISO 2108 itself is not freely available. The check digit algorithms and the
978/979 prefixes are documented in the ISBN Users' Manual, which is:
## Range data
Hyphenation uses the registration group and registrant ranges published
by the [International ISBN Agency](https://www.isbn-international.org/),
embedded at build time as `src/ranges.zig`. To update them:
```sh
zig build update-ranges
```
## Testing
```sh
zig build test
```
The test suite includes unit tests and a fuzz test.
`zig build check` compiles everything that `zig build test` does not
run — the benchmark, the documentation server, and the range generator
— so that none of them can quietly stop compiling.
`zig build bench` compares the SIMD and scalar check digit
implementations against each other; it calls both directly, so the
`-Dsimd` setting does not affect what it measures.
## License
[MIT](LICENSES/MIT.txt). This project is compliant with the
[REUSE Specification](https://reuse.software/).