rarestype/swift-bson
π **swift-bson** π
Requirements
The swift-bson library requires Swift 6.0 or later.
| Platform | Status | | -------- | ------ | | π¬ Documentation | [[Status]](https://github.com/rarestype/swift-bson/actions/workflows/Documentation.yml) | | π§ Linux | [[Status]](https://github.com/rarestype/swift-bson/actions/workflows/Tests.yml) | | π Darwin | [[Status]](https://github.com/rarestype/swift-bson/actions/workflows/Tests.yml) | | π Darwin (iOS) | [[Status]](https://github.com/rarestype/swift-bson/actions/workflows/Tests.yml) | | π Darwin (tvOS) | [[Status]](https://github.com/rarestype/swift-bson/actions/workflows/Tests.yml) | | π Darwin (visionOS) | [[Status]](https://github.com/rarestype/swift-bson/actions/workflows/Tests.yml) | | π Darwin (watchOS) | [[Status]](https://github.com/rarestype/swift-bson/actions/workflows/Tests.yml) |
What is BSON?
BSON is a general-purpose binary serialization format that is a superset of JSON. Parsing BSON requires much less memory than parsing JSON, and the format is traversable, which makes it possible to extract individual fields nested deep within a BSON document without actually parsing the entire file.
BSON was originally developed by MongoDB, for which it serves as its native data format. However, the file format itself is not tied to MongoDB, and can be used in any system that requires a high-performance, low-memory serialization format.
Why do I need this library?
If you are using MongoKitten, your MongoDB driver already includes a BSON parser based on the standard libraryβs Codable system, which has the advantage of generating much of the deserialization code for you automatically. However, Codable has well-known performance limitations, and is not suitable for high-throughput use cases.
Another reason to use this library is that it is portable and has few dependencies. BSON parsers provided by MongoDB drivers have dependencies on networking primitives such as ByteBuffer, which requires you to link the SwiftNIO library. For applications that simply use BSON as a storage format, this may not be desirable.
Is it faster than Codable?
The decoder is approximately 3 to 6 times faster than the default MongoKitten decoder. The encoder has similar throughput to `Codable`.
([Benchmark source code](/Benchmarks/Benchmarks/VsMongoKittenDefault))
<details>
<summary>Performance comparison </summary>
```
Host 'vscode' with 12 'x86_64' processors with 30 GB memory, running:
#202408030740 SMP PREEMPT_DYNAMIC Sat Aug 3 07:53:03 UTC 2024
====================
VsMongoKittenDefault
====================
Decode BSON with MongoKitten Default
βββββββββββββββββββββββββββββββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ
β Metric β p0 β p25 β p50 β p75 β p90 β p99 β p100 β Samples β
βββββββββββββββββββββββββββββββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββ‘
β (Alloc + Retain) - Release Ξ * β 1476 β 1500 β 1506 β 1512 β 1520 β 1529 β 1536 β 376 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Object allocs * β 2334 β 2459 β 2493 β 2521 β 2545 β 2605 β 2634 β 376 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Releases (K) * β 21 β 21 β 22 β 22 β 22 β 22 β 22 β 376 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Retains (K) * β 17 β 18 β 18 β 18 β 18 β 18 β 18 β 376 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Throughput (# / s) (#) β 617 β 592 β 583 β 572 β 562 β 415 β 234 β 376 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Time (wall clock) (ΞΌs) * β 1620 β 1690 β 1714 β 1747 β 1781 β 2408 β 4276 β 376 β
βββββββββββββββββββββββββββββββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ
Decode BSON with This Library
βββββββββββββββββββββββββββββββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ
β Metric β p0 β p25 β p50 β p75 β p90 β p99 β p100 β Samples β
βββββββββββββββββββββββββββββββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββ‘
β (Alloc + Retain) - Release Ξ * β 7917 β 8035 β 8071 β 8099 β 8123 β 8167 β 8187 β 1000 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Object allocs * β 892 β 969 β 984 β 997 β 1011 β 1034 β 1061 β 1000 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Releases (K) * β 13 β 14 β 14 β 14 β 14 β 14 β 14 β 1000 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Retains * β 4406 β 4523 β 4555 β 4583 β 4607 β 4643 β 4697 β 1000 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Throughput (# / s) (#) β 1915 β 1820 β 1791 β 1748 β 1669 β 1175 β 1061 β 1000 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Time (wall clock) (ΞΌs) * β 522 β 549 β 559 β 572 β 599 β 810 β 943 β 1000 β
βββββββββββββββββββββββββββββββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ
Encode BSON with MongoKitten Default
βββββββββββββββββββββββββββββββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ
β Metric β p0 β p25 β p50 β p75 β p90 β p99 β p100 β Samples β
βββββββββββββββββββββββββββββββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββ‘
β (Alloc + Retain) - Release Ξ * β 5054 β 5247 β 5307 β 5379 β 5427 β 5519 β 5598 β 513 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Object allocs * β 2993 β 3113 β 3153 β 3199 β 3229 β 3279 β 3323 β 513 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Releases (K) * β 39 β 41 β 41 β 42 β 42 β 43 β 44 β 513 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Retains (K) * β 31 β 33 β 33 β 33 β 34 β 34 β 35 β 513 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Throughput (# / s) (#) β 198 β 189 β 185 β 182 β 177 β 162 β 132 β 513 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Time (wall clock) (ΞΌs) * β 5039 β 5300 β 5394 β 5501 β 5661 β 6181 β 7598 β 513 β
βββββββββββββββββββββββββββββββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ
Encode BSON with This Library
βββββββββββββββββββββββββββββββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ€ββββββββββ
β Metric β p0 β p25 β p50 β p75 β p90 β p99 β p100 β Samples β
βββββββββββββββββββββββββββββββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββͺββββββββββ‘
β (Alloc + Retain) - Release Ξ * β 5066 β 5251 β 5311 β 5375 β 5427 β 5531 β 5614 β 514 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Object allocs * β 3003 β 3123 β 3153 β 3199 β 3229 β 3293 β 3353 β 514 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Releases (K) * β 39 β 41 β 41 β 42 β 42 β 43 β 44 β 514 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Retains (K) * β 31 β 33 β 33 β 33 β 34 β 34 β 35 β 514 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Throughput (# / s) (#) β 198 β 189 β 186 β 183 β 178 β 159 β 95 β 514 β
βββββββββββββββββββββββββββββββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Time (wall clock) (ΞΌs) * β 5061 β 5280 β 5366 β 5464 β 5612 β 6275 β 10509 β 514 β
βββββββββββββββββββββββββββββββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ
```
</details>Should I really be using BSON?
BSON is not for everyone. The rationales below are not good reasons to adopt BSON, at least by themselves.
Saving disk space
BSON will save memory when parsing, but in typical use cases, a BSON file will occupy a similar amount of space as an equivalent JSON file, and offer a similar compression ratio.
Serving to the web
BSON is generally considered a server side format, and there are few compelling reasons to synthesize it for the sole purpose of serving content to browsers.
That said, JavaScript libraries do exist for parsing BSON, so it is possible to use it on the client side. One good reason to do this is if you are storing BSON objects as static resources accessible from a CDN, and want clients to be able to download the BSON from the CDN instead of converting it dynamically to JSON via your HTTP server.
Is it worth the effort?
Learning this library will enable you to use a high-performance binary serialization format across a wide range of platforms. The library is small, written in pure Swift, and organized around a few key patterns that emphasize maintainability in large codebases.
Although swift-bson cannot synthesize serialization code for you, its idioms are predictable and easily βpaintableβ by LLMs such as GitHub Copilot.
What does the code look like?
In a βrealisticβ codebase, a BSON model type looks like this:
struct ExampleModel: BSONDocumentEncodable, BSONDocumentDecodable {
let id: Int64
let name: String?
let rank: Rank
// snippet.hide
init(id: Int64, name: String?, rank: Rank) {
self.id = id
self.name = name
self.rank = rank
}
// snippet.show
enum Rank: Int32, BSONEncodable, BSONDecodable {
case newModel
case risingStar
case aspiringModel
case fashionista
case glamourista
case fashionMaven
case runwayQueen
case trendSetter
case runwayDiva
case topModel
}
enum CodingKey: String, Sendable {
case id = "_id"
case name = "D"
case rank = "R"
}
func encode(to bson: inout BSON.DocumentEncoder<CodingKey>) {
bson[.id] = self.id
bson[.name] = self.name
bson[.rank] = self.rank == .newModel ? nil : self.rank
}
init(bson: BSON.DocumentDecoder<CodingKey>) throws {
self.id = try bson[.id].decode()
self.name = try bson[.name]?.decode()
self.rank = try bson[.rank]?.decode() ?? .newModel
}
}The code to actually round-trip this to and from raw data looks like this:
let models: [ExampleModel] = [
.init(id: 0, name: "Gigi", rank: .topModel),
.init(id: 1, name: nil, rank: .newModel),
]
/// Round-trip one model
let document: BSON.Document = .init(encoding: models[0])
let _: ArraySlice<UInt8> = document.bytes
let model: ExampleModel = try .init(bson: document)
/// Round-trip a list of models
let list: BSON.List = .init(elements: models)
let _: ArraySlice<UInt8> = list.bytes
let array: [ExampleModel] = try .init(bson: list)Tutorials
License
The swift-bson library is Apache 2.0 licensed.
Package Metadata
Repository: rarestype/swift-bson
Default branch: master
README: README.md