Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
38bfde3
Allocate blocklist ArrayList in one go
lvignoli Aug 21, 2025
1b36513
Keep stack allocated the working alphabet
lvignoli Aug 21, 2025
e8104ed
Remove toID and inline the ID generation
lvignoli Aug 22, 2025
cdee75d
Add buffer size estimation
lvignoli Aug 22, 2025
f07c238
Fix array list not deallocated after error
lvignoli Aug 22, 2025
2709fc5
Refactor internal encoding to reuse the same memory
lvignoli Aug 22, 2025
87f4392
Remove allocation when checking for blocked ID
lvignoli Aug 24, 2025
ea9ab64
Allocate working alphabet on the stack when encoding
lvignoli Aug 24, 2025
02c1405
Draft blocklist object
lvignoli Aug 20, 2025
cee3330
Better Blocklist
lvignoli Sep 4, 2025
e8569ba
Upgrade test runner to zig 0.16.
lvignoli May 9, 2026
edb2f7e
Upgrade to zig 0.16.
lvignoli May 9, 2026
3c7839f
Make Sqid unmanaged
lvignoli May 9, 2026
2c49aca
Rename Sqid.init to Sqid.new
lvignoli May 9, 2026
a4a3892
build.zig: Add a runnable main program
lvignoli May 9, 2026
decdcf3
Remove Blocklist object
lvignoli May 11, 2026
ecf0c59
Update main to run
lvignoli May 11, 2026
2093149
Better initialization
lvignoli May 11, 2026
1450a40
Upgrade CI to Zig 0.16
lvignoli May 11, 2026
b427cd1
Do not build docs on pull request
lvignoli May 11, 2026
dbee314
Update CI for 0.16 only, change zig setup action
lvignoli May 11, 2026
570ac10
Update bench.zig to be runnable
lvignoli May 11, 2026
7b9ed6c
Bump version to 0.4
lvignoli May 11, 2026
e0b1b8f
Add a changelog
lvignoli May 11, 2026
c0e0f08
Update README.md
lvignoli May 14, 2026
82c003c
Empty blocklist is slice of length 0, not undefined
lvignoli May 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ jobs:
strategy:
fail-fast: false
matrix:
zig: [0.13.0, master]
zig: [0.16.0]
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
name: Zig ${{ matrix.zig }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup Zig
uses: korandoru/setup-zig@v1
uses: mlugg/setup-zig@v2
with:
zig-version: ${{ matrix.zig }}
- run: zig build test
2 changes: 0 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name: docs
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
workflow_dispatch:

jobs:
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Changelog

## Unreleased

## [v0.4.0]

This release contains performance improvements, breaking changes in public API, and upgrades the
library to Zig 0.16.

## Changed
- Use `Sqids.init` to create a new Sqids encoder/decoder instance.
There is no need for an allocator anymore.
User is responsible for managing the blocklist.
There is no need to deinit instances.
- `Sqids.encode` and `Sqids.decode` now both take an allocator. Caller owns the memory.

## Added
- `blocklist_from_words` is now a public function, intended for users to create minimal blocklists for their input alphabet.
- `Sqids.default` is a handy shortcut to a Sqids instance with default alphabet, default blocklist
and no minimum length.

## Removed
- `Sqids.deinit`

[0.4.0]: https://github.com/sqids/sqids-zig/compare/v0.3.0...v0.4.0
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,12 @@ const sqids = @import("sqids");
Simple encode & decode:

```zig
const s = try sqids.Sqids.init(allocator, .{})
defer s.deinit();
const s: sqids.Sqids = .default;

const id = try s.encode(&.{1, 2, 3});
const id = try s.encode(allocator, &.{1, 2, 3});
defer allocator.free(id); // Caller owns the memory.

const numbers = try s.decode(id);
const numbers = try s.decode(allocator, id);
defer allocator.free(numbers); // Caller owns the memory.
```

Expand All @@ -93,24 +92,26 @@ The `sqids.Options` struct is used at initialization to customize the encoder.
Enforce a *minimum* length for IDs:

```zig
const s = try sqids.Sqids.init(allocator, .{.min_length = 10});
const id = try s.encode(&.{1, 2, 3}); // "86Rf07xd4z"
const s = try sqids.Sqids.init(.{.min_length = 10});
const id = try s.encode(allocator, &.{1, 2, 3}); // "86Rf07xd4z"
```

Randomize IDs by providing a custom alphabet:

```zig
const s = try sqids.Sqids.init(allocator, .{.alphabet = "FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE"});
const id = try s.encode(&.{1, 2, 3}); // "B4aajs"
const s = try sqids.Sqids.init(.{.alphabet = "FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE"});
const id = try s.encode(allocator, &.{1, 2, 3}); // "B4aajs"
```

Prevent specific words from appearing anywhere in the auto-generated IDs:

```zig
const s = try sqids.Sqids.init(allocator, .{.blocklist = &.{"86Rf07"}});
const id = try s.encode(&.{1, 2, 3}); // "se8ojk"
const s = try sqids.Sqids.init(.{.blocklist = &.{"86Rf07"}});
const id = try s.encode(allocator, &.{1, 2, 3}); // "se8ojk"
```

Use `sqids.blocklist_from_words` to create the minimal blocklist consistent with the working alphabet.

## 📝 License

[MIT](LICENSE)
17 changes: 8 additions & 9 deletions benchmark/bench.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,21 @@ const numbers = numbers_file.numbers;

const Sqids = sqids.Sqids;

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

const opts = sqids.Options{ .blocklist = undefined };
pub fn main(init: std.process.Init) !void {
const allocator = init.arena.allocator();

var ids = try allocator.alloc([]const u8, numbers.len);
defer allocator.free(ids);

// Using the default Sqids, encode the numbers to a Sqids ID.
const s = try Sqids.init(allocator, opts);
defer s.deinit();
const s = try Sqids.init(.{ .blocklist = &.{} });

for (numbers, 0..) |ns, i| {
const id = try s.encode(&ns);
const id = try s.encode(allocator, &ns);
ids[i] = id;
}

allocator.free(ids);
for (ids) |id| {
allocator.free(id);
}
}
2 changes: 1 addition & 1 deletion benchmark/gen_bench_encoding_exes.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ def run(cmd):

msg += "sudo poop \\\n"
for cid in commit_ids:
msg += f"\tzig-out/bin/benchmark_random_uuids-{cid} \\\n"
msg += f"\tzig-out/bin/bench-encode-random-uuids-{cid} \\\n"

print(msg)
23 changes: 18 additions & 5 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,25 @@ pub fn build(b: *std.Build) void {
test_step.dependOn(&b.addRunArtifact(tests).step);
test_step.dependOn(&b.addRunArtifact(root_tests).step);

const exe_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.optimize = optimize,
.target = target,
// const exe_module = b.createModule(.{
// .root_source_file = b.path("src/main.zig"),
// .optimize = optimize,
// .target = target,
// });
// exe_module.addImport("sqids", sqids_module);
const exe = b.addExecutable(.{
.name = "main",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.optimize = optimize,
.target = target,
.imports = &.{.{ .name = "sqids", .module = sqids_module }},
}),
});
exe_module.addImport("sqids", sqids_module);
const run_step = b.step("run", "Run the demo program");
// const exe_artifact = b.addInstallArtifact(exe, .{});
const exe_artifact = b.addRunArtifact(exe);
run_step.dependOn(&exe_artifact.step);

