Skip to main content

ScratchBuffers

Struct ScratchBuffers 

Source
pub struct ScratchBuffers {
    pub digit_buffer: DigitBuffer,
    pub byte_buffer: Vec<u8>,
    pub string_buffer: String,
}
Expand description

Reusable scratch buffers for worker threads

Pre-allocated buffers that can be reused across multiple record processing operations to minimize heap allocations in hot codec paths. This is a critical performance optimization that eliminates allocation overhead during high-throughput data processing.

§Usage Pattern

  1. Create once per worker thread: ScratchBuffers::new()
  2. Use buffers during record processing
  3. Clear buffers after each record: clear()
  4. Reuse for next record (no reallocation)

§Performance Benefits

  • Zero allocations in steady-state processing (after initial capacity growth)
  • CPU cache-friendly - buffers stay hot in L1/L2 cache
  • Reduced GC pressure - minimal heap churn
  • Thread-local - no synchronization overhead

§Buffer Purposes

  • digit_buffer - Packed/zoned decimal digit extraction (stack-allocated up to 32 bytes)
  • byte_buffer - General-purpose byte storage (EBCDIC conversion, field data)
  • string_buffer - UTF-8 text processing (field values, JSON strings)

§Examples

§Basic Usage

use copybook_codec_memory::ScratchBuffers;

let mut scratch = ScratchBuffers::new();

// Use buffers for processing
scratch.digit_buffer.push(5);
scratch.byte_buffer.extend_from_slice(b"EBCDIC data");
scratch.string_buffer.push_str("converted text");

// Clear for next record (no deallocation)
scratch.clear();
assert_eq!(scratch.digit_buffer.len(), 0);
assert_eq!(scratch.byte_buffer.len(), 0);
assert_eq!(scratch.string_buffer.len(), 0);

§Integration with Codec Functions

use copybook_codec_memory::ScratchBuffers;
use copybook_codec::{decode_record_with_scratch, DecodeOptions};
use copybook_core::parse_copybook;

let copybook = "01 RECORD.\n   05 FIELD PIC X(10).";
let schema = parse_copybook(copybook).unwrap();
let data = vec![0xF1; 10]; // EBCDIC '1' repeated

let mut scratch = ScratchBuffers::new();
let options = DecodeOptions::new();

// Process records in loop (reusing scratch buffers)
for _ in 0..1000 {
    let _value = decode_record_with_scratch(&schema, &data, &options, &mut scratch).unwrap();
    scratch.clear(); // Reuse buffers for next record
}

§Capacity Management

use copybook_codec_memory::ScratchBuffers;

let mut scratch = ScratchBuffers::new();

// Pre-allocate for large records
scratch.ensure_byte_capacity(8192);  // 8 KB record size
scratch.ensure_string_capacity(4096); // 4 KB string fields

// Buffers now have sufficient capacity for processing
assert!(scratch.byte_buffer.capacity() >= 8192);
assert!(scratch.string_buffer.capacity() >= 4096);

Fields§

§digit_buffer: DigitBuffer

Buffer for digit processing in packed/zoned decimal codecs

Stack-allocated up to 32 bytes (typical COBOL numerics), then spills to heap.

§byte_buffer: Vec<u8>

General-purpose byte buffer for record processing

Used for EBCDIC conversion, field extraction, and intermediate data storage. Initial capacity: 1 KB.

§string_buffer: String

String buffer for text processing

Used for UTF-8 text fields, JSON string construction, and character conversion. Initial capacity: 512 characters.

Implementations§

Source§

impl ScratchBuffers

Source

pub fn new() -> Self

Create new scratch buffers with reasonable initial capacity

Allocates buffers with initial capacities optimized for typical COBOL records:

  • digit_buffer: Stack-allocated (no heap until >32 bytes)
  • byte_buffer: 1 KB initial heap capacity
  • string_buffer: 512 characters initial heap capacity

These defaults handle most COBOL record layouts without reallocation.

§Examples
use copybook_codec_memory::ScratchBuffers;

let scratch = ScratchBuffers::new();
assert_eq!(scratch.digit_buffer.len(), 0);
assert!(scratch.byte_buffer.capacity() >= 1024);
assert!(scratch.string_buffer.capacity() >= 512);
Source

pub fn clear(&mut self)

Clear all buffers for reuse

Resets buffer lengths to zero without deallocating memory. This is the key operation for buffer reuse - call after each record to prepare for the next processing iteration.

Performance note: This is O(1) and does not touch buffer capacity.

§Examples
use copybook_codec_memory::ScratchBuffers;

let mut scratch = ScratchBuffers::new();

// Use buffers
scratch.digit_buffer.push(5);
scratch.byte_buffer.extend_from_slice(b"data");
scratch.string_buffer.push_str("text");

// Clear for reuse (capacity unchanged)
let byte_capacity = scratch.byte_buffer.capacity();
scratch.clear();

assert_eq!(scratch.digit_buffer.len(), 0);
assert_eq!(scratch.byte_buffer.len(), 0);
assert_eq!(scratch.string_buffer.len(), 0);
assert_eq!(scratch.byte_buffer.capacity(), byte_capacity);
Source

pub fn ensure_byte_capacity(&mut self, capacity: usize)

Ensure byte buffer has at least the specified capacity

Pre-allocates byte buffer capacity to avoid reallocation during processing. Use this when you know the maximum record size in advance.

Performance optimization: Fast-path optimized for common case where capacity is already sufficient. Growth only occurs when needed.

§Arguments
  • capacity - Minimum required capacity in bytes
§Examples
use copybook_codec_memory::ScratchBuffers;

let mut scratch = ScratchBuffers::new();

// Pre-allocate for 8 KB records
scratch.ensure_byte_capacity(8192);
assert!(scratch.byte_buffer.capacity() >= 8192);

// No reallocation on subsequent calls with same/lower capacity
scratch.ensure_byte_capacity(4096); // No-op, already sufficient
Source

pub fn ensure_string_capacity(&mut self, capacity: usize)

Ensure string buffer has at least the specified capacity

Pre-allocates string buffer capacity to avoid reallocation during text processing. Use this when you know the maximum string field size in advance.

Performance optimization: Fast-path optimized for common case where capacity is already sufficient. Growth only occurs when needed.

§Arguments
  • capacity - Minimum required capacity in characters (not bytes)
§Examples
use copybook_codec_memory::ScratchBuffers;

let mut scratch = ScratchBuffers::new();

// Pre-allocate for 4096 character fields
scratch.ensure_string_capacity(4096);
assert!(scratch.string_buffer.capacity() >= 4096);

// No reallocation on subsequent calls with same/lower capacity
scratch.ensure_string_capacity(2048); // No-op, already sufficient

Trait Implementations§

Source§

impl Debug for ScratchBuffers

Source§

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

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

impl Default for ScratchBuffers

Source§

fn default() -> Self

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

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

Source§

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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more