Skip to content

Proposal: Expose bytes from TraceID #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 111 additions & 57 deletions Sources/W3CTraceContext/SpanID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,23 @@
///
/// [W3C TraceContext: parent-id](https://www.w3.org/TR/trace-context-1/#parent-id)
public struct SpanID: Sendable {
private let _bytes: Bytes

/// An 8-byte array representation of the span ID.
public var bytes: [UInt8] {
withUnsafeBytes(of: _bytes, Array.init)
}
/// The 8 bytes making up the span ID.
public let bytes: Bytes

/// Create a span ID from 8 bytes.
///
/// - Parameter bytes: The 8 bytes making up the span ID.
public init(bytes: Bytes) {
_bytes = bytes
self.bytes = bytes
}

/// Create a random span ID using the given random number generator.
///
/// - Parameter randomNumberGenerator: The random number generator used to create random bytes for the span ID.
/// - Returns: A random span ID.
public static func random(using randomNumberGenerator: inout some RandomNumberGenerator) -> SpanID {
var bytes: SpanID.Bytes = (0, 0, 0, 0, 0, 0, 0, 0)
withUnsafeMutableBytes(of: &bytes) { ptr in
var bytes: SpanID.Bytes = .null
bytes.withUnsafeMutableBytes { ptr in
ptr.storeBytes(of: randomNumberGenerator.next().bigEndian, as: UInt64.self)
}
return SpanID(bytes: bytes)
Expand All @@ -49,68 +45,126 @@ public struct SpanID: Sendable {
var generator = SystemRandomNumberGenerator()
return random(using: &generator)
}

/// An 8-byte array.
public typealias Bytes = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
}

extension SpanID: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs._bytes.0 == rhs._bytes.0
&& lhs._bytes.1 == rhs._bytes.1
&& lhs._bytes.2 == rhs._bytes.2
&& lhs._bytes.3 == rhs._bytes.3
&& lhs._bytes.4 == rhs._bytes.4
&& lhs._bytes.5 == rhs._bytes.5
&& lhs._bytes.6 == rhs._bytes.6
&& lhs._bytes.7 == rhs._bytes.7
}
}
extension SpanID: Equatable {}

extension SpanID: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(_bytes.0)
hasher.combine(_bytes.1)
hasher.combine(_bytes.2)
hasher.combine(_bytes.3)
hasher.combine(_bytes.4)
hasher.combine(_bytes.5)
hasher.combine(_bytes.6)
hasher.combine(_bytes.7)
}
}
extension SpanID: Hashable {}

extension SpanID: Identifiable {
public var id: [UInt8] { bytes }
public var id: Self { self }
}

extension SpanID: CustomStringConvertible {
/// A 16 character hex string representation of the span ID.
public var description: String {
"\(bytes)"
}
}

// MARK: - Bytes

extension SpanID {
/// An 8-byte array.
public struct Bytes: Collection, Equatable, Hashable, Sendable {
public static var null: Self { SpanID.Bytes((0, 0, 0, 0, 0, 0, 0, 0)) }

@usableFromInline
var _bytes: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)

public init(_ bytes: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)) {
_bytes = bytes
}

public subscript(position: Int) -> UInt8 {
switch position {
case 0: _bytes.0
case 1: _bytes.1
case 2: _bytes.2
case 3: _bytes.3
case 4: _bytes.4
case 5: _bytes.5
case 6: _bytes.6
case 7: _bytes.7
default: fatalError("Index out of range")
}
}

public func index(after i: Int) -> Int {
precondition(i < endIndex, "Can't advance beyond endIndex")
return i + 1
}

public var startIndex: Int { 0 }
public var endIndex: Int { 8 }

@inlinable
public func withContiguousStorageIfAvailable<Result>(
_ body: (UnsafeBufferPointer<UInt8>) throws -> Result
) rethrows -> Result? {
try Swift.withUnsafeBytes(of: _bytes) { bytes in
try bytes.withMemoryRebound(to: UInt8.self, body)
}
}

/// Calls the given closure with a pointer to the span ID's underlying bytes.
///
/// - Parameter body: A closure receiving an `UnsafeRawBufferPointer` to the span ID's underlying bytes.
@inlinable
public func withUnsafeBytes<Result>(_ body: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result {
try Swift.withUnsafeBytes(of: _bytes, body)
}

/// Calls the given closure with a mutable pointer to the span ID's underlying bytes.
///
/// - Parameter body: A closure receiving an `UnsafeMutableRawBufferPointer` to the span ID's underlying bytes.
@inlinable
public mutating func withUnsafeMutableBytes<Result>(
_ body: (UnsafeMutableRawBufferPointer) throws -> Result
) rethrows -> Result {
try Swift.withUnsafeMutableBytes(of: &_bytes) { bytes in
try body(bytes)
}
}

public static func == (lhs: Self, rhs: Self) -> Bool {
lhs._bytes.0 == rhs._bytes.0
&& lhs._bytes.1 == rhs._bytes.1
&& lhs._bytes.2 == rhs._bytes.2
&& lhs._bytes.3 == rhs._bytes.3
&& lhs._bytes.4 == rhs._bytes.4
&& lhs._bytes.5 == rhs._bytes.5
&& lhs._bytes.6 == rhs._bytes.6
&& lhs._bytes.7 == rhs._bytes.7
}

public func hash(into hasher: inout Hasher) {
hasher.combine(_bytes.0)
hasher.combine(_bytes.1)
hasher.combine(_bytes.2)
hasher.combine(_bytes.3)
hasher.combine(_bytes.4)
hasher.combine(_bytes.5)
hasher.combine(_bytes.6)
hasher.combine(_bytes.7)
}
}
}

