Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 3 additions & 9 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@ indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

[*.zig]
[*.{zig,py}]
max_line_length = 100

[*.md]
max_line_length = 120
max_line_length = 150
trim_trailing_whitespace = false

[*.sh]
[*.{yml,yaml,json}]
indent_size = 2

[*.{yml,yaml}]
indent_size = 2

[*.py]
max_line_length = 100
4 changes: 3 additions & 1 deletion .github/workflows/benches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name: Run Benchmarks
on:
workflow_dispatch:
push:
branches:
- develop
tags:
- 'v*'
pull_request:
Expand All @@ -23,7 +25,7 @@ jobs:
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: '0.15.2'
version: '0.16.0'

- name: Install Dependencies
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: '0.15.2'
version: '0.16.0'

- name: Install System Dependencies
run: |
Expand Down
34 changes: 0 additions & 34 deletions .github/workflows/lints.yml

This file was deleted.

45 changes: 34 additions & 11 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,55 @@ name: Run Tests
on:
workflow_dispatch:
push:
branches:
- develop
tags:
- 'v*'
pull_request:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
tests:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
zig-url: https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz
zig-dir: zig-x86_64-linux-0.16.0
- os: macos-latest
zig-url: https://ziglang.org/download/0.16.0/zig-aarch64-macos-0.16.0.tar.xz
zig-dir: zig-aarch64-macos-0.16.0
- os: windows-latest
zig-url: https://ziglang.org/download/0.16.0/zig-x86_64-windows-0.16.0.zip
zig-dir: zig-x86_64-windows-0.16.0

steps:
- name: Checkout Repository
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: '0.15.2'
- name: Install Zig 0.16.0 (Unix)
if: runner.os != 'Windows'
run: |
curl -sSfL ${{ matrix.zig-url }} | tar -xJ
echo "$PWD/${{ matrix.zig-dir }}" >> "$GITHUB_PATH"

