CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor-io-watchosarm64

Asynchronous I/O library for Kotlin multiplatform providing channels, streams, and byte manipulation utilities optimized for watchOS ARM64

Pending
Overview
Eval results
Files

memory-management.mddocs/

Memory Management

Low-level memory operations providing direct byte access, primitive type loading/storing, and platform-optimized memory utilities.

Capabilities

Memory Class

Platform-specific linear range of bytes providing direct memory access with bounds checking and platform optimizations.

/**
 * Represents a linear range of bytes in memory.
 * Platform-specific implementation optimized for the target architecture.
 */
expect class Memory {
    /** Total size of this memory region in bytes */
    val size: Long
    
    /** Size as 32-bit integer (throws if size > Int.MAX_VALUE) */
    val size32: Int
    
    /**
     * Load a byte value from the specified index.
     * @param index byte offset from start of memory region
     * @return byte value at the index
     * @throws IndexOutOfBoundsException if index is out of bounds
     */
    fun loadAt(index: Int): Byte
    fun loadAt(index: Long): Byte
    
    /**
     * Store a byte value at the specified index.
     * @param index byte offset from start of memory region
     * @param value byte value to store
     * @throws IndexOutOfBoundsException if index is out of bounds
     */
    fun storeAt(index: Int, value: Byte)
    fun storeAt(index: Long, value: Byte)
    
    /**
     * Create a subrange view of this memory region.
     * Changes to the slice affect the original memory.
     * @param offset starting byte offset
     * @param length size of the slice in bytes
     * @return Memory view of the specified range
     */
    fun slice(offset: Int, length: Int): Memory
    fun slice(offset: Long, length: Long): Memory
    
    /**
     * Copy bytes from this memory to another memory region.
     * @param destination target memory region
     * @param offset source offset in this memory
     * @param length number of bytes to copy
     * @param destinationOffset target offset in destination memory
     */
    fun copyTo(destination: Memory, offset: Int, length: Int, destinationOffset: Int)
    fun copyTo(destination: Memory, offset: Long, length: Long, destinationOffset: Long)
    
    /**
     * Copy bytes from this memory to a byte array.
     * @param destination target byte array
     * @param offset source offset in this memory
     * @param length number of bytes to copy
     * @param destinationOffset target offset in destination array
     */
    fun copyTo(destination: ByteArray, offset: Int, length: Int, destinationOffset: Int)
    
    /**
     * Array-like access operator for reading bytes.
     * @param index byte offset from start
     * @return byte value at index
     */
    operator fun get(index: Int): Byte
    operator fun get(index: Long): Byte
    
    /**
     * Array-like assignment operator for storing bytes.
     * @param index byte offset from start
     * @param value byte value to store
     */
    operator fun set(index: Int, value: Byte)
    operator fun set(index: Long, value: Byte)
    
    /**
     * Fill a range of memory with a specific byte value.
     * @param offset starting offset to fill
     * @param count number of bytes to fill
     * @param value byte value to fill with
     */
    fun fill(offset: Int, count: Int, value: Byte)
    fun fill(offset: Long, count: Long, value: Byte)
    
    companion object {
        /** Empty memory region with zero size */
        val Empty: Memory
    }
}

Usage Examples:

import io.ktor.utils.io.bits.*

// Use temporary memory allocation
withMemory(1024) { memory ->
    // Basic byte operations
    memory[0] = 0xFF.toByte()
    memory[1] = 0x42.toByte()
    val firstByte = memory[0]
    val secondByte = memory.loadAt(1)

    // Fill memory with pattern
    memory.fill(0, 100, 0xAA.toByte())

    // Create slices
    val slice = memory.slice(10, 50) // 50 bytes starting at offset 10
    slice[0] = 0x99.toByte() // Modifies original memory at offset 10

    // Copy to byte array
    val array = ByteArray(20)
    memory.copyTo(array, offset = 0, length = 20, destinationOffset = 0)
}

// Copy operations between temporary memory regions
withMemory(100) { source ->
    withMemory(100) { dest ->
        source.copyTo(dest, offset = 0, length = 50, destinationOffset = 25)
    }
}

