The swift-bson library is a portable, Foundation-free library for working with BSON.
The swift-bson library requires Swift 6.0 or later.
Platform | Status |
---|---|
π§ Linux | |
π Darwin | |
π Darwin (iOS) | |
π Darwin (tvOS) | |
π Darwin (visionOS) | |
π Darwin (watchOS) |
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.
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.
The decoder is approximately 3 to 6 times faster than the default MongoKitten decoder. The encoder has similar throughput to Codable
.
Performance comparison
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 β
βββββββββββββββββββββββββββββββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ§ββββββββββ
BSON is not for everyone. The rationales below are not good reasons to adopt BSON, at least by themselves.
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.
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.
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.
In a βrealisticβ codebase, a BSON model type looks like this:
struct ExampleModel:BSONDocumentEncodable, BSONDocumentDecodable
{
let id:Int64
let name:String?
let rank:Rank
/// A custom enum type.
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
}
/// The schema definition.
enum CodingKey:String, Sendable
{
case id = "_id" // Chosen for compatibility with MongoDB
case name = "D"
case rank = "R"
}
/// The serialization logic.
func encode(to bson:inout BSON.DocumentEncoder<CodingKey>)
{
bson[.id] = self.id
bson[.name] = self.name
bson[.rank] = self.rank == .newModel ? nil : self.rank
}
/// The deserialization logic.
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)
The swift-bson library is Apache 2.0 licensed.