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:
| Platform | Implementation |
|---|---|
| JVM | DirectByteBuffer |
| Android | DirectByteBuffer |
| iOS/macOS | NSMutableData |
| JavaScript | Int8Array |
| WASM | LinearBuffer (native WASM memory) |
| Linux | NativeBuffer (malloc) |
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:
| Platform | Implementation |
|---|---|
| JVM | HeapByteBuffer |
| Android | HeapByteBuffer |
| WASM | ByteArrayBuffer |
| Others | ByteArrayBuffer |
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:
| Platform | Implementation |
|---|---|
| Android (API 27+) | SharedMemory |
| Android (< API 27) | Falls back to DirectByteBuffer |
| JavaScript | SharedArrayBuffer (requires CORS headers) |
| WASM | LinearBuffer (falls back to Default) |
| Others | Falls back to Default |
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:
| Platform | Implementation |
|---|---|
| JVM 9+ | DeterministicDirectJvmBuffer (DirectByteBuffer + Unsafe.invokeCleaner) |
| JVM 8 / Android | DeterministicUnsafeJvmBuffer (Unsafe.allocateMemory/freeMemory) |
| JVM 21+ | FfmBuffer (FFM Arena-backed) |
| Apple | MutableDataBuffer (ARC-managed, already deterministic) |
| Linux | NativeBuffer (malloc/free, already deterministic) |
| WASM | LinearBuffer (linear memory, already deterministic) |
| JS | JsBuffer (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 Case | Recommended Factory |
|---|---|
| Network I/O | Default |
| File I/O | Default |
| Short-lived parsing | managed() or pool from Default |
| Android IPC | shared() |
| Multi-threaded JS | shared() |
| Deterministic cleanup | deterministic() |
| General purpose | Default |