Parcode

Struct Parcode 

Source
pub struct Parcode { /* private fields */ }
Expand description

The main entry point for configuring and executing Parcode operations.

This struct provides a builder-style API for configuring serialization parameters and executing read/write operations. It is designed to be lightweight and can be constructed multiple times without significant overhead.

§Configuration Options

  • Compression: Enable or disable compression (default: disabled). When enabled, the library uses the default compression algorithm (LZ4 if the lz4_flex feature is enabled, otherwise no compression).

§Examples

§Basic Usage

use parcode::Parcode;

let data = vec![1, 2, 3];
Parcode::save("data_basic.par", &data).unwrap();
let loaded: Vec<i32> = Parcode::read("data_basic.par").unwrap();

§With Compression

use parcode::Parcode;

let data = vec![1, 2, 3];
Parcode::builder()
    .compression(true)
    .write("data_comp.par", &data)?;

§Performance Notes

  • The builder itself has negligible overhead (it’s just a small struct with flags)
  • Compression trades CPU time for reduced I/O and storage
  • Parallel execution scales with the number of independent chunks in your data

Implementations§

Source§

impl Parcode

Source

pub fn builder() -> Self

Creates a new Parcode builder with default settings.

This is the entry point for the builder pattern. The returned instance can be configured using method chaining before calling write.

§Default Settings
  • Compression: Disabled
§Examples
use parcode::Parcode;

let parcode = Parcode::builder()
    .compression(true);
§Performance

This method is extremely lightweight (just creates a small struct) and can be called repeatedly without concern for performance.

Source

pub fn compression(self, enable: bool) -> Self

Enables or disables compression for all chunks.

When compression is enabled, the library will use the default compression algorithm to reduce the size of serialized chunks. The actual algorithm used depends on which features are enabled:

  • If the lz4_flex feature is enabled: LZ4 compression (fast, moderate ratio)
  • Otherwise: No compression (pass-through)
§Parameters
  • enable: Whether to enable compression
§Returns

Returns self to allow method chaining.

§Trade-offs
  • Enabled: Smaller files, lower I/O bandwidth, higher CPU usage
  • Disabled: Larger files, higher I/O bandwidth, lower CPU usage
§Examples
use parcode::{Parcode, ParcodeObject};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, ParcodeObject)]
struct MyData { val: i32 }
let my_data = MyData { val: 42 };

// Enable compression
Parcode::builder()
    .compression(true)
    .write("data.par", &my_data)?;
§Performance Notes
  • LZ4 compression typically achieves 2-3x compression ratios on structured data with minimal CPU overhead. For data that doesn’t compress well (e.g., already compressed images), the overhead may outweigh the benefits.
Source

pub fn read<T, P>(path: P) -> Result<T>
where T: ParcodeNative, P: AsRef<Path>,

Reads and fully deserializes an object from a Parcode file.

This method automatically selects the optimal reconstruction strategy based on the type being read:

  • Collections (Vec<T>): Uses parallel reconstruction across shards
  • Maps (HashMap<K, V>): Reconstructs all shards and merges entries
  • Primitives and Structs: Uses sequential deserialization
§Type Parameters
  • T: The type to deserialize. Must implement ParcodeNative.
  • P: The path type (anything that implements AsRef<Path>).
§Parameters
  • path: The file path to read from.
§Returns

Returns the fully deserialized object of type T.

§Errors

This method can fail if:

  • The file does not exist or cannot be opened
  • The file is not a valid Parcode file (wrong magic bytes or version)
  • The file is corrupted or truncated
  • Deserialization fails (e.g., type mismatch)
  • Decompression fails
§Examples
use parcode::{Parcode, ParcodeObject};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, ParcodeObject)]
struct GameState { level: u32 }

// Setup
let data = vec![1, 2, 3];
Parcode::save("numbers_read.par", &data)?;
let state = GameState { level: 1 };
Parcode::save("game_read.par", &state)?;

// Read a vector
let data: Vec<i32> = Parcode::read("numbers_read.par")?;

// Read a custom struct
let state: GameState = Parcode::read("game_read.par")?;
Source

pub fn save<T, P>(path: P, root_object: &T) -> Result<()>
where T: ParcodeVisitor + Sync, P: AsRef<Path>,

Saves an object to a file using default settings (no compression).

This is a convenience method that creates a default Parcode instance and calls write.

§Type Parameters
  • T: The type to serialize. Must implement ParcodeVisitor and Sync (for parallel execution).
  • P: The path type (anything that implements AsRef<Path>).
§Parameters
  • path: The file path to write to. If the file exists, it will be truncated.
  • root_object: A reference to the object to serialize.
§Returns

Returns Ok(()) on success.

§Errors

This method can fail if:

  • The file cannot be created (e.g., permission denied, disk full)
  • Serialization fails (e.g., bincode error)
  • I/O errors occur during writing
§Examples
use parcode::{Parcode, ParcodeObject};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, ParcodeObject)]
struct MyData {
    value: i32,
}

let data = MyData { value: 42 };
Parcode::save("data_save.par", &data)?;
§Performance
  • Zero-Copy: The serializer borrows data rather than cloning it
  • Parallel Execution: Independent chunks are processed concurrently
  • Buffered I/O: Uses a 16MB buffer to minimize syscalls

