ByteBuffer

public struct ByteBuffer

ByteBuffer stores contiguously allocated raw bytes. It is a random and sequential accessible sequence of zero or more bytes (octets).

Allocation

Use allocator.buffer(capacity: desiredCapacity) to allocate a new ByteBuffer.

Supported types

A variety of types can be read/written from/to a ByteBuffer. Using Swift’s extension mechanism you can easily create ByteBuffer support for your own data types. Out of the box, ByteBuffer supports for example the following types (non-exhaustive list):

  • String/StaticString
  • Swift’s various (unsigned) integer types
  • Foundation‘s Data
  • [UInt8] and generally any Collection of UInt8

Random Access

For every supported type ByteBuffer usually contains two methods for random access:

  1. get<Type>(at: Int, length: Int) where <type> is for example String, Data, Bytes (for [UInt8])
  2. set<Type>(at: Int)

Example:

var buf = ...
buf.setString("Hello World", at: 0)
let helloWorld = buf.getString(at: 0, length: 11)

buf.setInteger(17 as Int, at: 11)
let seventeen: Int = buf.getInteger(at: 11)

If needed, ByteBuffer will automatically resize its storage to accommodate your set request.

Sequential Access

ByteBuffer provides two properties which are indices into the ByteBuffer to support sequential access:

  • readerIndex, the index of the next readable byte
  • writerIndex, the index of the next byte to write

For every supported type ByteBuffer usually contains two methods for sequential access:

  1. read<Type>(length: Int) to read length bytes from the current readerIndex (and then advance the reader index by length bytes)
  2. write<Type>(Type) to write, advancing the writerIndex by the appropriate amount

Example:

 var buf = ...
 buf.writeString("Hello World")
 buf.writeInteger(17 as Int)
 let helloWorld = buf.readString(length: 11)
 let seventeen: Int = buf.readInteger()

Layout

+-------------------+------------------+------------------+
| discardable bytes |  readable bytes  |  writable bytes  |
|                   |     (CONTENT)    |                  |
+-------------------+------------------+------------------+
|                   |                  |                  |
0      <=      readerIndex   <=   writerIndex    <=    capacity

The 'discardable bytes’ are usually bytes that have already been read, they can however still be accessed using the random access methods. ‘Readable bytes’ are the bytes currently available to be read using the sequential access interface (read<Type>/write<Type>). Getting writableBytes (bytes beyond the writer index) is undefined behaviour and might yield arbitrary bytes (not 0 initialised).

Slicing

ByteBuffer supports slicing a ByteBuffer without copying the underlying storage.

Example:

var buf = ...
let dataBytes: [UInt8] = [0xca, 0xfe, 0xba, 0xbe]
let dataBytesLength = UInt32(dataBytes.count)
buf.writeInteger(dataBytesLength) /* the header */
buf.writeBytes(dataBytes) /* the data */
let bufDataBytesOnly = buf.getSlice(at: 4, length: dataBytes.count)
/* `bufDataByteOnly` and `buf` will share their storage */

Notes