Memory Primitive Operations

Extension functions for loading and storing primitive types from Memory with big-endian byte order.

/**
 * Load a 16-bit signed short value in big-endian byte order.
 * @param offset byte offset in memory
 * @return short value
 */
fun Memory.loadShortAt(offset: Int): Short
fun Memory.loadShortAt(offset: Long): Short

/**
 * Store a 16-bit signed short value in big-endian byte order.
 * @param offset byte offset in memory
 * @param value short value to store
 */
fun Memory.storeShortAt(offset: Int, value: Short)
fun Memory.storeShortAt(offset: Long, value: Short)

/**
 * Load a 32-bit signed int value in big-endian byte order.
 * @param offset byte offset in memory
 * @return int value
 */
fun Memory.loadIntAt(offset: Int): Int
fun Memory.loadIntAt(offset: Long): Int

/**
 * Store a 32-bit signed int value in big-endian byte order.
 * @param offset byte offset in memory
 * @param value int value to store
 */
fun Memory.storeIntAt(offset: Int, value: Int)
fun Memory.storeIntAt(offset: Long, value: Int)

/**
 * Load a 64-bit signed long value in big-endian byte order.
 * @param offset byte offset in memory
 * @return long value
 */
fun Memory.loadLongAt(offset: Int): Long
fun Memory.loadLongAt(offset: Long): Long

/**
 * Store a 64-bit signed long value in big-endian byte order.
 * @param offset byte offset in memory
 * @param value long value to store
 */
fun Memory.storeLongAt(offset: Int, value: Long)
fun Memory.storeLongAt(offset: Long, value: Long)

/**
 * Load an unsigned 16-bit short value.
 * @param offset byte offset in memory
 * @return unsigned short value
 */
fun Memory.loadUShortAt(offset: Int): UShort
fun Memory.loadUShortAt(offset: Long): UShort

/**
 * Store an unsigned 16-bit short value.
 * @param offset byte offset in memory
 * @param value unsigned short value to store
 */
fun Memory.storeUShortAt(offset: Int, value: UShort)
fun Memory.storeUShortAt(offset: Long, value: UShort)

/**
 * Load an unsigned 32-bit int value.
 * @param offset byte offset in memory
 * @return unsigned int value
 */
fun Memory.loadUIntAt(offset: Int): UInt
fun Memory.loadUIntAt(offset: Long): UInt

/**
 * Store an unsigned 32-bit int value.
 * @param offset byte offset in memory
 * @param value unsigned int value to store
 */
fun Memory.storeUIntAt(offset: Int, value: UInt)
fun Memory.storeUIntAt(offset: Long, value: UInt)

/**
 * Load an unsigned 64-bit long value.
 * @param offset byte offset in memory
 * @return unsigned long value
 */
fun Memory.loadULongAt(offset: Int): ULong
fun Memory.loadULongAt(offset: Long): ULong

/**
 * Store an unsigned 64-bit long value.
 * @param offset byte offset in memory
 * @param value unsigned long value to store
 */
fun Memory.storeULongAt(offset: Int, value: ULong)
fun Memory.storeULongAt(offset: Long, value: ULong)

/**
 * Load a 32-bit float value in big-endian byte order.
 * @param offset byte offset in memory
 * @return float value
 */
fun Memory.loadFloatAt(offset: Int): Float
fun Memory.loadFloatAt(offset: Long): Float

/**
 * Store a 32-bit float value in big-endian byte order.
 * @param offset byte offset in memory
 * @param value float value to store
 */
fun Memory.storeFloatAt(offset: Int, value: Float)
fun Memory.storeFloatAt(offset: Long, value: Float)

/**
 * Load a 64-bit double value in big-endian byte order.
 * @param offset byte offset in memory
 * @return double value
 */
fun Memory.loadDoubleAt(offset: Int): Double
fun Memory.loadDoubleAt(offset: Long): Double

/**
 * Store a 64-bit double value in big-endian byte order.
 * @param offset byte offset in memory
 * @param value double value to store
 */
