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 (& ContiguousCollection) 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>: Type, at: Int)

Example:

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

buf.set(integer: 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.write(string: "Hello World")
 buf.write(integer: 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.write(integer: dataBytesLength) /* the header */
buf.write(bytes: dataBytes) /* the data */
let bufDataBytesOnly = buf.getSlice(at: 4, length: dataBytes.count)
/* `bufDataByteOnly` and `buf` will share their storage */

Important usage notes

Each method that is prefixed with get is considered unsafe as it allows the user to read uninitialized memory if the index or index + length points outside of the previous written range of the ByteBuffer. Because of this it’s strongly advised to prefer the usage of methods that start with the read prefix and only use the get prefixed methods if there is a strong reason for doing so. In any case, if you use the get prefixed methods you are responsible for ensuring that you do not reach into uninitialized memory by taking the readableBytes and readerIndex into account, and ensuring that you have previously written into the area covered by the index itself.

  • 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 }
  • Change the capacity to at least to bytes.

    Declaration

    Swift

    @available(*, deprecated, message: "changeCapacity has been replaced by reserveCapacity")
    public mutating func changeCapacity(to newCapacity: Int)

    Parameters

    to

    The desired minimum capacity.

  • 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

    @_inlineable
    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

    @_inlineable
    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
    @_inlineable
    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

    @_inlineable
    public func withVeryUnsafeBytes<T>(_ body: (UnsafeRawBufferPointer) 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

    @_inlineable
    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

    @_inlineable
    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

    @_inlineable
    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.

    Note

    Please consider using readSlice which is a safer alternative that automatically maintains the readerIndex and won’t allow you to slice off uninitialized memory.

    Warning

    This method allows the user to slice out any of the bytes in the ByteBuffer‘s storage, including uninitialized ones. To use this API in a safe way the user needs to make sure all the requested bytes have been written before and are therefore initialized. Note that bytes between (including) readerIndex and (excluding) writerIndex are always initialized by contract and therefore must be safe to read.

    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.

  • 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.

    Note

    Please consider using readBytes which is a safer alternative that automatically maintains the readerIndex and won’t allow you to read uninitialized memory.

    Warning

    This method allows the user to read any of the bytes in the ByteBuffer‘s storage, including uninitialized ones. To use this API in a safe way the user needs to make sure all the requested bytes have been written before and are therefore initialized. Note that bytes between (including) readerIndex and (excluding) writerIndex are always initialized by contract and therefore must be safe to read.

    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 ByteBuffer doesn’t contain those bytes.

  • 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 write(staticString 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 set(staticString 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 write(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
    public mutating func set(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.

    Note

    Please consider using readString which is a safer alternative that automatically maintains the readerIndex and won’t allow you to read uninitialized memory.

    Warning

    This method allows the user to read any of the bytes in the ByteBuffer‘s storage, including uninitialized ones. To use this API in a safe way the user needs to make sure all the requested bytes have been written before and are therefore initialized. Note that bytes between (including) readerIndex and (excluding) writerIndex are always initialized by contract and therefore must be safe to read.

    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 deserialized from this ByteBuffer or nil if the requested bytes aren’t contained in this ByteBuffer.

  • 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.

  • 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

    @_inlineable
    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

    @_inlineable
    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

    @_inlineable
    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

    @_inlineable
    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 write(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
    @_inlineable
    public mutating func write<S>(bytes: S) -> Int where S : Sequence, S.Element == UInt8

    Parameters

    bytes

    A Collection of UInt8 to be written.

    Return Value

    The number of bytes written or bytes.count.

  • Write bytes, a ContiguousCollection of UInt8 into this ByteBuffer. Moves the writer index forward by the number of bytes written. This method is likely more efficient than the one operating on plain Collection as it will use memcpy to copy all the bytes in one go.

    Declaration

    Swift

    @discardableResult
    @_inlineable
    public mutating func write<S>(bytes: S) -> Int where S : ContiguousCollection, S.Element == UInt8

    Parameters

    bytes

    A ContiguousCollection of UInt8 to be written.

    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
    @_inlineable
    public mutating func set<S>(bytes: S, at index: Int) -> Int where S : Sequence, S.Element == UInt8
  • Copy the collection of bytes into the ByteBuffer at index.

    Declaration

    Swift

    @discardableResult
    @_inlineable
    public mutating func set<S>(bytes: S, at index: Int) -> Int where S : ContiguousCollection, S.Element == UInt8
  • 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

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

    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.

    Note

    Please consider using readInteger which is a safer alternative that automatically maintains the readerIndex and won’t allow you to read uninitialized memory.

    Warning

    This method allows the user to read any of the bytes in the ByteBuffer‘s storage, including uninitialized ones. To use this API in a safe way the user needs to make sure all the requested bytes have been written before and are therefore initialized. Note that bytes between (including) readerIndex and (excluding) writerIndex are always initialized by contract and therefore must be safe to read.

    Declaration

    Swift

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

    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 aren’t contained in the ByteBuffer.

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

    Declaration

    Swift

    @discardableResult
    @_inlineable
    public mutating func write<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
    @_inlineable
    public mutating func set<T: FixedWidthInteger>(integer: T, at index: Int, endianness: Endianness = .big, as: T.Type = T.self) -> Int

    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.

  • A view into the readable bytes of the ByteBuffer.

    Declaration

    Swift

    public var readableBytesView: ByteBufferView { get }
  • Returns a view into some portion of a ByteBuffer.

    Declaration

    Swift

    public func viewBytes(at index: Int, length: Int) -> ByteBufferView

    Parameters

    index

    The index the view should start at

    length

    The length of the view (in bytes)