SkillAgentSearch skills...

IronSBE

IronSBE is a complete Rust implementation of the [Simple Binary Encoding (SBE)](https://www.fixtrading.org/standards/sbe/) protocol, designed for ultra-low-latency financial systems. It provides both server and client capabilities with a focus on performance, type safety, and ease of use.

Install / Use

/learn @joaquinbejar/IronSBE
About this skill

Quality Score

0/100

Category

Design

Supported Platforms

Universal

README

<p align="center"> <img src="doc/assets/ironsbe-logo.png" alt="IronSBE Logo" width="200"/> </p> <h1 align="center">IronSBE</h1> <p align="center"> <strong>High-Performance Simple Binary Encoding (SBE) Server/Client for Rust</strong> </p> <p align="center"> <a href="https://crates.io/crates/ironsbe"><img src="https://img.shields.io/crates/v/ironsbe.svg" alt="Crates.io"/></a> <a href="https://docs.rs/ironsbe"><img src="https://docs.rs/ironsbe/badge.svg" alt="Documentation"/></a> <a href="https://github.com/joaquinbejar/IronSBE/actions"><img src="https://github.com/joaquinbejar/IronSBE/workflows/Build/badge.svg" alt="Build Status"/></a> <a href="https://codecov.io/gh/joaquinbejar/IronSBE"><img src="https://codecov.io/gh/joaquinbejar/IronSBE/branch/main/graph/badge.svg" alt="Coverage"/></a> <a href="https://github.com/joaquinbejar/IronSBE/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"/></a> </p> <p align="center"> Zero-copy • Schema-driven codegen • Sub-microsecond latency • Millions of msg/sec </p>

Overview

IronSBE is a complete Rust implementation of the Simple Binary Encoding (SBE) protocol, designed for ultra-low-latency financial systems. It provides both server and client capabilities with a focus on performance, type safety, and ease of use.

SBE is the binary encoding standard used by major exchanges including CME, Eurex, LSE, NASDAQ, and Binance for market data feeds and order entry systems.

Key Features

  • Zero-copy decoding - Direct buffer access with compile-time offset calculation
  • Schema-driven code generation - Type-safe messages from XML specifications
  • Sub-microsecond latency - Cache-friendly memory layouts with aligned buffers
  • Multi-transport support - TCP, UDP unicast/multicast, shared memory IPC
  • A/B feed arbitration - First-arrival-wins deduplication for redundant feeds
  • Market data patterns - Order book management, gap detection, snapshot recovery
  • 100% safe Rust - No unsafe code in core library
  • Async/await support - Built on Tokio for high-performance async I/O

Performance

Benchmarked on Apple M4 Max 64GB, macOS Tahoe 26.2:

| Operation | Latency (p50) | Latency (p99) | Throughput | |-----------|---------------|---------------|------------| | Encode NewOrderSingle | 3 ns | 4 ns | 342.8M msg/sec | | Decode NewOrderSingle | 1 ns | 1 ns | 1262.6M msg/sec | | Encode MarketData (10 entries) | 6 ns | 8 ns | 165.5M msg/sec | | Decode MarketData (10 entries) | 0 ns | 1 ns | 2178.6M msg/sec | | SPSC channel send | 2 ns | 2 ns | 648.5M msg/sec | | MPSC channel send | 7 ns | 9 ns | 145.5M msg/sec | | TCP round-trip (localhost) | 16.8 μs | 23.7 μs | 60K msg/sec |

Run your own benchmarks:

cargo run --example benchmark_report --release

Quick Start

Installation

Add IronSBE to your Cargo.toml:

[dependencies]
ironsbe = "0.1"

[build-dependencies]
ironsbe-codegen = "0.1"

Define Your Schema

Create an SBE schema file (schemas/trading.xml):

<?xml version="1.0" encoding="UTF-8"?>
<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
                   package="trading"
                   id="1"
                   version="1"
                   byteOrder="littleEndian">
    <types>
        <composite name="messageHeader">
            <type name="blockLength" primitiveType="uint16"/>
            <type name="templateId" primitiveType="uint16"/>
            <type name="schemaId" primitiveType="uint16"/>
            <type name="version" primitiveType="uint16"/>
        </composite>
        
        <enum name="Side" encodingType="uint8">
            <validValue name="Buy">1</validValue>
            <validValue name="Sell">2</validValue>
        </enum>
        
        <type name="Symbol" primitiveType="char" length="8"/>
        <type name="ClOrdId" primitiveType="char" length="20"/>
    </types>
    
    <sbe:message name="NewOrderSingle" id="1" blockLength="48">
        <field name="clOrdId" id="11" type="ClOrdId" offset="0"/>
        <field name="symbol" id="55" type="Symbol" offset="20"/>
        <field name="side" id="54" type="Side" offset="28"/>
        <field name="price" id="44" type="int64" offset="29"/>
        <field name="quantity" id="38" type="uint64" offset="37"/>
    </sbe:message>
</sbe:messageSchema>

Generate Code

Add to your build.rs:

fn main() {
    ironsbe_codegen::generate(
        "schemas/trading.xml",
        &format!("{}/trading.rs", std::env::var("OUT_DIR").unwrap()),
    ).expect("Failed to generate SBE codecs");
}

Encode Messages

use ironsbe::prelude::*;

// Include generated code
mod trading {
    include!(concat!(env!("OUT_DIR"), "/trading.rs"));
}

use trading::{NewOrderSingleEncoder, Side};

fn main() {
    // Allocate buffer (stack or pool)
    let mut buffer = [0u8; 256];
    
    // Create encoder (writes header automatically)
    let mut encoder = NewOrderSingleEncoder::wrap(&mut buffer, 0);
    
    // Set fields with builder pattern
    encoder
        .set_cl_ord_id(b"ORDER-001           ")
        .set_symbol(b"AAPL    ")
        .set_side(Side::Buy)
        .set_price(15050)      // $150.50 as fixed-point
        .set_quantity(100);
    
    // Get encoded length
    let len = encoder.encoded_length();
    
    // Send buffer[..len] over network
    println!("Encoded {} bytes", len);
}

Decode Messages

use ironsbe::prelude::*;

mod trading {
    include!(concat!(env!("OUT_DIR"), "/trading.rs"));
}

use trading::{NewOrderSingleDecoder, SCHEMA_VERSION};

fn main() {
    // Received from network
    let buffer: &[u8] = /* ... */;
    
    // Zero-copy decode (no allocation)
    let decoder = NewOrderSingleDecoder::wrap(
        buffer,
        MessageHeader::ENCODED_LENGTH,
        SCHEMA_VERSION,
    );
    
    // Access fields directly from buffer
    println!("ClOrdId: {:?}", decoder.cl_ord_id());
    println!("Symbol: {:?}", decoder.symbol());
    println!("Side: {:?}", decoder.side());
    println!("Price: {}", decoder.price());
    println!("Quantity: {}", decoder.quantity());
}

Architecture

graph TB
    subgraph Application["Application Layer"]
        Server["Server Engine"]
        Client["Client Engine"]
        MarketData["Market Data Handler"]
    end

    subgraph Channel["Channel Layer"]
        SPSC["SPSC<br/>~20 ns"]
        MPSC["MPSC<br/>~100 ns"]
        Broadcast["Broadcast<br/>1-to-N"]
    end

    subgraph Transport["Transport Layer"]
        TCP["TCP<br/>Tokio"]
        UDP["UDP<br/>Unicast"]
        Multicast["Multicast<br/>A/B"]
        IPC["IPC<br/>SHM"]
    end

    subgraph Codec["Codec Layer"]
        Encoders["Generated Encoders / Decoders<br/>Zero-copy, compile-time offsets, type-safe"]
        Core["ironsbe-core<br/>Buffer traits, headers, primitives"]
    end

    Server --> SPSC
    Server --> MPSC
    Client --> SPSC
    Client --> MPSC
    MarketData --> Broadcast

    SPSC --> TCP
    SPSC --> UDP
    MPSC --> TCP
    MPSC --> Multicast
    Broadcast --> Multicast
    Broadcast --> IPC

    TCP --> Encoders
    UDP --> Encoders
    Multicast --> Encoders
    IPC --> Encoders
    Encoders --> Core

Crate Structure

IronSBE is organized as a Cargo workspace with 11 crates:

| Crate | Description | |-------|-------------| | ironsbe | Facade crate re-exporting public API | | ironsbe-core | Buffer traits, message headers, primitive types, encoder/decoder traits | | ironsbe-schema | SBE XML schema parser and validation | | ironsbe-codegen | Build-time Rust code generation from SBE schemas | | ironsbe-derive | Procedural macros (#[derive(SbeMessage)]) | | ironsbe-channel | Lock-free SPSC/MPSC/Broadcast channels | | ironsbe-transport | TCP, UDP unicast/multicast, shared memory IPC | | ironsbe-server | Async server engine with session management | | ironsbe-client | Async client with auto-reconnection | | ironsbe-marketdata | Order book, gap detection, A/B feed arbitration | | ironsbe-bench | Benchmarks using Criterion |

Dependency Graph

ironsbe (facade)
├── ironsbe-core
├── ironsbe-schema
├── ironsbe-codegen
├── ironsbe-channel
├── ironsbe-transport
├── ironsbe-server
├── ironsbe-client
└── ironsbe-marketdata

ironsbe-server
├── ironsbe-core
├── ironsbe-channel
└── ironsbe-transport

ironsbe-client
├── ironsbe-core
├── ironsbe-channel
└── ironsbe-transport

ironsbe-codegen
├── ironsbe-core
└── ironsbe-schema

Examples

Running the Examples

IronSBE includes working server and client examples:

# Terminal 1: Start the server
cd ironsbe && cargo run --example server

# Terminal 2: Run the client
cd ironsbe && cargo run --example client

Expected output:

Server:

Starting IronSBE server on 127.0.0.1:9000
[Server] Session 1 connected
[Server] Message #1 from session 1: template_id=101, size=45 bytes
[Server] Message #2 from session 1: template_id=102, size=45 bytes
...
[Server] Session 1 disconnected

Client:

Connecting to IronSBE server at 127.0.0.1:9000
[Client] Connected to server
[Client] Sent message #1
[Client] Received response: 45 bytes
[Client] Response payload: Hello from IronSBE client! Message #1
...
Client stopped

TCP Echo Server

use ironsbe_core::header::MessageHeader;
use ironsbe_server::builder::ServerBuilder;
use ironsbe_server::handler::{MessageHandler, Responder};
use std::net::SocketAddr;
use std::sync::atomic::{AtomicU64, Ordering};

struct EchoHandler {
    message_count: AtomicU64,
}

impl MessageHandler for EchoHandler {
    fn on_message(
        &self,
        session_id: u64,
        header: &Me
View on GitHub
GitHub Stars13
CategoryDesign
Updated6h ago
Forks4

Languages

Rust

Security Score

80/100

Audited on Apr 6, 2026

No findings