Skip to main content

Buffer Factories

Buffer factories determine how buffer memory is allocated. Instead of passing a zone parameter, you choose (or create) a factory that encapsulates the allocation strategy.

Overview

import com.ditchoom.buffer.BufferFactory

// Platform-optimal native memory (recommended)
val buffer = BufferFactory.Default.allocate(1024)

// GC-managed heap memory
val managed = BufferFactory.managed().allocate(1024)

// Cross-process shared memory (for IPC on Android)
val shared = BufferFactory.shared().allocate(1024)

Built-in Presets

BufferFactory.Default

Platform-optimal native memory. This is the recommended default.

val buffer = BufferFactory.Default.allocate(1024)

Characteristics:

  • Off-heap / native memory where available
  • Zero-copy I/O with native code
  • Best for network I/O and file operations
  • Default allocation strategy

Platform behavior:

PlatformImplementation
JVMDirectByteBuffer
AndroidDirectByteBuffer
iOS/macOSNSMutableData
JavaScriptInt8Array
WASMLinearBuffer (native WASM memory)
LinuxNativeBuffer (malloc)

Heap vs Direct Memory

BufferFactory.managed()

GC-managed heap memory backed by Kotlin ByteArray.

val buffer = BufferFactory.managed().allocate(1024)

Characteristics:

  • Managed by garbage collector
  • May require copying for native I/O operations
  • Good for short-lived, small buffers

Platform behavior:

PlatformImplementation
JVMHeapByteBuffer
AndroidHeapByteBuffer
WASMByteArrayBuffer
OthersByteArrayBuffer

BufferFactory.shared()

Memory that can be shared across process boundaries.

val buffer = BufferFactory.shared().allocate(1024)

Characteristics:

  • Enables zero-copy IPC (Inter-Process Communication)
  • Platform-specific availability
  • Useful for Android services and multi-process apps

Platform behavior:

PlatformImplementation
Android (API 27+)SharedMemory
Android (< API 27)Falls back to DirectByteBuffer
JavaScriptSharedArrayBuffer (requires CORS headers)
WASMLinearBuffer (falls back to Default)
OthersFalls back to Default

SharedMemory IPC

BufferFactory.deterministic()

Buffers with guaranteed resource cleanup, independent of garbage collection.

BufferFactory.deterministic().allocate(1024).use { buffer ->
buffer.writeInt(42)
} // freed immediately, no GC needed

Platform behavior:

PlatformImplementation
JVM 9+DeterministicDirectJvmBuffer (DirectByteBuffer + Unsafe.invokeCleaner)
JVM 8 / AndroidDeterministicUnsafeJvmBuffer (Unsafe.allocateMemory/freeMemory)
JVM 21+FfmBuffer (FFM Arena-backed)
AppleMutableDataBuffer (ARC-managed, already deterministic)
LinuxNativeBuffer (malloc/free, already deterministic)
WASMLinearBuffer (linear memory, already deterministic)
JSJsBuffer (GC-managed, no deterministic alternative)

Composable Decorators

Layer behaviors on top of any factory:

val factory = BufferFactory.Default
.requiring<NativeMemoryAccess>() // throw if buffer lacks native access
.withSizeLimit(1_048_576) // cap allocation size
.withPooling(pool) // recycle buffers

Custom Factories

Implement the interface directly or delegate:

class MonitoredFactory(
private val delegate: BufferFactory = BufferFactory.Default,
) : BufferFactory by delegate {
override fun allocate(size: Int, byteOrder: ByteOrder): PlatformBuffer {
metrics.recordAllocation(size)
return delegate.allocate(size, byteOrder)
}
}

Library Author Pattern

Accept a factory parameter so callers can control allocation:

class ProtocolConnection(
val factory: BufferFactory = BufferFactory.Default,
) {
fun send(packet: Packet) {
factory.allocate(bufferSize).use { buffer ->
packet.writeTo(buffer)
}
}
}

Platform-Specific Notes

Android SharedMemory

All JvmBuffers are Parcelable. For zero-copy IPC:

// In your Service or ContentProvider
val buffer = BufferFactory.shared().allocate(1024)
buffer.writeBytes(data)
buffer.resetForRead()

// Pass through Binder
parcel.writeParcelable(buffer as Parcelable, 0)

JavaScript SharedArrayBuffer

Requires CORS headers. Add to webpack.config.d/:

if (config.devServer != null) {
config.devServer.headers = {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
}

Without proper headers, falls back to regular ArrayBuffer.

Choosing a Factory

Use CaseRecommended Factory
Network I/ODefault
File I/ODefault
Short-lived parsingmanaged() or pool from Default
Android IPCshared()
Multi-threaded JSshared()
Deterministic cleanupdeterministic()
General purposeDefault