fun Memory.storeDoubleAt(offset: Int, value: Double)
fun Memory.storeDoubleAt(offset: Long, value: Double)

Usage Examples:

import io.ktor.utils.io.bits.*

withMemory(64) { memory ->
    // Store different primitive types
    memory.storeIntAt(0, 0x12345678)
    memory.storeFloatAt(4, 3.14159f)
    memory.storeDoubleAt(8, 2.718281828)
    memory.storeLongAt(16, 0x123456789ABCDEF0L)

    // Load values back
    val intValue = memory.loadIntAt(0)         // 0x12345678
    val floatValue = memory.loadFloatAt(4)     // 3.14159f
    val doubleValue = memory.loadDoubleAt(8)   // 2.718281828
    val longValue = memory.loadLongAt(16)      // 0x123456789ABCDEF0L

    // Work with unsigned types
    memory.storeUIntAt(24, 0xFFFFFFFFu)
    val unsignedValue = memory.loadUIntAt(24)  // 0xFFFFFFFFu
}

// Store data structure
data class Point(val x: Float, val y: Float, val z: Float)

fun storePoint(memory: Memory, offset: Int, point: Point) {
    memory.storeFloatAt(offset, point.x)
    memory.storeFloatAt(offset + 4, point.y)
    memory.storeFloatAt(offset + 8, point.z)
}

fun loadPoint(memory: Memory, offset: Int): Point {
    return Point(
        x = memory.loadFloatAt(offset),
        y = memory.loadFloatAt(offset + 4),
        z = memory.loadFloatAt(offset + 8)
    )
}

val point = Point(1.0f, 2.0f, 3.0f)
storePoint(memory, 32, point)
val loadedPoint = loadPoint(memory, 32)

Buffer Class (Deprecated)

Buffer with read/write positions backed by Memory for stream-like operations.

/**
 * Buffer with read/write positions backed by Memory.
 * Provides stream-like reading and writing with position tracking.
 * @deprecated Use Memory and BytePacketBuilder/ByteReadPacket instead
 */
@Deprecated("Use Memory and packet APIs instead")
class Buffer {
    /** Underlying memory region backing this buffer */
    val memory: Memory
    
    /** Current read position in the buffer */
    var readPosition: Int
    
    /** Current write position in the buffer */
    var writePosition: Int
    
    /** Reserved space at the beginning of the buffer */
    val startGap: Int
    
    /** Maximum write position allowed */
    val limit: Int
    
    /** Reserved space at the end of the buffer */
    val endGap: Int
    
    /** Total capacity of the buffer */
    val capacity: Int
    
    /** Number of bytes available for reading */
    val readRemaining: Int
    
    /** Number of bytes available for writing */
    val writeRemaining: Int
    
    /**
     * Discard exactly count bytes from the read position.
     * @param count number of bytes to discard
     */
    fun discardExact(count: Int)
    
    /**
     * Commit count bytes that were written directly to memory.
     * @param count number of bytes to commit
     */
    fun commitWritten(count: Int)
    
    /**
     * Rewind the read position by count bytes.
     * @param count number of bytes to rewind
     */
    fun rewind(count: Int)
    
    /**
     * Reserve space at the beginning of the buffer.
     * @param startGap number of bytes to reserve
     */
    fun reserveStartGap(startGap: Int)
    
    /**
     * Reserve space at the end of the buffer.
     * @param endGap number of bytes to reserve
     */
    fun reserveEndGap(endGap: Int)
    
    /** Reset buffer for reading from the beginning */
    fun resetForRead()
    
    /** Reset buffer for writing from the beginning */
    fun resetForWrite()
    
    /**
     * Create a duplicate buffer sharing the same memory.
     * @return new Buffer instance
     */
    fun duplicate(): Buffer
    
    /**
     * Try to peek at the next byte without consuming it.
     * @return next byte value or -1 if no more bytes
     */
    fun tryPeekByte(): Int
    