const bench_module = b.createModule(.{ .root_source_file = b.path("benchmark/bench.zig"), .target = target, .optimize = optimize });
bench_module.addImport("sqids", sqids_module);
Expand Down
4 changes: 2 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.{
.name = .sqids,
.fingerprint = 0x2448d471e1087204,
.version = "0.3.0",
.minimum_zig_version = "0.14.0",
.version = "0.4.0",
.minimum_zig_version = "0.16.0",
.dependencies = .{},
.paths = .{
"build.zig",
Expand Down
2 changes: 1 addition & 1 deletion src/blocklist.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub const default_blocklist = .{
pub const default_blocklist = [_][]const u8{
"0rgasm",
"1d10t",
"1d1ot",
Expand Down
20 changes: 20 additions & 0 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const std = @import("std");
const sqids = @import("sqids");
const mem = std.mem;

const Sqids = sqids.Sqids;

pub fn main(init: std.process.Init) !void {
var arena = init.arena;
const allocator = arena.allocator();

const numbers = &.{ 1, 2, 3 };

// Using the default Sqids, encode the numbers to a Sqids ID.
const s: Sqids = .default;
const id = try s.encode(allocator, numbers);
defer allocator.free(id);

// Print to stdout.
std.debug.print("{s}\n", .{id});
}
Loading
Loading