Lilliput 是一个原生的 Swift 框架,用于处理二进制数据。
import Lilliput
// Allocate enough storage for a 32-bit unsigned integer
let buffer = ByteBuffer(count: 4)
try buffer.slice(...) { bytes in
// Write a UInt32 to that storage in little endian encoding
var writer = ByteSpanWriter(bytes)
try writer.write(UInt32(100), as: UInt32.LittleEndian.self)
// Read a UInt32 from that stroage in little endian encoding
var reader = ByteSpanReader(bytes)
let value = try reader.read(UInt32.LittleEndian.self)
}
struct MyType {
var value0: Int
var value1: Int8
var value3: Double
}
extension MyType: ByteDecoder {
static func decode<R: Reader>(from reader: inout R) throws -> MyType {
return MyType(
value0: Int(try reader.read(UInt32.LittleEndian.self)),
value1: try reader.read(Int8.self),
value2: try reader.read(Float64.LittleEndian.self)
)
}
}
extension MyType: ByteEncoder {
static func encode<W: Writer>(_ value: MyType, to writer: inout W) throws {
try writer.write(UInt32(value.value0), as: UInt32.LittleEndian.self)
try writer.write(value.value1)
try writer.write(value.value2, as: Float64.LittleEndian.self)
}
}
extension MyType {
@frozen enum Custom {}
}
extension MyType.Custom: ByteDecoder {
static func decode<R: Reader>(from reader: inout R) throws -> MyType {
// Values are loaded/stored in reverse order from the type definition and using big endian
let value2 = try reader.read(Float64.BigEndian.self)
let value1 = try reader.read(Int8.self)
try reader.alignTo(4) // Make sure next read occurs after three padding bytes
let value0 = Int(try reader.read(UInt32.BigEndian.self))
return MyType(
value0: value0,
value1: value1,
value2: value2
)
}
}
extension MyType.Custom: ByteEncoder {
static func encode<W: Writer>(_ value: MyType, to writer: inout W) throws {
// Values are loaded/stored in reverse order from the type definition and using big endian
try writer.write(value.value2, as: Float64.BigEndian.self)
try writer.write(value.value1)
try writer.write((0, 0, 0), as: UInt8.Tuple3.self) // Add three padding bytes
try writer.write(UInt32(value.value0), as: UInt32.BigEndian.self)
}
}
import Lilliput
import SystemPackage
let file = try FileDescriptor.open(path, .readOnly)
let buffer = ByteBuffer(count: 13)
let myType = try buffer.slice(...) { bytes in
try file.readAll(into: bytes)
var reader = ByteSpanReader(bytes)
return try reader.read(MyType.self)
}
如果数据是使用 Custom 编码存储在磁盘上
return try reader.read(MyType.Custom.self)
import Lilliput
import SystemPackage
let myType = MyType(value0: 100, value1: 8, value2: 300.56)
let buffer = ByteBuffer(count: 13)
try buffer.slice(...) { bytes in
var writer = SpanByteWriter(buffer)
try writer.write(myType)
}
let file = try FileDescriptor.open(path, .writeOnly)
file.writeAll(buffer)
如果我们想使用 Custom 编码存储数据
try writer.write(myType, as: MyType.Custom.self)
let count = Int(try reader.read(UInt32.LittleEndian.self))
let arrayOfMyType = try reader.read(Element<MyType>.self, count: count)
try writer.write(UInt32(arrayOfMyType.count), as: UInt32.LittleEndian.self)
try writer.write(arrayOfMyType, as: Element<MyType>.self)
注意:由于关于如何编码计数器值的决定在不同用例中可能差异很大,因此该功能未内置到库中。
@frozen struct MyArray<E> {}
extension MyArray: ByteDecoder where E: ByteDecoder {
static func decode<R: Reader>(from reader: inout R) throws -> [E.Decodable] {
let count = Int(try reader.read(UInt32.LittleEndian.self))
return try reader.read(Element<E>.self, count: count)
}
}
extension MyArray: ByteEncoder where E: ByteEncoder {
static func encode<W: ByteWriter>(_ value: [E.Encodable], to writer: inout W) throws {
try writer.write(UInt32(value.count), as: UInt32.LittleEndian.self)
for element in value {
try writer.write(element, as: E.self)
}
}
}
用法(请注意,计数器值现在无需额外工作即可处理)
let arrayOfMyType = try reader.read(MyArray<MyType>.self)
try writer.write(arrayOfMyType, as: MyArray<MyType>.self)
let data = Data(...) // Get data somehow
var reader = DataReader(data: data, maxReadCount: 8)
let myType = try reader.read(MyType.self)
为 maxReadCount
选择一个值,该值表示一次调用 read
中读取的最大字节数。 在上面的示例中,最长的读取是 Double
,它是 8 个字节。 在最坏的情况下,您可以省略 maxReadCount
参数,然后 Data 的整个长度将用作默认值。
ByteReader
上的所有 read
方法和 ByteWriter
上的所有 write
方法,如果它们不涉及 ByteDecoder
/ByteEncoder
,则不再检查是否有足够的字节来完成操作。 现在,这通过调用 ensure
单独处理。 例如,如果没有足够的字节可以读取,try reader.ensure(5)
将抛出异常。
此更改的副作用是,当您写入单个 UInt8
时,它不再触发 ByteEncoder
的 UInt8
实现。
以前你可以这样做,如果空间不足以写入,它会抛出异常
try writer.write(UInt8(7))
现在 ByteWriter
上新的接受单个 UInt8
的 write
方法取代了它。 要获得新行为,请执行以下操作
try ensure(1)
writer.write(UInt8(7))
或执行此操作,手动触发 ByteEncoder
实现
try writer.write(UInt8(7), as: UInt8.self)
要在 SwiftPM 项目中使用 Lilliput
库,请将以下行添加到 Package.swift
文件中的依赖项中
.package(url: "https://github.com/jkolb/Lilliput.git", from: "12.0.0"),
最后,将 "Lilliput"
作为目标的依赖项包含进来
let package = Package(
// name, platforms, products, etc.
dependencies: [
.package(url: "https://github.com/jkolb/Lilliput.git", from: "12.0.0"),
// other dependencies
],
targets: [
.target(name: "MyTarget", dependencies: [
.product(name: "Lilliput", package: "Lilliput"),
]),
// other targets
]
)
Lilliput 在 MIT 许可下可用。 有关更多信息,请参见 LICENSE 文件。