    /**
     * Try to read a byte, returning -1 if none available.
     * @return byte value or -1 if no more bytes
     */
    fun tryReadByte(): Int
    
    /**
     * Read a byte, throwing exception if none available.
     * @return byte value
     * @throws EOFException if no more bytes
     */
    fun readByte(): Byte
    
    /**
     * Write a byte to the buffer.
     * @param value byte value to write
     */
    fun writeByte(value: Byte)
    
    companion object {
        /** Default reserved size for gaps */
        const val ReservedSize: Int = 8
        
        /** Empty buffer singleton */
        val Empty: Buffer
    }
}

Memory Allocation Functions

Functions for temporary memory allocation with automatic cleanup.

/**
 * Invoke block function with a temporary Memory instance of the specified size.
 * The provided instance shouldn't be captured and used outside of the block.
 * @param size number of bytes to allocate
 * @param block operation to perform with the memory
 * @return result of the block operation
 */
inline fun <R> withMemory(size: Int, block: (Memory) -> R): R

/**
 * Invoke block function with a temporary Memory instance of the specified size.
 * @param size number of bytes to allocate (as Long)
 * @param block operation to perform with the memory
 * @return result of the block operation
 */
inline fun <R> withMemory(size: Long, block: (Memory) -> R): R

/**
 * Execute block of code providing a temporary Memory view of this byte array range.
 * @param offset starting position in array
 * @param length number of bytes to use
 * @param block operation to perform with the memory view
 * @return result of the block operation
 */
fun <R> ByteArray.useMemory(offset: Int = 0, length: Int, block: (Memory) -> R): R

Advanced Usage Examples:

import io.ktor.utils.io.bits.*

// Memory allocation strategies
withMemory(1024) { smallMemory ->
    // Use small memory allocation
}

withMemory(1024 * 1024) { largeMemory ->
    // Use large memory allocation
}

// Use existing byte arrays as memory
val existingArray = ByteArray(100) { it.toByte() }
existingArray.useMemory { wrappedMemory ->
    // Use entire array as memory
}

existingArray.useMemory(offset = 10, length = 50) { partialMemory ->
    // Use part of array as memory
}

// Complex data serialization
class Header(val magic: Int, val version: Short, val flags: Byte)
class Payload(val data: ByteArray)

fun serializeMessage(memory: Memory, header: Header, payload: Payload): Int {
    var offset = 0
    
    // Write header
    memory.storeIntAt(offset, header.magic)
    offset += 4
    memory.storeShortAt(offset, header.version)
    offset += 2
    memory.storeAt(offset, header.flags)
    offset += 1
    
    // Write payload size
    memory.storeIntAt(offset, payload.data.size)
    offset += 4
    
    // Write payload data
    payload.data.forEachIndexed { index, byte ->
        memory.storeAt(offset + index, byte)
    }
    offset += payload.data.size
    
    return offset // Total bytes written
}

fun deserializeMessage(memory: Memory): Pair<Header, Payload> {
    var offset = 0
    
    // Read header
    val magic = memory.loadIntAt(offset)
    offset += 4
    val version = memory.loadShortAt(offset)
    offset += 2
    val flags = memory.loadAt(offset)
    offset += 1
    
    val header = Header(magic, version, flags)
    
    // Read payload
    val payloadSize = memory.loadIntAt(offset)
    offset += 4
    
    val payloadData = ByteArray(payloadSize) { index ->
        memory.loadAt(offset + index)
    }
    
    val payload = Payload(payloadData)
    
    return header to payload
}

// Usage
withMemory(1024) { memory ->
    val header = Header(0x12345678, 1, 0xFF.toByte())
    val payload = Payload("Hello World".toByteArray())

    val bytesWritten = serializeMessage(memory, header, payload)
    val (deserializedHeader, deserializedPayload) = deserializeMessage(memory)
}

Install with Tessl CLI

npx tessl i tessl/maven-io-ktor--ktor-io-watchosarm64

docs

async-channels.md

byte-order.md

character-encoding.md

index.md

memory-management.md

object-pooling.md

packet-io.md

tile.json