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
5 changes: 5 additions & 0 deletions .changeset/pod_collections_and_refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
pina_pod_primitives: minor
---

Add `PodOption<T>`, `PodString<N, PFX>`, and `PodVec<T, N, PFX>` fixed-capacity collection types for zero-copy Solana account layouts. Split the monolithic `lib.rs` into a multi-file module structure for maintainability. Add kani proof harnesses for collection types.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ level = "warn"
check-cfg = [
'cfg(target_os, values("solana"))',
'cfg(target_feature, values("static-syscalls"))',
'cfg(kani)',
]

[workspace.lints.clippy]
Expand Down
42 changes: 42 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,48 @@

All notable changes to this project will be documented in this file.

## Unreleased

### Features

#### Add PodOption, PodString, and PodVec collection types (pina_pod_primitives)

Fixed-capacity, alignment-1 collection types for zero-copy Solana account layouts:

- `PodOption<T: Pod>` — fixed-size optional with 1-byte discriminant
- `PodString<N, PFX=1>` — fixed-capacity string with length prefix
- `PodVec<T: Pod, N, PFX=2>` — fixed-capacity vector with length prefix

All implement `bytemuck::Pod` + `bytemuck::Zeroable`. Overflow returns `PodCollectionError`.

New mdt providers:

- `podCollectionTypesTable` — collection types reference table
- `podCollectionDescription` — collection type semantics

Updated documentation:

- `pina_pod_primitives/readme.md` — added collection types section
- `docs/src/core-concepts.md` — added Pod collection types section
- `docs/src/crates-and-features.md` — added collection types and description
- `readme.md` — added Pod collection types section
- Updated `podTypesTable` and `podArithmeticDescription` providers to clarify integer-only scope

### Refactoring

#### Split `pina_pod_primitives/lib.rs` into multi-file module structure

The monolithic `lib.rs` (2322 lines) was split into focused modules:

- `pod_bool.rs` — PodBool type
- `pod_numeric.rs` — PodU16..PodI128 via macros
- `macros.rs` — define_pod_unsigned!/define_pod_signed! etc.
- `error.rs` — PodCollectionError
- `option.rs` — PodOption + kani proofs
- `string.rs` — PodString + kani proofs
- `vec.rs` — PodVec + kani proofs
- `tests/` — unit tests per type

## 0.8.0 (2026-03-30)

### Breaking Changes
Expand Down
32 changes: 28 additions & 4 deletions crates/pina_pod_primitives/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

<br>

Alignment-safe primitive POD wrappers used by Pina and generated Codama Rust clients.
Alignment-safe primitive POD wrappers and fixed-capacity collection types used by Pina and generated Codama Rust clients.

[![Crates.io][crate-image]][crate-link] [![Docs.rs][docs-image]][docs-link] [![CI][ci-status-image]][ci-status-link] [![License][unlicense-image]][unlicense-link] [![codecov][codecov-image]][codecov-link]

This crate provides `PodBool`, `PodU16`, `PodI16`, `PodU32`, `PodI32`, `PodU64`, `PodI64`, `PodU128`, and `PodI128` for use in `#[repr(C)]` zero-copy layouts.
This crate provides `PodBool`, `PodU16`, `PodI16`, `PodU32`, `PodI32`, `PodU64`, `PodI64`, `PodU128`, and `PodI128` for use in `#[repr(C)]` zero-copy layouts, plus fixed-capacity collection types `PodOption<T>`, `PodString<N, PFX>`, and `PodVec<T, N, PFX>`.

## Arithmetic

<!-- {=podArithmeticDescription} -->

Arithmetic operators (`+`, `-`, `*`) use **wrapping** semantics in release builds for CU efficiency and **panic on overflow** in debug builds. Use `checked_add`, `checked_sub`, `checked_mul`, `checked_div` where overflow must be detected in all build profiles.
Arithmetic operators (`+`, `-`, `*`) on Pod **integer** types use **wrapping** semantics in release builds for CU efficiency and **panic on overflow** in debug builds. Use `checked_add`, `checked_sub`, `checked_mul`, `checked_div` where overflow must be detected in all build profiles.

Each Pod integer type provides `ZERO`, `MIN`, and `MAX` constants.

Expand All @@ -26,7 +26,7 @@ Each Pod integer type provides `ZERO`, `MIN`, and `MAX` constants.
cargo add pina_pod_primitives
```

## Types
## Integer types

<br>

Expand All @@ -48,6 +48,30 @@ All types are `#[repr(transparent)]` over byte arrays (or `u8` for `PodBool`) an

<!-- {/podTypesTable} -->

## Collection types

<br>

<!-- {=podCollectionTypesTable} -->

| Type | Purpose | Layout |
| -------------------------- | ---------------------- | ----------------------------------------- |
| `PodOption<T: Pod>` | Fixed-size `Option<T>` | 1-byte discriminant + `T` |
| `PodString<N, PFX=1>` | Fixed-capacity string | `PFX`-byte length prefix + `N` data bytes |
| `PodVec<T: Pod, N, PFX=2>` | Fixed-capacity vec | `PFX`-byte length prefix + `N` elements |

All collection types are `#[repr(C)]`, alignment-1, and implement `bytemuck::Pod` + `bytemuck::Zeroable`. Length prefixes (`PFX`) default to 1 byte for strings (max 255) and 2 bytes for vectors (max 65 535 elements).

<!-- {/podCollectionTypesTable} -->

<!-- {=podCollectionDescription} -->

Collection types store data inline with a length prefix, enabling zero-copy access inside `#[repr(C)]` account structs. Overflow is detected at insertion time — `try_set` / `try_push` return `Err(PodCollectionError::Overflow)` when capacity is exceeded.

`PodString` provides UTF-8 validation via `try_as_str()`, while `PodVec` offers slice-based access via `as_slice()` / `as_mut_slice()`. `PodOption` mirrors the `Option<T>` API with `get()`, `set()`, and `clear()`.

<!-- {/podCollectionDescription} -->

[crate-image]: https://img.shields.io/crates/v/pina_pod_primitives.svg?style=flat-square
[crate-link]: https://crates.io/crates/pina_pod_primitives
[docs-image]: https://docs.rs/pina_pod_primitives/badge.svg
Expand Down
35 changes: 35 additions & 0 deletions crates/pina_pod_primitives/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Error type for Pod collection operations.

use core::fmt;

/// Error type for collection operations.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PodCollectionError {
/// Value exceeds capacity.
Overflow,
/// Invalid UTF-8 in string data.
InvalidUtf8,
/// Index out of bounds.
OutOfBounds,
}

impl fmt::Display for PodCollectionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Overflow => write!(f, "value exceeds capacity"),
Self::InvalidUtf8 => write!(f, "invalid UTF-8"),
Self::OutOfBounds => write!(f, "index out of bounds"),
}
}
}

/// Returns the maximum `N` value representable by a `PFX`-byte length prefix.
pub(crate) const fn max_n_for_pfx(pfx: usize) -> usize {
match pfx {
1 => u8::MAX as usize,
2 => u16::MAX as usize,
4 => u32::MAX as usize,
8 => usize::MAX,
_ => 0,
}
}
Loading
Loading