Asynchronous I/O library for Kotlin multiplatform providing channels, streams, and byte manipulation utilities optimized for watchOS ARM64
—
Low-level memory operations providing direct byte access, primitive type loading/storing, and platform-optimized memory utilities.
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)
}
}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 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
}
}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): RAdvanced 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