All ByteBuffer methods that don’t contain the word ‘unsafe’ will only allow you to access the ‘readable bytes’.

  • The number of bytes writable until ByteBuffer will need to grow its underlying storage which will likely trigger a copy of the bytes.

    Declaration

    Swift

    public var writableBytes: Int { get }
  • The number of bytes readable (readableBytes = writerIndex - readerIndex).

    Declaration

    Swift

    public var readableBytes: Int { get }
  • The current capacity of the storage of this ByteBuffer, this is not constant and does not signify the number of bytes that have been written to this ByteBuffer.

    Declaration

    Swift

    public var capacity: Int { get }
  • Reserves enough space to store the specified number of bytes.

    This method will ensure that the buffer has space for at least as many bytes as requested. This includes any bytes already stored, and completely disregards the reader/writer indices. If the buffer already has space to store the requested number of bytes, this method will be a no-op.

    Declaration

    Swift

    public mutating func reserveCapacity(_ minimumCapacity: Int)

    Parameters

    minimumCapacity

    The minimum number of bytes this buffer must be able to store.

  • Yields a mutable buffer pointer containing this ByteBuffer‘s readable bytes. You may modify those bytes.

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @inlinable
    public mutating func withUnsafeMutableReadableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T

    Parameters

    body

    The closure that will accept the yielded bytes.

    Return Value

    The value returned by fn.

  • Yields the bytes currently writable (bytesWritable = capacity - writerIndex). Before reading those bytes you must first write to them otherwise you will trigger undefined behaviour. The writer index will remain unchanged.

    Note

    In almost all cases you should use writeWithUnsafeMutableBytes which will move the write pointer instead of this method

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @inlinable
    public mutating func withUnsafeMutableWritableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T

    Parameters

    body

    The closure that will accept the yielded bytes and return the number of bytes written.

    Return Value

    The number of bytes written.

  • Undocumented

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func writeWithUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int
  • This vends a pointer to the storage of the ByteBuffer. It’s marked as very unsafe because it might contain uninitialised memory and it’s undefined behaviour to read it. In most cases you should use withUnsafeReadableBytes.

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @inlinable
    public func withVeryUnsafeBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T
  • This vends a pointer to the storage of the ByteBuffer. It’s marked as very unsafe because it might contain uninitialised memory and it’s undefined behaviour to read it. In most cases you should use withUnsafeMutableWritableBytes.

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @inlinable
    public mutating func withVeryUnsafeMutableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T
  • Yields a buffer pointer containing this ByteBuffer‘s readable bytes.

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @inlinable
    public func withUnsafeReadableBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T

    Parameters

    body

    The closure that will accept the yielded bytes.

    Return Value

    The value returned by fn.

  • Yields a buffer pointer containing this ByteBuffer‘s readable bytes. You may hold a pointer to those bytes even after the closure returned iff you model the lifetime of those bytes correctly using the Unmanaged instance. If you don’t require the pointer after the closure returns, use withUnsafeReadableBytes.

    If you escape the pointer from the closure, you must call storageManagement.retain() to get ownership to the bytes and you also must call storageManagement.release() if you no longer require those bytes. Calls to retain and release must be balanced.

    Declaration

    Swift

    @inlinable
    public func withUnsafeReadableBytesWithStorageManagement<T>(_ body: (UnsafeRawBufferPointer, Unmanaged<AnyObject>) throws -> T) rethrows -> T

    Parameters

    body

    The closure that will accept the yielded bytes and the storageManagement.

    Return Value

    The value returned by fn.

  • See withUnsafeReadableBytesWithStorageManagement and withVeryUnsafeBytes.

    Declaration

    Swift

    @inlinable
    public func withVeryUnsafeBytesWithStorageManagement<T>(_ body: (UnsafeRawBufferPointer, Unmanaged<AnyObject>) throws -> T) rethrows -> T
  • Returns a slice of size length bytes, starting at index. The ByteBuffer this is invoked on and the ByteBuffer returned will share the same underlying storage. However, the byte at index in this ByteBuffer will correspond to index 0 in the returned ByteBuffer. The readerIndex of the returned ByteBuffer will be 0, the writerIndex will be length.

    The selected bytes must be readable or else nil will be returned.

    Declaration

    Swift

    public func getSlice(at index: Int, length: Int) -> ByteBuffer?

    Parameters

    index

    The index the requested slice starts at.

    length

    The length of the requested slice.

    Return Value

    A ByteBuffer containing the selected bytes as readable bytes or nil if the selected bytes were not readable in the initial ByteBuffer.

  • Discard the bytes before the reader index. The byte at index readerIndex before calling this method will be at index 0 after the call returns.

    Declaration

    Swift

    @discardableResult
    public mutating func discardReadBytes() -> Bool

    Return Value

    true if one or more bytes have been discarded, false if there are no bytes to discard.

  • The reader index or the number of bytes previously read from this ByteBuffer. readerIndex is 0 for a newly allocated ByteBuffer.

    Declaration

    Swift

    public var readerIndex: Int { get }
  • The write index or the number of bytes previously written to this ByteBuffer. writerIndex is 0 for a newly allocated ByteBuffer.

    Declaration

    Swift

    public var writerIndex: Int { get }
  • Set both reader index and writer index to 0. This will reset the state of this ByteBuffer to the state of a freshly allocated one, if possible without allocations. This is the cheapest way to recycle a ByteBuffer for a new use-case.

    Note

    This method will allocate if the underlying storage is referenced by another ByteBuffer. Even if an allocation is necessary this will be cheaper as the copy of the storage is elided.

    Declaration

    Swift

    public mutating func clear()
  • Get length bytes starting at index and return the result as [UInt8]. This will not change the reader index. The selected bytes must be readable or else nil will be returned.

    Declaration

    Swift

    public func getBytes(at index: Int, length: Int) -> [UInt8]?

    Parameters

    index

    The starting index of the bytes of interest into the ByteBuffer.

    length

    The number of bytes of interest.

    Return Value

    A [UInt8] value containing the bytes of interest or nil if the bytes ByteBuffer are not readable.

  • Read length bytes off this ByteBuffer, move the reader index forward by length bytes and return the result as [UInt8].

    Declaration

    Swift

    public mutating func readBytes(length: Int) -> [UInt8]?

    Parameters

    length

    The number of bytes to be read from this ByteBuffer.

    Return Value

    A [UInt8] value containing length bytes or nil if there aren’t at least length bytes readable.

  • Write the static string into this ByteBuffer using UTF-8 encoding, moving the writer index forward appropriately.

    Declaration

    Swift

    @discardableResult
    public mutating func writeStaticString(_ string: StaticString) -> Int

    Parameters

    string

    The string to write.

    Return Value

    The number of bytes written.

  • Write the static string into this ByteBuffer at index using UTF-8 encoding, moving the writer index forward appropriately.

    Declaration

    Swift

    public mutating func setStaticString(_ string: StaticString, at index: Int) -> Int

    Parameters

    string

    The string to write.

    index

    The index for the first serialized byte.

    Return Value

    The number of bytes written.

  • Write string into this ByteBuffer using UTF-8 encoding, moving the writer index forward appropriately.

    Declaration

    Swift

    @discardableResult
    public mutating func writeString(_ string: String) -> Int

    Parameters

    string

    The string to write.

    Return Value

    The number of bytes written.

  • Write string into this ByteBuffer at index using UTF-8 encoding. Does not move the writer index.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func setString(_ string: String, at index: Int) -> Int

    Parameters

    string

    The string to write.

    index

    The index for the first serialized byte.

    Return Value

    The number of bytes written.

  • Get the string at index from this ByteBuffer decoding using the UTF-8 encoding. Does not move the reader index. The selected bytes must be readable or else nil will be returned.

    Declaration

    Swift

    public func getString(at index: Int, length: Int) -> String?

    Parameters

    index

    The starting index into ByteBuffer containing the string of interest.

    length

    The number of bytes making up the string.

    Return Value

    A String value containing the UTF-8 decoded selected bytes from this ByteBuffer or nil if the requested bytes are not readable.

  • Read length bytes off this ByteBuffer, decoding it as String using the UTF-8 encoding. Move the reader index forward by length.

    Declaration

    Swift

    public mutating func readString(length: Int) -> String?

    Parameters

    length

    The number of bytes making up the string.

    Return Value

    A String value deserialized from this ByteBuffer or nil if there aren’t at least length bytes readable.

  • Write dispatchData into this ByteBuffer, moving the writer index forward appropriately.

    Declaration

    Swift

    @discardableResult
    public mutating func writeDispatchData(_ dispatchData: DispatchData) -> Int

    Parameters

    dispatchData

    The DispatchData instance to write to the ByteBuffer.

    Return Value

    The number of bytes written.

  • Write dispatchData into this ByteBuffer at index. Does not move the writer index.

    Declaration

    Swift

    @discardableResult
    public mutating func setDispatchData(_ dispatchData: DispatchData, at index: Int) -> Int

    Parameters

    dispatchData

    The DispatchData to write.

    index

    The index for the first serialized byte.

    Return Value

    The number of bytes written.

  • Get the bytes at index from this ByteBuffer as a DispatchData. Does not move the reader index. The selected bytes must be readable or else nil will be returned.

    Declaration

    Swift

    public func getDispatchData(at index: Int, length: Int) -> DispatchData?

    Parameters

    index

    The starting index into ByteBuffer containing the string of interest.

    length

    The number of bytes.

    Return Value

    A DispatchData value deserialized from this ByteBuffer or nil if the requested bytes are not readable.

  • Read length bytes off this ByteBuffer and return them as a DispatchData. Move the reader index forward by length.

    Declaration

    Swift

    public mutating func readDispatchData(length: Int) -> DispatchData?

    Parameters

    length

    The number of bytes.

    Return Value

    A DispatchData value containing the bytes from this ByteBuffer or nil if there aren’t at least length bytes readable.

  • Yields an immutable buffer pointer containing this ByteBuffer‘s readable bytes. Will move the reader index by the number of bytes returned by fn.

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func readWithUnsafeReadableBytes(_ body: (UnsafeRawBufferPointer) throws -> Int) rethrows -> Int

    Parameters

    body

    The closure that will accept the yielded bytes and returns the number of bytes it processed.

    Return Value

    The number of bytes read.

  • Yields an immutable buffer pointer containing this ByteBuffer‘s readable bytes. Will move the reader index by the number of bytes fn returns in the first tuple component.

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @inlinable
    public mutating func readWithUnsafeReadableBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> (Int, T)) rethrows -> T

    Parameters

    body

    The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value.

    Return Value

    The value fn returned in the second tuple component.

  • Yields a mutable buffer pointer containing this ByteBuffer‘s readable bytes. You may modify the yielded bytes. Will move the reader index by the number of bytes returned by fn but leave writer index as it was.

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func readWithUnsafeMutableReadableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int

    Parameters

    body

    The closure that will accept the yielded bytes and returns the number of bytes it processed.

    Return Value

    The number of bytes read.

  • Yields a mutable buffer pointer containing this ByteBuffer‘s readable bytes. You may modify the yielded bytes. Will move the reader index by the number of bytes fn returns in the first tuple component but leave writer index as it was.

    Warning

    Do not escape the pointer from the closure for later use.

    Declaration

    Swift

    @inlinable
    public mutating func readWithUnsafeMutableReadableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> (Int, T)) rethrows -> T

    Parameters

    body

    The closure that will accept the yielded bytes and returns the number of bytes it processed along with some other value.

    Return Value

    The value fn returned in the second tuple component.

  • Copy buffer‘s readable bytes into this ByteBuffer starting at index. Does not move any of the reader or writer indices.

    Declaration

    Swift

    @discardableResult
    public mutating func set(buffer: ByteBuffer, at index: Int) -> Int

    Parameters

    buffer

    The ByteBuffer to copy.

    index

    The index for the first byte.

    Return Value

    The number of bytes written.

  • Write buffer‘s readable bytes into this ByteBuffer starting at writerIndex. This will move both this ByteBuffer’s writer index as well as buffer’s reader index by the number of bytes readable in buffer.

    Declaration

    Swift

    @discardableResult
    public mutating func writeBuffer(_ buffer: inout ByteBuffer) -> Int

    Parameters

    buffer

    The ByteBuffer to write.

    Return Value

    The number of bytes written to this ByteBuffer which is equal to the number of bytes read from buffer.

  • Write bytes, a Sequence of UInt8 into this ByteBuffer. Moves the writer index forward by the number of bytes written.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func writeBytes<Bytes>(_ bytes: Bytes) -> Int where Bytes : Sequence, Bytes.Element == UInt8

    Parameters

    bytes

    A Collection of UInt8 to be written.

    Return Value

    The number of bytes written or bytes.count.

  • Write bytes into this ByteBuffer. Moves the writer index forward by the number of bytes written.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func writeBytes(_ bytes: UnsafeRawBufferPointer) -> Int

    Parameters

    bytes

    An UnsafeRawBufferPointer

    Return Value

    The number of bytes written or bytes.count.

  • Slice the readable bytes off this ByteBuffer without modifying the reader index. This method will return a ByteBuffer sharing the underlying storage with the ByteBuffer the method was invoked on. The returned ByteBuffer will contain the bytes in the range readerIndex..<writerIndex of the original ByteBuffer.

    Note

    Because ByteBuffer implements copy-on-write a copy of the storage will be automatically triggered when either of the ByteBuffers sharing storage is written to.

    Declaration

    Swift

    public func slice() -> ByteBuffer

    Return Value

    A ByteBuffer sharing storage containing the readable bytes only.

  • Slice length bytes off this ByteBuffer and move the reader index forward by length. If enough bytes are readable the ByteBuffer returned by this method will share the underlying storage with the ByteBuffer the method was invoked on. The returned ByteBuffer will contain the bytes in the range readerIndex..<(readerIndex + length) of the original ByteBuffer. The readerIndex of the returned ByteBuffer will be 0, the writerIndex will be length.

    Note

    Because ByteBuffer implements copy-on-write a copy of the storage will be automatically triggered when either of the ByteBuffers sharing storage is written to.

    Declaration

    Swift

    public mutating func readSlice(length: Int) -> ByteBuffer?

    Parameters

    length

    The number of bytes to slice off.

    Return Value

    A ByteBuffer sharing storage containing length bytes or nil if the not enough bytes were readable.

  • A String describing this ByteBuffer. Example:

    ByteBuffer { readerIndex: 0, writerIndex: 4, readableBytes: 4, capacity: 512, slice: 256..<768, storage: 0x0000000103001000 (1024 bytes)}
    

    The format of the description is not API.

    Declaration

    Swift

    public var description: String { get }

    Return Value

    A description of this ByteBuffer.

  • A String describing this ByteBuffer with some portion of the readable bytes dumped too. Example:

    ByteBuffer { readerIndex: 0, writerIndex: 4, readableBytes: 4, capacity: 512, slice: 256..<768, storage: 0x0000000103001000 (1024 bytes)}
    readable bytes (max 1k): [ 00 01 02 03 ]
    

    The format of the description is not API.

    Declaration

    Swift

    public var debugDescription: String { get }

    Return Value

    A description of this ByteBuffer useful for debugging.

  • Copy the collection of bytes into the ByteBuffer at index.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func setBytes<Bytes>(_ bytes: Bytes, at index: Int) -> Int where Bytes : Sequence, Bytes.Element == UInt8
  • Copy bytes into the ByteBuffer at index.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func setBytes(_ bytes: UnsafeRawBufferPointer, at index: Int) -> Int
  • Move the reader index forward by offset bytes.

    Warning

    By contract the bytes between (including) readerIndex and (excluding) writerIndex must be initialised, ie. have been written before. Also the readerIndex must always be less than or equal to the writerIndex. Failing to meet either of these requirements leads to undefined behaviour.

    Declaration

    Swift

    public mutating func moveReaderIndex(forwardBy offset: Int)

    Parameters

    offset

    The number of bytes to move the reader index forward by.

  • Set the reader index to offset.

    Warning

    By contract the bytes between (including) readerIndex and (excluding) writerIndex must be initialised, ie. have been written before. Also the readerIndex must always be less than or equal to the writerIndex. Failing to meet either of these requirements leads to undefined behaviour.

    Declaration

    Swift

    public mutating func moveReaderIndex(to offset: Int)

    Parameters

    offset

    The offset in bytes to set the reader index to.

  • Move the writer index forward by offset bytes.

    Warning

    By contract the bytes between (including) readerIndex and (excluding) writerIndex must be initialised, ie. have been written before. Also the readerIndex must always be less than or equal to the writerIndex. Failing to meet either of these requirements leads to undefined behaviour.

    Declaration

    Swift

    public mutating func moveWriterIndex(forwardBy offset: Int)

    Parameters

    offset

    The number of bytes to move the writer index forward by.

  • Set the writer index to offset.

    Warning

    By contract the bytes between (including) readerIndex and (excluding) writerIndex must be initialised, ie. have been written before. Also the readerIndex must always be less than or equal to the writerIndex. Failing to meet either of these requirements leads to undefined behaviour.

    Declaration

    Swift

    public mutating func moveWriterIndex(to offset: Int)

    Parameters

    offset

    The offset in bytes to set the reader index to.

  • Compare two ByteBuffer values. Two ByteBuffer values are considered equal if the readable bytes are equal.

    Declaration

    Swift

    public static func == (lhs: ByteBuffer, rhs: ByteBuffer) -> Bool
  • Read an integer off this ByteBuffer, move the reader index forward by the integer’s byte size and return the result.

    Declaration

    Swift

    @inlinable
    public mutating func readInteger<T>(endianness: Endianness = .big, as: T.Type = T.self) -> T? where T : FixedWidthInteger

    Parameters

    endianness

    The endianness of the integer in this ByteBuffer (defaults to big endian).

    as

    the desired FixedWidthInteger type (optional parameter)

    Return Value

    An integer value deserialized from this ByteBuffer or nil if there aren’t enough bytes readable.

  • Get the integer at index from this ByteBuffer. Does not move the reader index. The selected bytes must be readable or else nil will be returned.

    Declaration

    Swift

    @inlinable
    public func getInteger<T>(at index: Int, endianness: Endianness = Endianness.big, as: T.Type = T.self) -> T? where T : FixedWidthInteger

    Parameters

    index

    The starting index of the bytes for the integer into the ByteBuffer.

    endianness

    The endianness of the integer in this ByteBuffer (defaults to big endian).

    as

    the desired FixedWidthInteger type (optional parameter)

    Return Value

    An integer value deserialized from this ByteBuffer or nil if the bytes of interest are not readable.

  • Write integer into this ByteBuffer, moving the writer index forward appropriately.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func writeInteger<T: FixedWidthInteger>(_ integer: T,
                                                            endianness: Endianness = .big,
                                                            as: T.Type = T.self) -> Int

    Parameters

    integer

    The integer to serialize.

    endianness

    The endianness to use, defaults to big endian.

    Return Value

    The number of bytes written.

  • Write integer into this ByteBuffer starting at index. This does not alter the writer index.

    Declaration

    Swift

    @discardableResult
    @inlinable
    public mutating func setInteger<T>(_ integer: T, at index: Int, endianness: Endianness = .big, as: T.Type = T.self) -> Int where T : FixedWidthInteger

    Parameters

    integer

    The integer to serialize.

    index

    The index of the first byte to write.

    endianness

    The endianness to use, defaults to big endian.

    Return Value

    The number of bytes written.