- name: Install Dependencies
- name: Install Zig 0.16.0 (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
sudo apt-get update
sudo apt-get install -y make
Invoke-WebRequest -Uri "${{ matrix.zig-url }}" -OutFile zig.zip
Expand-Archive zig.zip -DestinationPath .
echo "$PWD\${{ matrix.zig-dir }}" | Out-File -Append -FilePath $env:GITHUB_PATH

- name: Run the Tests
run: make test
- name: Run tests
run: zig build test --summary all
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,5 @@ docs/api/
*.dll
*.exe
latest
.claude/
.codex
155 changes: 155 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# AGENTS.md

This file provides guidance to coding agents collaborating on this repository.

## Mission

Ordered is a sorted-collection library for Zig. It provides in-memory data structures that keep their elements sorted:
two set types (value-only) and four map types (key-value), all exposing a small, uniform API for insertion, lookup,
removal, and in-order iteration.
Priorities, in order:

1. Correctness of ordering, iteration, and memory management across insertions and removals.
2. Minimal public API that is consistent across every container type.
3. Zero non-Zig dependencies, maintainable, and well-tested code.
4. Cross-platform support (Linux, macOS, and Windows).

## Core Rules

- Use English for code, comments, docs, and tests.
- Prefer small, focused changes over large refactoring.
- Add comments only when they clarify non-obvious behavior.
- Do not add features, error handling, or abstractions beyond what is needed for the current task.
- Keep the project dependency-free: no external Zig packages or C libraries unless explicitly agreed.

## Writing Style

- Use Oxford commas in inline lists: "a, b, and c" not "a, b, c".
- Do not use em dashes. Restructure the sentence, or use a colon or semicolon instead.
- Avoid colorful adjectives and adverbs. Write "TCP proxy" not "lightweight TCP proxy", "scoring components" not "transparent scoring components".
- Use noun phrases for checklist items, not imperative verbs. Write "redundant index detection" not "detect redundant indexes".
- Headings in Markdown files must be in the title case: "Build from Source" not "Build from source". Minor words (a, an, the, and, but, or, for, in,
on, at, to, by, of, is, are, was, were, be) stay lowercase unless they are the first word.

## Repository Layout

- `src/lib.zig`: Public API entry point. Re-exports `SortedSet`, `RedBlackTreeSet`, `BTreeMap`, `SkipListMap`, `TrieMap`, and `CartesianTreeMap`.
- `src/ordered/sorted_set.zig`: `SortedSet` (insertion-sorted `std.ArrayList` backed by a linear scan for insert and removal).
- `src/ordered/red_black_tree_set.zig`: `RedBlackTreeSet` (self-balancing BST; takes an explicit three-way comparison function, consistent with the other generic-key containers).
- `src/ordered/btree_map.zig`: `BTreeMap` (cache-friendly B-tree with configurable branching factor).
- `src/ordered/skip_list_map.zig`: `SkipListMap` (probabilistic skip list with a per-instance PRNG).
- `src/ordered/trie_map.zig`: `TrieMap` (prefix tree, specialised for `[]const u8` keys).
- `src/ordered/cartesian_tree_map.zig`: `CartesianTreeMap` (treap combining BST ordering with max-heap priorities; takes an explicit key-comparison function).
- `examples/`: Self-contained example programs (`e1_btree_map.zig` through `e6_cartesian_tree_map.zig`) built as executables via `build.zig`.
- `benches/`: Benchmark programs (`b1_btree_map.zig` through `b6_cartesian_tree_map.zig`) built in `ReleaseFast`.
- `benches/util/timer.zig`: Internal compatibility shim for the removed `std.time.Timer`, backed by `std.Io.Timestamp`.
- `.github/workflows/`: CI workflows (`tests.yml` for unit tests on Linux, macOS, and Windows, `docs.yml`, `lints.yml`, and `benches.yml`).
- `build.zig` / `build.zig.zon`: Zig build configuration and package metadata.
- `Makefile`: GNU Make wrapper around `zig build` targets.
- `docs/`: Generated API docs land in `docs/api/` (produced by `make docs`).

## Architecture

### Common API Across Containers

Every map exposes `init(allocator)`, `deinit()`, `count()`, `contains(key)`, `get(key)`, `getPtr(key)`, `put(key, value)`,
`remove(key)`, and `iterator()`. Every set exposes the same shape with value-only variants. New containers should
adopt this surface instead of inventing new method names.

### Iteration

Iterators return entries in the natural sort order of the underlying structure. Modifying a container during
iteration is undefined behaviour; each container's docstring states this explicitly. New iterators should follow
the same convention and document the contract.

### Randomised Containers

`SkipListMap` and `CartesianTreeMap` use randomness internally (levels and priorities, respectively). Each instance
owns its own `std.Random.DefaultPrng`, seeded at `init` from an ASLR-derived hash of a stack address, which is
sufficient randomness for skip-list level selection and treap priority generation. Callers do not need to provide
a seed. Tests that require deterministic behaviour should use the explicit `*WithPriority` / level-setting API
on the container rather than seeding the PRNG directly.

### Public API Surface

Everything re-exported from `src/lib.zig` is part of the public API. Changes to names, signatures, or semantics
there are breaking. The rest of `src/ordered/` is internal and may be refactored freely as long as the public
surface and its behavior are preserved.

### Dependencies

Ordered has **no external Zig or C dependencies**.
The only `build.zig.zon` entries should be Ordered itself.
Please do not add dependencies without prior discussion.

## Zig Conventions

- Zig version: 0.16.0 (as declared in `build.zig.zon` and the Makefile's `ZIG_LOCAL` path).
- Formatting is enforced by `zig fmt`. Run `make format` before committing.
- Naming follows Zig standard-library conventions: `camelCase` for functions (e.g. `getPtr`, `keysWithPrefix`), `snake_case` for local variables and
struct fields, `PascalCase` for types and structs, and `SCREAMING_SNAKE_CASE` for top-level compile-time constants.

## Required Validation

Run the relevant targets for any change:

| Target | Command | What It Runs |
|------------------|-------------------------------------|-------------------------------------------------------------------|
| Unit tests | `make test` | Inline `test` blocks across `src/lib.zig` and `src/ordered/*.zig` |
| Lint | `make lint` | Checks Zig formatting with `zig fmt --check src examples` |
| Single example | `make run EXAMPLE=e1_btree_map` | Builds and runs one example program |
| All examples | `make run` | Builds and runs every example under `examples/` |
| Single benchmark | `make bench BENCHMARK=b1_btree_map` | Builds (ReleaseFast) and runs one benchmark program |
| All benchmarks | `make bench` | Builds and runs every benchmark under `benches/` |
| Docs | `make docs` | Generates API docs into `docs/api` |
| Everything | `make all` | Runs `build`, `test`, `lint`, and `docs` |

## First Contribution Flow

1. Read the relevant module under `src/ordered/` (often one container file per change).
2. Implement the smallest change that covers the requirement.
3. Add or update inline `test` blocks in the changed Zig module to cover the new behavior.
4. Run `make test` and `make lint`.
5. If the change could affect performance, also run the corresponding benchmark with `make bench BENCHMARK=bN_*`.

Good first tasks:

- New example under `examples/` demonstrating a container method, listed in `examples/README.md`.
- Additional inline `test` block covering a boundary case (empty container, single element, duplicate key, unicode key).
- Performance improvement in an existing container, paired with benchmark output before/after.
- Documentation refinement in a container module's top-level docstring.

## Testing Expectations

- Unit and regression tests live as inline `test` blocks in the module they cover (`src/lib.zig` and `src/ordered/*.zig`). There is no separate
`tests/` directory.
- Tests are discovered automatically via `std.testing.refAllDecls(@This())` in `src/lib.zig`, so new `test` blocks only need to live in a module that
is reachable from `lib.zig`.
- Every new public function or container branch must ship with at least one `test` block that exercises it, including the error paths where
applicable.
- Memory tests should use `std.testing.allocator` so leaks are caught automatically.
- No public API change is complete without a test covering the new or changed behavior.

## Change Design Checklist

Before coding:

1. Modules affected by the change (which container file, or `lib.zig` if the public surface moves).
2. Whether the change alters ordering, iteration, or removal semantics, and therefore needs explicit test coverage.
3. Public API impact, i.e. whether the change adds to or alters anything re-exported from `src/lib.zig`, and is therefore additive or breaking.
4. Cross-platform implications, especially for anything that touches timing, the filesystem, or non-deterministic ordering.

Before submitting:

1. `make test` passes.
2. `make lint` passes.
3. `make bench` still succeeds for any benchmark whose container was touched, with a note on whether performance moved.
4. Docs updated (`make docs`) if the public API surface changed, and `README.md` ticked or updated if a container was added or removed.

## Commit and PR Hygiene

- Keep commits scoped to one logical change.
- PR descriptions should include:
1. Behavioral change summary.
2. Tests added or updated.
3. Whether benchmarks were run locally (yes/no), and on which OS, with before/after numbers when relevant.
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ would like to work on or if it has already been resolved.
### Development Workflow

> [!IMPORTANT]
> If you're using an AI-assisted coding tool like Claude Code or Codex, make sure the AI follows the instructions in the [AGENTS.md](AGENTS.md) file.
#### Prerequisites

Install GNU Make on your system if it's not already installed.
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# ################################################################################
# # Configuration and Variables
# ################################################################################
ZIG ?= $(shell which zig || echo ~/.local/share/zig/0.15.2/zig)
ZIG_LOCAL := $(HOME)/.local/share/zig/0.16.0/zig
ZIG ?= $(shell test -x $(ZIG_LOCAL) && echo $(ZIG_LOCAL) || which zig)
BUILD_TYPE ?= Debug
BUILD_OPTS = -Doptimize=$(BUILD_TYPE)
JOBS ?= $(shell nproc || echo 2)
Expand Down
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@

[![Tests](https://img.shields.io/github/actions/workflow/status/CogitatorTech/ordered/tests.yml?label=tests&style=flat&labelColor=282c34&logo=github)](https://github.com/CogitatorTech/ordered/actions/workflows/tests.yml)
[![Benchmarks](https://img.shields.io/github/actions/workflow/status/CogitatorTech/ordered/benches.yml?label=benchmarks&style=flat&labelColor=282c34&logo=github)](https://github.com/CogitatorTech/ordered/actions/workflows/benches.yml)
[![CodeFactor](https://img.shields.io/codefactor/grade/github/CogitatorTech/ordered?label=quality&style=flat&labelColor=282c34&logo=codefactor)](https://www.codefactor.io/repository/github/CogitatorTech/ordered)
[![Zig Version](https://img.shields.io/badge/Zig-0.15.2-orange?logo=zig&labelColor=282c34)](https://ziglang.org/download/)
[![Zig Version](https://img.shields.io/badge/Zig-0.16.0-orange?logo=zig&labelColor=282c34)](https://ziglang.org/download/)
[![Release](https://img.shields.io/github/release/CogitatorTech/ordered.svg?label=release&style=flat&labelColor=282c34&logo=github)](https://github.com/CogitatorTech/ordered/releases/latest)
<br>
[![Docs](https://img.shields.io/badge/docs-read-blue?style=flat&labelColor=282c34&logo=read-the-docs)](https://CogitatorTech.github.io/ordered/)
[![Examples](https://img.shields.io/badge/examples-view-green?style=flat&labelColor=282c34&logo=zig)](https://github.com/CogitatorTech/ordered/tree/main/examples)
[![License](https://img.shields.io/badge/license-MIT-007ec6?label=license&style=flat&labelColor=282c34&logo=open-source-initiative)](https://github.com/CogitatorTech/ordered/blob/main/LICENSE)
Expand Down Expand Up @@ -78,9 +76,18 @@ Run the following command in the root directory of your project to download Orde
zig fetch --save=ordered "https://github.com/CogitatorTech/ordered/archive/<branch_or_tag>.tar.gz"
```

Replace `<branch_or_tag>` with the desired branch or release tag, like `main` (for the development version) or `v0.1.0`.
Replace `<branch_or_tag>` with the desired branch or release tag, like `main` (for the development version) or `v0.3.0`.
This command will download Ordered and add it to Zig's global cache and update your project's `build.zig.zon` file.

Zig version supported by each tagged release:

| Zig | Ordered Tags |
|----------|--------------|
| `0.16.0` | `v0.2.x` |
| `0.15.2` | `v0.1.0` |

The `main` branch normally is developed and build using the latest (non-developmental) Zig release.

#### Adding to Build Script

Next, modify your `build.zig` file to make Ordered available to your build target as a module.
Expand Down
4 changes: 2 additions & 2 deletions benches/b1_btree_map.zig
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const std = @import("std");
const ordered = @import("ordered");
const Timer = std.time.Timer;
const Timer = @import("util/timer.zig").Timer;

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

Expand Down
4 changes: 2 additions & 2 deletions benches/b2_sorted_set.zig
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const std = @import("std");
const ordered = @import("ordered");
const Timer = std.time.Timer;
const Timer = @import("util/timer.zig").Timer;

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

Expand Down
Loading
Loading