bounded-str 0.1.4

Parse, don't validate! No-std bounded string with compile-time length limits and policies
Documentation

BoundedStr: Type-Safe Strings with Static Limits and Hybrid Storage

BoundedStr is a library for creating string types with guaranteed invariants.
It follows the "Parse, don't validate" principle: once an object is created, you can trust its length, format, and encoding.

  • Hybrid Storage: Automatic selection between stack (StackStr) and dynamic memory (FlexStr) when the byte limit is exceeded.
  • Policy-Based Design: You choose how to measure length (bytes or characters) and how to validate content (ASCII, AlphaNumeric, etc.) via traits.
  • No-Std First: Full support for embedded systems, alloc is required only for FlexStr.

Core Concepts

1. Policies

Instead of hard-coded logic, BoundedStr uses:

  • LengthPolicy: Bytes (fast, O(1)) or Chars (Unicode-correct, O(n)).
  • FormatPolicy: AllowAll, AsciiOnly, or your own rules (e.g., EmailValidator).

2. Storage Types

  • StackStr: Always on the stack. If the string does not fit in MAX_BYTES — error.
  • FlexStr: Tries to fit on the stack, but if alloc is enabled and the data is large — transparently moves to the heap. Optimized Memory: Starting from v0.1.4, FlexStr uses an internal enum. If data moves to the heap, the stack buffer is freed, significantly reducing stack pressure (SSO).

Key Features

  • Compile-time checks: Checks MIN <= MAX at compile time via const assertions.
  • Transactional Mutation: mutate() allows modifying both content &mut [u8] and length &mut usize. It automatically rolls back if the new string violates length, UTF-8, or format rules.
  • Zero-cost Deref: Implements Deref<Target=str>, works like a regular string with no overhead.
  • Security: Supports zeroize for automatic memory clearing (passwords, keys) and constant-time comparison.

Usage

use bounded_str::{StackStr, FlexStr, Bytes, Chars, AsciiOnly};
use serde::Deserialize;

// Matrix spec-compliant types — showcase ALL crate features:

// 1. Bytes (O(1)) — Room IDs, technical strings
type RoomId     = StackStr<8, 255, 255, Bytes>; 

// 2. Chars (Unicode) — usernames with Cyrillic/emoji  
type Username   = StackStr<1, 255, 1024, Chars>; 

// 3. Bytes + AsciiOnly — device IDs, technical strings
type DeviceId   = StackStr<1, 32, 32, Bytes, AsciiOnly>;

// 4. Passwords — short, zeroize-enabled - true
type Password = StackStr<8, 128, 128, Bytes, AsciiOnly, true>; 

// 5. JWT tokens — large buffer (2KiB)
type Token      = FlexStr<16, 2048, 255, Bytes>; 

// 6. HTML content — large, auto heap (up to 64KiB) if > 1024
type HtmlBody   = FlexStr<0, 65536, 1024, Bytes>;  

// JSON auto-validation! (parse, don't validate)
#[derive(Deserialize)]
struct LoginRequest {
    username:    Username,       // Unicode OK, 1-255 chars (1KiB buffer)
    device_id:   DeviceId,       // ASCII only, 1-32 bytes (32B buffer)
    password:    Password,       // 8-128 bytes, zeroize (128B buffer)
    access_token: Option<Token>, // JWT up to 2KiB buffer
    room_id:     Option<RoomId>, // Matrix room everywhere!
    html_body:   HtmlBody,       // Large HTML → auto heap
}

// Real usage — invalid JSON fails automatically!
let json = r#"{
    "username": "alexey", 
    "device_id": "DEV123",
    "password": "MySecurePass123",
    "html_body": "<p>Matrix <b>rich content</b> up to 64KiB</p>"
}"#;

let req: Result<LoginRequest, _> = serde_json::from_str(json);

// Short password or oversized HTML → serde fails instantly! 
// No manual if-checks needed.

Cargo Features

[dependencies]

bounded-str = { version = "0.1", features = ["serde", "alloc", "zeroize", "constant-time"] }

  • serde: Automatic validation during deserialization.
  • alloc: Enables FlexStr and dynamic memory support.
  • zeroize: Clears the buffer when it goes out of scope (Drop).
  • constant-time: Protects all equality checks (==) against timing attacks by comparing every byte regardless of content.

Limitations

  • By default, the stack limit is set to a reasonable size (recommended up to 4KiB).
  • The Chars policy requires a full scan of the string during creation and mutation.

Important Architectural Note

Although FormatPolicy allows checking data format (e.g., Email or Regex), remember the "Parse, don't validate" philosophy:

  • Complexity: Attempting perfect Email validation via policies may produce fragile code.
  • Recommendation: Use policies for structural constraints (length, ASCII encoding, absence of control characters).
  • Business Logic: Deep validation (e.g., domain existence, RFC 5322 compliance) is better handled by specialized parsers that convert BoundedStr into stricter data types.