I’m making a lot of use of the BinaryParsing package; it’s a godsend for reading binary data.
But I also need to write structured binary formats, and AFAIK there’s not a good solution for that, so I’ve started writing my own. I’ve got a BinaryWriter protocol with a variety of write() methods that take Spans, UnsafeBufferPointers, integers, Strings… One implementation appends to a Data object, the other writes into a fixed-capacity MutableSpan.
If I’m reinventing the wheel, please let me know! Otherwise, would there be interest in having me extract this into a reusable package?
/** Protocol for a binary output stream, which may or may not have a limited capacity. */
package protocol BinaryWriter: ~Copyable, ~Escapable {
/// The number of bytes written to this stream.
var count: Int {get}
/// The number of bytes more that can be written to this stream.
/// (If the stream has no fixed capacity, this will be something close to `Int.max`!)
var available: Int {get}
/// Writes raw bytes to the stream. May throw if there's not enough capacity.
mutating func write(unsafeBytes bytes: UnsafeRawPointer?, length: Int)
/// The closure is passed a MutableSpan whose size is `maxCount` if there's enough capacity,
/// else the writer's remaining capacity.
/// It should write to some prefix of the buffer, then return the number of bytes it wrote.
mutating func writeInto(maxCount: Int,
fn: (inout MutableSpan<UInt8>) throws -> Int) rethrows
/// Passes the closure a span of the already-written bytes at the given range.
func readBytes<T>(range: some RangeExpression<Int>, fn: (Span<UInt8>) -> T) -> T
/// Reads or writes an already-written byte.
subscript(index: Int) -> UInt8 {get set}
}
/** Free bonus methods for BinaryWriter!!! */
package extension BinaryWriter where Self : ~Copyable, Self : ~Escapable {
/// True if no more bytes can be written.
var isFull: Bool {available == 0}
mutating func write(unsafeBuffer: UnsafeRawBufferPointer)
mutating func write(bytes: RawSpan)
mutating func write(byte: UInt8)
mutating func write(bigEndian: some FixedWidthInteger)
/// Writes a non-negative integer in (Go-style) varint form.
mutating func write(varint: some BinaryInteger)
/// Writes a String in UTF-8 encoding.
/// - note Does not write a trailing 0 byte!
mutating func write(utf8: String)
}