For custom configuration (e.g., enabling compression), use the builder pattern:

use parcode::Parcode;
let data = vec![1, 2, 3];
Parcode::builder()
    .compression(true)
    .write("data_builder.par", &data)?;
Source

pub fn write<T, P>(&self, path: P, root_object: &T) -> Result<()>
where T: ParcodeVisitor + Sync, P: AsRef<Path>,

Serializes an object graph to disk with the configured settings.

This method performs the complete serialization pipeline:

  1. Graph Construction: Analyzes the object structure and builds a dependency graph
  2. Parallel Execution: Processes independent chunks concurrently using Rayon
  3. Compression: Applies compression to each chunk (if enabled)
  4. I/O: Writes chunks to disk in a bottom-up order
  5. Header Writing: Appends the global header pointing to the root chunk
§Type Parameters
  • 'a: The lifetime of the object being serialized. The graph borrows from the object, enabling zero-copy serialization.
  • T: The type to serialize. Must implement ParcodeVisitor and Sync (for parallel execution).
  • P: The path type (anything that implements AsRef<Path>).
§Parameters
  • path: The file path to write to. If the file exists, it will be truncated.
  • root_object: A reference to the object to serialize. This reference must remain valid for the entire duration of the serialization process.
§Returns

Returns Ok(()) on success.

§Errors

This method can fail if:

  • The file cannot be created (e.g., permission denied, disk full)
  • Serialization fails (e.g., bincode error)
  • Compression fails
  • I/O errors occur during writing
  • The graph contains cycles (should not happen with valid ParcodeVisitor implementations)
§Examples
use parcode::Parcode;

let data = vec![1, 2, 3, 4, 5];

// Write with compression
Parcode::builder()
    .compression(true)
    .write("data_write.par", &data)?;
§Performance Characteristics
  • Parallelism: Scales with the number of independent chunks (typically O(cores))
  • Memory: Uses zero-copy where possible; peak memory is proportional to the largest chunk plus buffer overhead
  • I/O: Buffered writes (16MB buffer) minimize syscalls
  • Compression: LZ4 compression adds ~10-20% CPU overhead but can reduce I/O by 2-3x

Serializes an object graph to disk with the configured settings.

This is a convenience wrapper around write_to_writer that handles file creation.

§Type Parameters
  • T: The type to serialize. Must implement ParcodeVisitor and Sync (for parallel execution).
  • P: The path type (anything that implements AsRef<Path>).
§Parameters
  • path: The file path to write to. If the file exists, it will be truncated.
  • root_object: A reference to the object to serialize.
Source

pub fn write_to_writer<'a, T, W>( &self, writer: W, root_object: &'a T, ) -> Result<()>
where T: ParcodeVisitor + Sync, W: Write + Send,

Serializes the object graph to a generic writer (File, Vec, TcpStream, etc).

This method performs the complete serialization pipeline:

  1. Graph Construction: Analyzes the object structure and builds a dependency graph
  2. Parallel Execution: Processes independent chunks concurrently using Rayon
  3. Compression: Applies compression to each chunk (if enabled)
  4. I/O: Writes chunks to the writer in a bottom-up order
  5. Header Writing: Appends the global header pointing to the root chunk
§Type Parameters
  • 'a: The lifetime of the object being serialized. The graph borrows from the object, enabling zero-copy serialization.
§Thread Safety
  • T: The type to serialize. Must implement ParcodeVisitor + Sync.
  • W: The writer type. Must implement Write + Send.
§Parameters
  • writer: The destination to write to.
  • root_object: A reference to the object to serialize.
§Returns

Returns Ok(()) on success.

§Errors

This method can fail if:

  • Serialization fails (e.g., bincode error)
  • Compression fails
  • I/O errors occur during writing to the writer
§Examples
use parcode::Parcode;

let data = vec![1, 2, 3];
let mut buffer = Vec::new();

Parcode::builder()
    .write_to_writer(&mut buffer, &data)?;
Source

pub fn save_sync<T, P>(path: P, root_object: &T) -> Result<()>
where T: ParcodeVisitor, P: AsRef<Path>,

Serializes an object synchronously (single-threaded).

This method is useful for:

  • Environments where spawning threads is expensive or restricted (WASM, embeddedish).
  • Debugging serialization logic without concurrency noise.
  • Benchmarking vs Parallel implementation.

It uses less memory than write because it reuses a single compression buffer.

Source

pub fn write_sync<'a, T, P>(&self, path: P, root_object: &'a T) -> Result<()>
where T: ParcodeVisitor, P: AsRef<Path>,

Internal synchronous write implementation.

Currently only supports file paths because execute_graph_sync consumes the writer, and we need to re-open the file to append the header (simplest approach for now). A future refactor could support generic writers for sync mode if needed.

Trait Implementations§

Source§

impl Debug for Parcode

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Parcode

Source§

fn default() -> Parcode

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

§

impl<T> Any for T
where T: 'static + ?Sized,

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Borrow<T> for T
where T: ?Sized,

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

impl<T> BorrowMut<T> for T
where T: ?Sized,

§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T, U> Into<U> for T
where U: From<T>,

§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V