Add utilties for reading and writing UUIDs (#2045)
Motivation: UUIDs are often sent over the wire but writing and reading their bytes to/from a buffer is a bit of a pain. Modifications: - Add utilties to 'NIOFoundationCompat' for reading/writing and getting/setting a UUID on a `ByteBuffer`. Result: Easier to write/read UUIDs to/from a buffer. Co-authored-by: Cory Benfield <lukasa@apple.com>
This commit is contained in:
parent
d086bab75a
commit
edfceecba1
|
@ -265,6 +265,101 @@ extension ByteBuffer {
|
|||
}
|
||||
return written
|
||||
}
|
||||
|
||||
// MARK: - UUID
|
||||
|
||||
/// Get a `UUID` from the 16 bytes starting at `index`. This will not change the reader index.
|
||||
/// If there are less than 16 bytes starting at `index` then `nil` will be returned.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - index: The starting index of the bytes of interest into the `ByteBuffer`.
|
||||
/// - Returns: A `UUID` value containing the bytes of interest or `nil` if the selected bytes
|
||||
/// are not readable or there were not enough bytes.
|
||||
public func getUUIDBytes(at index: Int) -> UUID? {
|
||||
guard let chunk1 = self.getInteger(at: index, as: UInt64.self),
|
||||
let chunk2 = self.getInteger(at: index + 8, as: UInt64.self) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let uuidBytes = (
|
||||
UInt8(truncatingIfNeeded: chunk1 >> 56),
|
||||
UInt8(truncatingIfNeeded: chunk1 >> 48),
|
||||
UInt8(truncatingIfNeeded: chunk1 >> 40),
|
||||
UInt8(truncatingIfNeeded: chunk1 >> 32),
|
||||
UInt8(truncatingIfNeeded: chunk1 >> 24),
|
||||
UInt8(truncatingIfNeeded: chunk1 >> 16),
|
||||
UInt8(truncatingIfNeeded: chunk1 >> 8),
|
||||
UInt8(truncatingIfNeeded: chunk1),
|
||||
UInt8(truncatingIfNeeded: chunk2 >> 56),
|
||||
UInt8(truncatingIfNeeded: chunk2 >> 48),
|
||||
UInt8(truncatingIfNeeded: chunk2 >> 40),
|
||||
UInt8(truncatingIfNeeded: chunk2 >> 32),
|
||||
UInt8(truncatingIfNeeded: chunk2 >> 24),
|
||||
UInt8(truncatingIfNeeded: chunk2 >> 16),
|
||||
UInt8(truncatingIfNeeded: chunk2 >> 8),
|
||||
UInt8(truncatingIfNeeded: chunk2)
|
||||
)
|
||||
|
||||
return UUID(uuid: uuidBytes)
|
||||
}
|
||||
|
||||
/// Set the bytes of the `UUID` into this `ByteBuffer` at `index`, allocating more storage if
|
||||
/// necessary. Does not move the writer index.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - uuid: The UUID to set.
|
||||
/// - index: The index into the buffer where `uuid` should be written.
|
||||
/// - Returns: The number of bytes written.
|
||||
@discardableResult
|
||||
public mutating func setUUIDBytes(_ uuid: UUID, at index: Int) -> Int {
|
||||
let bytes = uuid.uuid
|
||||
|
||||
// Pack the bytes into two 'UInt64's and set them.
|
||||
let chunk1 = UInt64(bytes.0) << 56
|
||||
| UInt64(bytes.1) << 48
|
||||
| UInt64(bytes.2) << 40
|
||||
| UInt64(bytes.3) << 32
|
||||
| UInt64(bytes.4) << 24
|
||||
| UInt64(bytes.5) << 16
|
||||
| UInt64(bytes.6) << 8
|
||||
| UInt64(bytes.7)
|
||||
|
||||
let chunk2 = UInt64(bytes.8) << 56
|
||||
| UInt64(bytes.9) << 48
|
||||
| UInt64(bytes.10) << 40
|
||||
| UInt64(bytes.11) << 32
|
||||
| UInt64(bytes.12) << 24
|
||||
| UInt64(bytes.13) << 16
|
||||
| UInt64(bytes.14) << 8
|
||||
| UInt64(bytes.15)
|
||||
|
||||
var written = self.setInteger(chunk1, at: index)
|
||||
written &+= self.setInteger(chunk2, at: index &+ written)
|
||||
assert(written == 16)
|
||||
return written
|
||||
}
|
||||
|
||||
/// Read a `UUID` from the first 16 bytes in the buffer. Advances the reader index.
|
||||
///
|
||||
/// - Returns: The `UUID` or `nil` if the buffer did not contain enough bytes.
|
||||
public mutating func readUUIDBytes() -> UUID? {
|
||||
guard let uuid = self.getUUIDBytes(at: self.readerIndex) else {
|
||||
return nil
|
||||
}
|
||||
self.moveReaderIndex(forwardBy: MemoryLayout<uuid_t>.size)
|
||||
return uuid
|
||||
}
|
||||
|
||||
/// Write a `UUID` info the buffer and advances the writer index.
|
||||
///
|
||||
/// - Parameter uuid: The `UUID` to write into the buffer.
|
||||
/// - Returns: The number of bytes written.
|
||||
@discardableResult
|
||||
public mutating func writeUUIDBytes(_ uuid: UUID) -> Int {
|
||||
let written = self.setUUIDBytes(uuid, at: self.writerIndex)
|
||||
self.moveWriterIndex(forwardBy: written)
|
||||
return written
|
||||
}
|
||||
}
|
||||
|
||||
extension ByteBufferAllocator {
|
||||
|
@ -294,12 +389,12 @@ extension ByteBufferView: MutableDataProtocol {}
|
|||
|
||||
// MARK: - Data
|
||||
extension Data {
|
||||
|
||||
|
||||
/// Creates a `Data` from a given `ByteBuffer`. The entire readable portion of the buffer will be read.
|
||||
/// - parameter buffer: The buffer to read.
|
||||
public init(buffer: ByteBuffer, byteTransferStrategy: ByteBuffer.ByteTransferStrategy = .automatic) {
|
||||
var buffer = buffer
|
||||
self = buffer.readData(length: buffer.readableBytes, byteTransferStrategy: byteTransferStrategy)!
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ class LinuxMainRunner {
|
|||
testCase(ByteBufferDataProtocolTests.allTests),
|
||||
testCase(ByteBufferLengthPrefixTests.allTests),
|
||||
testCase(ByteBufferTest.allTests),
|
||||
testCase(ByteBufferUUIDTests.allTests),
|
||||
testCase(ByteBufferUtilsTest.allTests),
|
||||
testCase(ByteBufferViewDataProtocolTests.allTests),
|
||||
testCase(ByteToMessageDecoderTest.allTests),
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the SwiftNIO open source project
|
||||
//
|
||||
// Copyright (c) 2017-2022 Apple Inc. and the SwiftNIO project authors
|
||||
// Licensed under Apache License v2.0
|
||||
//
|
||||
// See LICENSE.txt for license information
|
||||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// ByteBuffer+UUIDTests+XCTest.swift
|
||||
//
|
||||
import XCTest
|
||||
|
||||
///
|
||||
/// NOTE: This file was generated by generate_linux_tests.rb
|
||||
///
|
||||
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
|
||||
///
|
||||
|
||||
extension ByteBufferUUIDTests {
|
||||
|
||||
@available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings")
|
||||
static var allTests : [(String, (ByteBufferUUIDTests) -> () throws -> Void)] {
|
||||
return [
|
||||
("testSetUUIDBytes", testSetUUIDBytes),
|
||||
("testSetUUIDBytesBlatsExistingBytes", testSetUUIDBytesBlatsExistingBytes),
|
||||
("testGetUUIDEmptyBuffer", testGetUUIDEmptyBuffer),
|
||||
("testGetUUIDAfterSet", testGetUUIDAfterSet),
|
||||
("testWriteUUIDBytesIntoEmptyBuffer", testWriteUUIDBytesIntoEmptyBuffer),
|
||||
("testWriteUUIDBytesIntoNonEmptyBuffer", testWriteUUIDBytesIntoNonEmptyBuffer),
|
||||
("testReadUUID", testReadUUID),
|
||||
("testReadUUIDNotEnoughBytes", testReadUUIDNotEnoughBytes),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the SwiftNIO open source project
|
||||
//
|
||||
// Copyright (c) 2022 Apple Inc. and the SwiftNIO project authors
|
||||
// Licensed under Apache License v2.0
|
||||
//
|
||||
// See LICENSE.txt for license information
|
||||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import NIOCore
|
||||
import NIOFoundationCompat
|
||||
import XCTest
|
||||
|
||||
final class ByteBufferUUIDTests: XCTestCase {
|
||||
func testSetUUIDBytes() {
|
||||
let uuid = UUID(uuid: (0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf))
|
||||
var buffer = ByteBuffer()
|
||||
|
||||
XCTAssertEqual(buffer.storageCapacity, 0)
|
||||
XCTAssertEqual(buffer.setUUIDBytes(uuid, at: 0), 16)
|
||||
XCTAssertEqual(buffer.writerIndex, 0)
|
||||
XCTAssertEqual(buffer.readableBytes, 0)
|
||||
XCTAssertGreaterThanOrEqual(buffer.storageCapacity, 16)
|
||||
|
||||
buffer.moveWriterIndex(forwardBy: 16)
|
||||
let bytes = buffer.getBytes(at: buffer.readerIndex, length: 16)
|
||||
XCTAssertEqual(bytes, Array(0..<16))
|
||||
}
|
||||
|
||||
func testSetUUIDBytesBlatsExistingBytes() {
|
||||
var buffer = ByteBuffer()
|
||||
buffer.writeRepeatingByte(.max, count: 32)
|
||||
|
||||
let uuid = UUID(uuid: (0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf))
|
||||
buffer.setUUIDBytes(uuid, at: buffer.readerIndex + 4)
|
||||
|
||||
XCTAssertEqual(buffer.readBytes(length: 4), Array(repeating: .max, count: 4))
|
||||
XCTAssertEqual(buffer.readBytes(length: 16), Array(0..<16))
|
||||
XCTAssertEqual(buffer.readBytes(length: 12), Array(repeating: .max, count: 12))
|
||||
XCTAssertEqual(buffer.readableBytes, 0)
|
||||
}
|
||||
|
||||
func testGetUUIDEmptyBuffer() {
|
||||
let buffer = ByteBuffer()
|
||||
XCTAssertNil(buffer.getUUIDBytes(at: 0))
|
||||
}
|
||||
|
||||
func testGetUUIDAfterSet() {
|
||||
let uuid = UUID()
|
||||
var buffer = ByteBuffer()
|
||||
XCTAssertEqual(buffer.setUUIDBytes(uuid, at: 0), 16)
|
||||
// nil because there are no bytes to read
|
||||
XCTAssertNil(buffer.getUUIDBytes(at: 0))
|
||||
}
|
||||
|
||||
func testWriteUUIDBytesIntoEmptyBuffer() {
|
||||
let uuid = UUID(uuid: (0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf))
|
||||
var buffer = ByteBuffer()
|
||||
|
||||
XCTAssertEqual(buffer.writeUUIDBytes(uuid), 16)
|
||||
XCTAssertEqual(buffer.readableBytesView, [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf])
|
||||
XCTAssertEqual(buffer.readableBytes, 16)
|
||||
XCTAssertEqual(buffer.writerIndex, 16)
|
||||
}
|
||||
|
||||
func testWriteUUIDBytesIntoNonEmptyBuffer() {
|
||||
let uuid = UUID(uuid: (0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf))
|
||||
|
||||
var buffer = ByteBuffer()
|
||||
buffer.writeRepeatingByte(42, count: 10)
|
||||
XCTAssertEqual(buffer.writeUUIDBytes(uuid), 16)
|
||||
XCTAssertEqual(buffer.readableBytes, 26)
|
||||
XCTAssertEqual(buffer.writerIndex, 26)
|
||||
|
||||
XCTAssertEqual(buffer.readableBytesView.dropFirst(10),
|
||||
[0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf])
|
||||
}
|
||||
|
||||
func testReadUUID() {
|
||||
let uuid = UUID(uuid: (0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
|
||||
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf))
|
||||
var buffer = ByteBuffer()
|
||||
XCTAssertEqual(buffer.writeUUIDBytes(uuid), 16)
|
||||
XCTAssertEqual(buffer.readUUIDBytes(), uuid)
|
||||
XCTAssertEqual(buffer.readableBytes, 0)
|
||||
}
|
||||
|
||||
func testReadUUIDNotEnoughBytes() {
|
||||
var buffer = ByteBuffer()
|
||||
XCTAssertNil(buffer.readUUIDBytes())
|
||||
XCTAssertEqual(buffer.readerIndex, 0)
|
||||
|
||||
buffer.writeRepeatingByte(0, count: 8)
|
||||
XCTAssertNil(buffer.readUUIDBytes())
|
||||
XCTAssertEqual(buffer.readerIndex, 0)
|
||||
|
||||
buffer.writeRepeatingByte(0, count: 8)
|
||||
XCTAssertEqual(buffer.readUUIDBytes(),
|
||||
UUID(uuid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)))
|
||||
XCTAssertEqual(buffer.readerIndex, 16)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue