Textures and Coordinates

BSON documents can embed arbitrary binary data. This is useful for storing trivial repeating values like RGB colors or 3D coordinates, and can save an enormous amount of keying overhead, though at the cost of making the data un-queryable, as the database will not understand your custom data format.

Textures and Coordinates.md

Endianness

Although most real-world systems are little-endian, you should always assume that your data will be read by and written from systems with varying endianness.

To help you avoid making mistakes, the swift-bson library provides the BSON.BinaryBuffer and BSON.BinaryArray abstractions. The latter, which accounts for endianness, is layered on top of the former, which does not. The BSON.BinaryPackable protocol serves as the bridge between the two.

Worked Example

Below is a worked example of how to efficiently round trip a mesh of 3D triangles.

Defining Point and Triangle Types

struct Point3D
{
    var x:Float
    var y:Float
    var z:Float
}
Triangles.swift:3
struct Triangle3D
{
    var a:Point3D
    var b:Point3D
    var c:Point3D
}
Triangles.swift:16

Conforming to BSON.BinaryPackable

extension Point3D:BSON.BinaryPackable
{
    typealias Storage = (UInt32, UInt32, UInt32)

    static func get(_ storage:Storage) -> Self
    {
        .init(x: .get(storage.0), y: .get(storage.1), z: .get(storage.2))
    }

    consuming func set() -> Storage
    {
        (self.x.set(), self.y.set(), self.z.set())
    }
}
Triangles.swift:29
extension Triangle3D:BSON.BinaryPackable
{
    typealias Storage = (Point3D.Storage, Point3D.Storage, Point3D.Storage)

    static func get(_ storage:Storage) -> Self
    {
        .init(a: .get(storage.0), b: .get(storage.1), c: .get(storage.2))
    }

    consuming func set() -> Storage
    {
        (self.a.set(), self.b.set(), self.c.set())
    }
}
Triangles.swift:44

Defining the Mesh Buffer

struct Mesh3D
{
    let triangles:[Triangle3D]
}
extension Mesh3D:BSONArrayEncodable, RandomAccessCollection
{
    var startIndex:Int { self.triangles.startIndex }
    var endIndex:Int { self.triangles.endIndex }

    subscript(position:Int) -> Triangle3D { self.triangles[position] }
}
extension Mesh3D:BSONArrayDecodable
{
    init(from array:borrowing BSON.BinaryArray<Triangle3D>) throws
    {
        self.triangles = array.map(\.self)
    }
}
Triangles.swift:61

Defining the Top-Level Document

struct MeshContainer<Value> where Value:BSONEncodable, Value:BSONDecodable
{
    let value:Value

    enum CodingKey:String, Sendable
    {
        case value = "V"
    }
}
extension MeshContainer:BSONDocumentEncodable, BSONDocumentDecodable
{
    func encode(to bson:inout BSON.DocumentEncoder<CodingKey>)
    {
        bson[.value] = self.value
    }

    init(bson:BSON.DocumentDecoder<CodingKey>) throws
    {
        self.value = try bson[.value].decode()
    }
}
Triangles.swift:81

Round-tripping the Mesh

let triangles:[Triangle3D] = [
    .init(
        a: .init(x: 0, y: 0, z: 0),
        b: .init(x: 1, y: 0, z: 0),
        c: .init(x: 0, y: 1, z: 0)),
    .init(
        a: .init(x: 1, y: 0, z: 0),
        b: .init(x: 1, y: 1, z: 0),
        c: .init(x: 0, y: 1, z: 0)),
]

let mesh:MeshContainer<Mesh3D> = .init(value: .init(triangles: triangles))
let meshEncoded:BSON.Document = .init(encoding: mesh)
let meshDecoded:MeshContainer<Mesh3D> = try .init(bson: meshEncoded)

print(meshDecoded.value.triangles)
Triangles.swift:107
let trianglesEagerlyEncoded:BSON.BinaryArray<Triangle3D> = triangles.indices.reduce(
    into: .init(count: triangles.count))
{
    $0[$1] = triangles[$1]
}
let view:MeshContainer<BSON.BinaryArray<Triangle3D>> = .init(
    value: trianglesEagerlyEncoded)
let viewEncoded:BSON.Document = .init(encoding: view)
let viewDecoded:MeshContainer<BSON.BinaryArray<Triangle3D>> = try .init(
    bson: viewEncoded)

for triangleLazilyDecoded:Triangle3D in viewDecoded.value
{
    print(triangleLazilyDecoded)
}
Triangles.swift:125

See also

  • BSON Usage Examples

    Using BSON in production generally consists of three kinds of tasks: defining root types that model the documents you want to store as BSON, defining reusable supporting types to model fields in other types, and actually encoding and decoding BSON documents to and from binary data.

    Read More
  • BSON Decoding and Encoding, Explained

    This article walks through the process of implementing BSONDocumentDecodable and BSONDocumentEncodable conformances for a model type in detail, and explains the rationale behind library’s design decisions.

    Read More
  • Advanced Serialization Patterns

    Level up your BSON encoding skills by learning these composable serialiation patterns. Internalizing these techniques will help you write more efficient and maintainable database applications by avoiding temporary allocations and reusing generic library code.

    Read More