diff --git a/Sources/W3CTraceContext/SpanID.swift b/Sources/W3CTraceContext/SpanID.swift index 8090882..51df113 100644 --- a/Sources/W3CTraceContext/SpanID.swift +++ b/Sources/W3CTraceContext/SpanID.swift @@ -16,18 +16,14 @@ /// /// [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. @@ -35,8 +31,8 @@ public struct SpanID: Sendable { /// - 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) @@ -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( + _ body: (UnsafeBufferPointer) 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(_ 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( + _ 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 } } diff --git a/Sources/W3CTraceContext/TraceContext.swift b/Sources/W3CTraceContext/TraceContext.swift index 67e76ab..6c514df 100644 --- a/Sources/W3CTraceContext/TraceContext.swift +++ b/Sources/W3CTraceContext/TraceContext.swift @@ -69,27 +69,11 @@ 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)) ) @@ -97,19 +81,11 @@ public struct TraceContext: Sendable { // 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)) ) diff --git a/Sources/W3CTraceContext/TraceID.swift b/Sources/W3CTraceContext/TraceID.swift index 97a2754..8ed80c3 100644 --- a/Sources/W3CTraceContext/TraceID.swift +++ b/Sources/W3CTraceContext/TraceID.swift @@ -16,18 +16,14 @@ /// /// [W3C TraceContext: trace-id](https://www.w3.org/TR/trace-context-1/#trace-id) public struct TraceID: Sendable { - private let _bytes: Bytes - - /// A 16-byte array representation of the span ID. - public var bytes: [UInt8] { - withUnsafeBytes(of: _bytes, Array.init) - } + /// The 16 bytes making up the trace ID. + public let bytes: Bytes /// Create a trace ID from 16 bytes. /// - /// - Parameter bytes: The eight bytes making up the span ID. + /// - Parameter bytes: The 16 bytes making up the trace ID. public init(bytes: Bytes) { - _bytes = bytes + self.bytes = bytes } /// Create a random trace ID using the given random number generator. @@ -35,10 +31,12 @@ public struct TraceID: Sendable { /// - Parameter randomNumberGenerator: The random number generator used to create random bytes for the trace ID. /// - Returns: A random trace ID. public static func random(using randomNumberGenerator: inout some RandomNumberGenerator) -> TraceID { - var bytes: TraceID.Bytes = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - withUnsafeMutableBytes(of: &bytes) { ptr in - ptr.storeBytes(of: randomNumberGenerator.next().bigEndian, as: UInt64.self) - ptr.storeBytes(of: randomNumberGenerator.next().bigEndian, toByteOffset: 8, as: UInt64.self) + var bytes: TraceID.Bytes = .null + bytes.withUnsafeMutableBytes { ptr in + let rand1 = randomNumberGenerator.next() + let rand2 = randomNumberGenerator.next() + ptr.storeBytes(of: rand1.bigEndian, toByteOffset: 0, as: UInt64.self) + ptr.storeBytes(of: rand2.bigEndian, toByteOffset: 8, as: UInt64.self) } return TraceID(bytes: bytes) } @@ -50,103 +48,156 @@ public struct TraceID: Sendable { var generator = SystemRandomNumberGenerator() return random(using: &generator) } - - /// A 16-byte array. - public typealias Bytes = ( - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 - ) } -extension TraceID: 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 - && lhs._bytes.8 == rhs._bytes.8 - && lhs._bytes.9 == rhs._bytes.9 - && lhs._bytes.10 == rhs._bytes.10 - && lhs._bytes.11 == rhs._bytes.11 - && lhs._bytes.12 == rhs._bytes.12 - && lhs._bytes.13 == rhs._bytes.13 - && lhs._bytes.14 == rhs._bytes.14 - && lhs._bytes.15 == rhs._bytes.15 - } +extension TraceID: Equatable {} + +extension TraceID: Hashable {} + +extension TraceID: Identifiable { + public var id: Self { self } } -extension TraceID: 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) - hasher.combine(_bytes.8) - hasher.combine(_bytes.9) - hasher.combine(_bytes.10) - hasher.combine(_bytes.11) - hasher.combine(_bytes.12) - hasher.combine(_bytes.13) - hasher.combine(_bytes.14) - hasher.combine(_bytes.15) +extension TraceID: CustomStringConvertible { + /// A 32-character hex string representation of the trace ID. + public var description: String { + "\(bytes)" } } -extension TraceID: Identifiable { - public var id: [UInt8] { bytes } +// MARK: - Bytes + +extension TraceID { + /// A 16-byte array. + public struct Bytes: Collection, Equatable, Hashable, Sendable { + public static var null: Self { TraceID.Bytes((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) } + + @usableFromInline + var _bytes: ( + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 + ) + + public init(_ bytes: ( + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + 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 + case 8: _bytes.8 + case 9: _bytes.9 + case 10: _bytes.10 + case 11: _bytes.11 + case 12: _bytes.12 + case 13: _bytes.13 + case 14: _bytes.14 + case 15: _bytes.15 + 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 { 16 } + + @inlinable + public func withContiguousStorageIfAvailable( + _ body: (UnsafeBufferPointer) 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 trace ID's underlying bytes. + /// - Parameter body: A closure receiving an `UnsafeRawBufferPointer` to the trace ID's underlying bytes. + @inlinable + public func withUnsafeBytes( + _ body: (UnsafeRawBufferPointer) throws -> Result + ) rethrows -> Result { + try Swift.withUnsafeBytes(of: _bytes, body) + } + + /// Calls the given closure with a mutable pointer to the trace ID's underlying bytes. + /// - Parameter body: A closure receiving an `UnsafeMutableRawBufferPointer` to the trace ID's underlying bytes. + @inlinable + public mutating func withUnsafeMutableBytes( + _ 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 && + lhs._bytes.8 == rhs._bytes.8 && + lhs._bytes.9 == rhs._bytes.9 && + lhs._bytes.10 == rhs._bytes.10 && + lhs._bytes.11 == rhs._bytes.11 && + lhs._bytes.12 == rhs._bytes.12 && + lhs._bytes.13 == rhs._bytes.13 && + lhs._bytes.14 == rhs._bytes.14 && + lhs._bytes.15 == rhs._bytes.15 + } + + 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) + hasher.combine(_bytes.8) + hasher.combine(_bytes.9) + hasher.combine(_bytes.10) + hasher.combine(_bytes.11) + hasher.combine(_bytes.12) + hasher.combine(_bytes.13) + hasher.combine(_bytes.14) + hasher.combine(_bytes.15) + } + } } -extension TraceID: CustomStringConvertible { - /// A 32 character hex string representation of the span ID. +extension TraceID.Bytes: CustomStringConvertible { + /// A 32-character hex string representation of the bytes. public var description: String { String(decoding: hexBytes, as: UTF8.self) } - /// A 32 character UTF-8 hex byte array representation of the span ID. + /// A 32-character UTF-8 hex byte array representation of the bytes. public var hexBytes: [UInt8] { - var asciiBytes: (UInt64, UInt64, UInt64, UInt64) = (0, 0, 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)] - ptr[16] = Hex.lookup[Int(_bytes.8 >> 4)] - ptr[17] = Hex.lookup[Int(_bytes.8 & 0x0F)] - ptr[18] = Hex.lookup[Int(_bytes.9 >> 4)] - ptr[19] = Hex.lookup[Int(_bytes.9 & 0x0F)] - ptr[20] = Hex.lookup[Int(_bytes.10 >> 4)] - ptr[21] = Hex.lookup[Int(_bytes.10 & 0x0F)] - ptr[22] = Hex.lookup[Int(_bytes.11 >> 4)] - ptr[23] = Hex.lookup[Int(_bytes.11 & 0x0F)] - ptr[24] = Hex.lookup[Int(_bytes.12 >> 4)] - ptr[25] = Hex.lookup[Int(_bytes.12 & 0x0F)] - ptr[26] = Hex.lookup[Int(_bytes.13 >> 4)] - ptr[27] = Hex.lookup[Int(_bytes.13 & 0x0F)] - ptr[28] = Hex.lookup[Int(_bytes.14 >> 4)] - ptr[29] = Hex.lookup[Int(_bytes.14 & 0x0F)] - ptr[30] = Hex.lookup[Int(_bytes.15 >> 4)] - ptr[31] = Hex.lookup[Int(_bytes.15 & 0x0F)] - return Array(ptr) + var asciiBytes = [UInt8](repeating: 0, count: 32) + 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 } } diff --git a/Tests/W3CTraceContextTests/SpanID+Stubs.swift b/Tests/W3CTraceContextTests/SpanID+Stubs.swift index fc15d63..cb8148c 100644 --- a/Tests/W3CTraceContextTests/SpanID+Stubs.swift +++ b/Tests/W3CTraceContextTests/SpanID+Stubs.swift @@ -16,5 +16,5 @@ import W3CTraceContext extension SpanID { /// A stubbed `SpanID` with bytes from one to eight. - static let oneToEight = SpanID(bytes: (1, 2, 3, 4, 5, 6, 7, 8)) + static let oneToEight = SpanID(bytes: .init((1, 2, 3, 4, 5, 6, 7, 8))) } diff --git a/Tests/W3CTraceContextTests/SpanIDTests.swift b/Tests/W3CTraceContextTests/SpanIDTests.swift index 04d286a..bc68037 100644 --- a/Tests/W3CTraceContextTests/SpanIDTests.swift +++ b/Tests/W3CTraceContextTests/SpanIDTests.swift @@ -19,12 +19,12 @@ final class SpanIDTests: XCTestCase { func test_bytes_returnsEightByteArrayRepresentation() { let spanID = SpanID.oneToEight - XCTAssertEqual(spanID.bytes, [1, 2, 3, 4, 5, 6, 7, 8]) + XCTAssertEqual(Array(spanID.bytes), [1, 2, 3, 4, 5, 6, 7, 8]) } func test_equatableConformance() { - let spanID1 = SpanID(bytes: (1, 2, 3, 4, 5, 6, 7, 8)) - let spanID2 = SpanID(bytes: (1, 2, 3, 4, 5, 6, 7, 0)) + let spanID1 = SpanID(bytes: .init((1, 2, 3, 4, 5, 6, 7, 8))) + let spanID2 = SpanID(bytes: .init((1, 2, 3, 4, 5, 6, 7, 0))) XCTAssertEqual(spanID1, spanID1) XCTAssertEqual(spanID2, spanID2) @@ -38,7 +38,7 @@ final class SpanIDTests: XCTestCase { } func test_description_returnsHexStringRepresentation() { - let spanID = SpanID(bytes: (0, 10, 20, 50, 100, 150, 200, 255)) + let spanID = SpanID(bytes: .init((0, 10, 20, 50, 100, 150, 200, 255))) XCTAssertEqual("\(spanID)", "000a14326496c8ff") } @@ -47,10 +47,10 @@ final class SpanIDTests: XCTestCase { var generator = IncrementingRandomNumberGenerator() let spanID1 = SpanID.random(using: &generator) - XCTAssertEqual(spanID1, SpanID(bytes: (0, 0, 0, 0, 0, 0, 0, 0))) + XCTAssertEqual(spanID1, SpanID(bytes: .init((0, 0, 0, 0, 0, 0, 0, 0)))) let spanID2 = SpanID.random(using: &generator) - XCTAssertEqual(spanID2, SpanID(bytes: (0, 0, 0, 0, 0, 0, 0, 1))) + XCTAssertEqual(spanID2, SpanID(bytes: .init((0, 0, 0, 0, 0, 0, 0, 1)))) } func test_random_withDefaultNumberGenerator_returnsRandomSpanIDs() { @@ -58,4 +58,35 @@ final class SpanIDTests: XCTestCase { XCTAssertEqual(Set(randomSpanIDs).count, 100) } + + // MARK: - Bytes + + func test_spanIDBytes_withUnsafeBytes_invokesClosureWithPointerToBytes() { + let bytes = SpanID.Bytes((0, 10, 20, 50, 100, 150, 200, 255)) + + let byteArray = bytes.withUnsafeBytes { ptr in + Array(ptr) + } + XCTAssertEqual(byteArray, [0, 10, 20, 50, 100, 150, 200, 255]) + } + + func test_spanIDBytes_withUnsafeMutableBytes_allowsMutatingBytesViaClosure() { + var bytes = SpanID.Bytes((0, 10, 20, 50, 100, 150, 200, 255)) + + bytes.withUnsafeMutableBytes { ptr in + ptr.storeBytes(of: 42, as: UInt8.self) + } + + XCTAssertEqual(Array(bytes), [42, 10, 20, 50, 100, 150, 200, 255]) + } + + func test_withContiguousStorageIfAvailable_invokesClosureWithPointerToBytes() { + let bytes = SpanID.Bytes((0, 10, 20, 50, 100, 150, 200, 255)) + + let byteArray = bytes.withContiguousStorageIfAvailable { ptr in + Array(ptr) + } + + XCTAssertEqual(byteArray, [0, 10, 20, 50, 100, 150, 200, 255]) + } } diff --git a/Tests/W3CTraceContextTests/TraceContextTests.swift b/Tests/W3CTraceContextTests/TraceContextTests.swift index 45edab5..3e90001 100644 --- a/Tests/W3CTraceContextTests/TraceContextTests.swift +++ b/Tests/W3CTraceContextTests/TraceContextTests.swift @@ -22,8 +22,8 @@ final class TraceContextTests: XCTestCase { ) XCTAssertEqual(traceContext, TraceContext( - traceID: TraceID(bytes: (10, 247, 101, 25, 22, 205, 67, 221, 132, 72, 235, 33, 28, 128, 49, 156)), - spanID: SpanID(bytes: (183, 173, 107, 113, 105, 32, 51, 49)), + traceID: TraceID(bytes: .init((10, 247, 101, 25, 22, 205, 67, 221, 132, 72, 235, 33, 28, 128, 49, 156))), + spanID: SpanID(bytes: .init((183, 173, 107, 113, 105, 32, 51, 49))), flags: .sampled, state: TraceState() )) @@ -36,8 +36,8 @@ final class TraceContextTests: XCTestCase { ) XCTAssertEqual(traceContext, TraceContext( - traceID: TraceID(bytes: (10, 247, 101, 25, 22, 205, 67, 221, 132, 72, 235, 33, 28, 128, 49, 156)), - spanID: SpanID(bytes: (183, 173, 107, 113, 105, 32, 51, 49)), + traceID: TraceID(bytes: .init((10, 247, 101, 25, 22, 205, 67, 221, 132, 72, 235, 33, 28, 128, 49, 156))), + spanID: SpanID(bytes: .init((183, 173, 107, 113, 105, 32, 51, 49))), flags: .sampled, state: TraceState([ (.simple("foo"), "bar"), diff --git a/Tests/W3CTraceContextTests/TraceID+Stubs.swift b/Tests/W3CTraceContextTests/TraceID+Stubs.swift index 98c1b3b..666ffbe 100644 --- a/Tests/W3CTraceContextTests/TraceID+Stubs.swift +++ b/Tests/W3CTraceContextTests/TraceID+Stubs.swift @@ -16,5 +16,5 @@ import W3CTraceContext extension TraceID { /// A stubbed `TraceID` with bytes from one to sixteen. - static let oneToSixteen = TraceID(bytes: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) + static let oneToSixteen = TraceID(bytes: .init((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))) } diff --git a/Tests/W3CTraceContextTests/TraceIDTests.swift b/Tests/W3CTraceContextTests/TraceIDTests.swift index 85a5246..efa7649 100644 --- a/Tests/W3CTraceContextTests/TraceIDTests.swift +++ b/Tests/W3CTraceContextTests/TraceIDTests.swift @@ -19,12 +19,13 @@ final class TraceIDTests: XCTestCase { func test_bytes_returnsSixteenByteArrayRepresentation() { let traceID = TraceID.oneToSixteen - XCTAssertEqual(traceID.bytes, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) + let array = Array(traceID.bytes) + XCTAssertEqual(array, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) } func test_equatableConformance() { - let traceID1 = TraceID(bytes: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) - let traceID2 = TraceID(bytes: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0)) + let traceID1 = TraceID(bytes: .init((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))) + let traceID2 = TraceID(bytes: .init((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0))) XCTAssertEqual(traceID1, traceID1) XCTAssertEqual(traceID2, traceID2) @@ -38,7 +39,7 @@ final class TraceIDTests: XCTestCase { } func test_description_returnsHexStringRepresentation() { - let traceID = TraceID(bytes: (0, 10, 20, 30, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 255)) + let traceID = TraceID(bytes: .init((0, 10, 20, 30, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 255))) XCTAssertEqual("\(traceID)", "000a141e283c5064788ca0b4c8dcf0ff") } @@ -47,10 +48,10 @@ final class TraceIDTests: XCTestCase { var generator = IncrementingRandomNumberGenerator() let traceID1 = TraceID.random(using: &generator) - XCTAssertEqual(traceID1, TraceID(bytes: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1))) + XCTAssertEqual(traceID1, TraceID(bytes: .init((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)))) let traceID2 = TraceID.random(using: &generator) - XCTAssertEqual(traceID2, TraceID(bytes: (0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3))) + XCTAssertEqual(traceID2, TraceID(bytes: .init((0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3)))) } func test_random_withDefaultNumberGenerator_returnsRandomSpanIDs() { @@ -58,4 +59,35 @@ final class TraceIDTests: XCTestCase { XCTAssertEqual(Set(randomTraceIDs).count, 100) } + + // MARK: - Bytes + + func test_traceIDBytes_withUnsafeBytes_invokesClosureWithPointerToBytes() { + let bytes = TraceID.Bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) + + let byteArray = bytes.withUnsafeBytes { ptr in + Array(ptr) + } + XCTAssertEqual(byteArray, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) + } + + func test_traceIDBytes_withUnsafeMutableBytes_allowsMutatingBytesViaClosure() { + var bytes = TraceID.Bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) + + bytes.withUnsafeMutableBytes { ptr in + ptr.storeBytes(of: 42, as: UInt8.self) + } + + XCTAssertEqual(Array(bytes), [42, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) + } + + func test_withContiguousStorageIfAvailable_invokesClosureWithPointerToBytes() { + let bytes = TraceID.Bytes((1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) + + let byteArray = bytes.withContiguousStorageIfAvailable { ptr in + Array(ptr) + } + + XCTAssertEqual(byteArray, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) + } }