extension SpanID.Bytes: CustomStringConvertible {
/// A 16 character hex string representation of the bytes.
public var description: String {
String(decoding: hexBytes, as: UTF8.self)
}

/// A 16 character UTF-8 hex byte array representation of the span ID.
/// A 16 character UTF-8 hex byte array representation of the bytes.
public var hexBytes: [UInt8] {
var asciiBytes: (UInt64, UInt64) = (0, 0)
return withUnsafeMutableBytes(of: &asciiBytes) { ptr in
ptr[0] = Hex.lookup[Int(_bytes.0 >> 4)]
ptr[1] = Hex.lookup[Int(_bytes.0 & 0x0F)]
ptr[2] = Hex.lookup[Int(_bytes.1 >> 4)]
ptr[3] = Hex.lookup[Int(_bytes.1 & 0x0F)]
ptr[4] = Hex.lookup[Int(_bytes.2 >> 4)]
ptr[5] = Hex.lookup[Int(_bytes.2 & 0x0F)]
ptr[6] = Hex.lookup[Int(_bytes.3 >> 4)]
ptr[7] = Hex.lookup[Int(_bytes.3 & 0x0F)]
ptr[8] = Hex.lookup[Int(_bytes.4 >> 4)]
ptr[9] = Hex.lookup[Int(_bytes.4 & 0x0F)]
ptr[10] = Hex.lookup[Int(_bytes.5 >> 4)]
ptr[11] = Hex.lookup[Int(_bytes.5 & 0x0F)]
ptr[12] = Hex.lookup[Int(_bytes.6 >> 4)]
ptr[13] = Hex.lookup[Int(_bytes.6 & 0x0F)]
ptr[14] = Hex.lookup[Int(_bytes.7 >> 4)]
ptr[15] = Hex.lookup[Int(_bytes.7 & 0x0F)]
return Array(ptr)
var asciiBytes = [UInt8](repeating: 0, count: 16)
for i in startIndex ..< endIndex {
let byte = self[i]
asciiBytes[2 * i] = Hex.lookup[Int(byte >> 4)]
asciiBytes[2 * i + 1] = Hex.lookup[Int(byte & 0x0F)]
}
return asciiBytes
}
}
34 changes: 5 additions & 29 deletions Sources/W3CTraceContext/TraceContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,47 +69,23 @@ public struct TraceContext: Sendable {

// trace ID

var traceIDBytes = TraceID.Bytes(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
var traceIDBytes = TraceID.Bytes.null
withUnsafeMutableBytes(of: &traceIDBytes) { ptr in
Hex.convert(traceParent[3 ..< 35], toBytes: ptr)
}
if traceIDBytes.0 == 0,
traceIDBytes.1 == 0,
traceIDBytes.2 == 0,
traceIDBytes.3 == 0,
traceIDBytes.4 == 0,
traceIDBytes.5 == 0,
traceIDBytes.6 == 0,
traceIDBytes.7 == 0,
traceIDBytes.8 == 0,
traceIDBytes.9 == 0,
traceIDBytes.10 == 0,
traceIDBytes.11 == 0,
traceIDBytes.12 == 0,
traceIDBytes.13 == 0,
traceIDBytes.14 == 0,
traceIDBytes.15 == 0
{
if traceIDBytes == .null {
throw TraceParentDecodingError(
.invalidTraceID(String(decoding: traceParent[3 ..< 35], as: UTF8.self))
)
}

// span ID

var spanIDBytes = SpanID.Bytes(0, 0, 0, 0, 0, 0, 0, 0)
withUnsafeMutableBytes(of: &spanIDBytes) { ptr in
var spanIDBytes = SpanID.Bytes.null
spanIDBytes.withUnsafeMutableBytes { ptr in
Hex.convert(traceParent[36 ..< 52], toBytes: ptr)
}
if spanIDBytes.0 == 0,
spanIDBytes.1 == 0,
spanIDBytes.2 == 0,
spanIDBytes.3 == 0,
spanIDBytes.4 == 0,
spanIDBytes.5 == 0,
spanIDBytes.6 == 0,
spanIDBytes.7 == 0
{
if spanIDBytes == .null {
throw TraceParentDecodingError(
.invalidSpanID(String(decoding: traceParent[36 ..< 52], as: UTF8.self))
)
Expand Down
Loading
Loading