-
Notifications
You must be signed in to change notification settings - Fork 690
Description
ByteBuffer's API is great for common in-memory binary data storage and for parsing/serializing data.
One of the problem that I find in every single project I have is the poor performance of ByteBuffer's backing storage which Swift is retaining/releasing every single API call. I'd love to have a data type such as UnsafeByteBuffer
and UnsafeMutableByteBuffer
which is the same API wrapped around a manually allocated/deallocated buffer.
I'm very sure that this would open up many opportunities for optimizing current applications such as Database drivers, JSON libraries and templating libraries. Even if those oppurtunities are there now, it'd still be a great improvement in the ease of doing said operations.
Databases
Many databases will, for example, return a contiguous buffer with many separate entities. For each entity, an encoder will need to be created which decodes the info of a single entity's row/document into a struct/class. I
struct DatabasePacket {
let buffer: ByteBuffer
var rows: Int {
// computed property to return the amount of rows read from the buffer as indicated by a packet header
}
}
let packet: DatabasePacket = ...
var dataModels = [DataModel]() // Of course you'd reserve capacity etc...
for _ in 0..<packet.rows {
// A
let rowLength = packet.buffer.readInteger(as: Int32.self)
let unsafeByteBuffer = packet.readUnsafeByteBuffer(length: Int(rowLength))
let model = try DatabaseRowDecoder().decode(DataModel.self, from: unsafeByteBuffer)
dataModels.append(model)
}
UnsafeByteBuffer Pseudo Implementation
I'd directly refer to the pointer and provide an API for reading the data from that. Like ByteBuffer but without wrapping a class for auto-deallocation. It also wouldn't allow mutations and therefore mutation.
struct UnsafeByteBuffer {
private let pointer: UnsafeRawPointer
private var readerIndex: Int
private let totalBytes: Int
}
UnsafeMutableByteBuffer Pseudo Implementation
I'd directly refer to the pointer again to provide an API for reading the data from that. Again without wrapping a class for auto-deallocation. But it would allow mutations, but would fatalError
or at least do an assertionFailure
if you try to access data outside of bounds.
struct UnsafeMutableByteBuffer {
private let pointer: UnsafeMutableRawPointer
public private(set) var readerIndex: Int
public private(set) var writerIndex: Int
private let totalBytes: Int
private var isDeallocated = false
mutating func deallocate() {
if isDeallocated { return }
isDeallocated = true
pointer.deallocate()
}
}
The goal of this type is not to read data efficiently, but to modify current buffers manually and/or to provide a useful interface. It might support auto-expansion using mutating methods. But allocation/deallocation is 100% within the developer's control.