swift-nio/Tests/NIOTests/ByteBufferTest.swift

2518 lines
98 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 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 struct Foundation.Data
import XCTest
@testable import NIO
import NIOFoundationCompat
class ByteBufferTest: XCTestCase {
private let allocator = ByteBufferAllocator()
private var buf: ByteBuffer! = nil
private func setGetInt<T: FixedWidthInteger>(index: Int, v: T) throws {
var buffer = allocator.buffer(capacity: 32)
XCTAssertEqual(MemoryLayout<T>.size, buffer.setInteger(v, at: index))
buffer.moveWriterIndex(to: index + MemoryLayout<T>.size)
buffer.moveReaderIndex(to: index)
XCTAssertEqual(v, buffer.getInteger(at: index))
}
private func writeReadInt<T: FixedWidthInteger>(v: T) throws {
var buffer = allocator.buffer(capacity: 32)
XCTAssertEqual(0, buffer.writerIndex)
XCTAssertEqual(MemoryLayout<T>.size, buffer.writeInteger(v))
XCTAssertEqual(MemoryLayout<T>.size, buffer.writerIndex)
XCTAssertEqual(v, buffer.readInteger())
XCTAssertEqual(0, buffer.readableBytes)
}
override func setUp() {
super.setUp()
self.buf = allocator.buffer(capacity: 1024)
self.buf.writeBytes(Array(repeating: UInt8(0xff), count: 1024))
self.buf = self.buf.getSlice(at: 256, length: 512)
self.buf.clear()
}
override func tearDown() {
self.buf = nil
super.tearDown()
}
func testAllocateAndCount() {
let b = allocator.buffer(capacity: 1024)
XCTAssertEqual(1024, b.capacity)
}
func testEqualsComparesReadBuffersOnly() throws {
// Only cares about the read buffer
self.buf.writeInteger(Int8.max)
self.buf.writeString("oh hi")
let actual: Int8 = buf.readInteger()! // Just getting rid of it from the read buffer
XCTAssertEqual(Int8.max, actual)
var otherBuffer = allocator.buffer(capacity: 32)
otherBuffer.writeString("oh hi")
XCTAssertEqual(otherBuffer, buf)
}
func testSimpleReadTest() throws {
buf.withUnsafeReadableBytes { ptr in
XCTAssertEqual(ptr.count, 0)
}
buf.writeString("Hello world!")
buf.withUnsafeReadableBytes { ptr in
XCTAssertEqual(12, ptr.count)
}
}
func testSimpleWrites() {
var written = buf.writeString("")
XCTAssertEqual(0, written)
XCTAssertEqual(0, buf.readableBytes)
written = buf.writeString("X")
XCTAssertEqual(1, written)
XCTAssertEqual(1, buf.readableBytes)
written = buf.writeString("XXXXX")
XCTAssertEqual(5, written)
XCTAssertEqual(6, buf.readableBytes)
}
func makeSliceToBufferWhichIsDeallocated() -> ByteBuffer {
var buf = self.allocator.buffer(capacity: 16)
let oldCapacity = buf.capacity
buf.writeBytes(0..<16)
XCTAssertEqual(oldCapacity, buf.capacity)
return buf.getSlice(at: 15, length: 1)!
}
func testMakeSureUniquelyOwnedSliceDoesNotGetReallocatedOnWrite() {
var slice = self.makeSliceToBufferWhichIsDeallocated()
XCTAssertEqual(1, slice.capacity)
let oldStorageBegin = slice.withUnsafeReadableBytes { ptr in
return UInt(bitPattern: ptr.baseAddress!)
}
slice.setInteger(1, at: 0, as: UInt8.self)
let newStorageBegin = slice.withUnsafeReadableBytes { ptr in
return UInt(bitPattern: ptr.baseAddress!)
}
XCTAssertEqual(oldStorageBegin, newStorageBegin)
}
func testWriteToUniquelyOwnedSliceWhichTriggersAReallocation() {
var slice = self.makeSliceToBufferWhichIsDeallocated()
XCTAssertEqual(1, slice.capacity)
// this will cause a re-allocation, the whole buffer should be 32 bytes then, the slice having 17 of that.
// this fills 16 bytes so will still fit
slice.writeBytes(Array(16..<32))
XCTAssertEqual(Array(15..<32), slice.readBytes(length: slice.readableBytes)!)
// and this will need another re-allocation
slice.writeBytes(Array(32..<47))
}
func testReadWrite() {
buf.writeString("X")
buf.writeString("Y")
let d = buf.readData(length: 1)
XCTAssertNotNil(d)
if let d = d {
XCTAssertEqual(1, d.count)
XCTAssertEqual("X".utf8.first!, d.first!)
}
}
func testStaticStringReadTests() throws {
var allBytes = 0
for testString in ["", "Hello world!", "👍", "🇬🇧🇺🇸🇪🇺"] as [StaticString] {
buf.withUnsafeReadableBytes { ptr in
XCTAssertEqual(0, ptr.count)
}
XCTAssertEqual(0, buf.readableBytes)
XCTAssertEqual(allBytes, buf.readerIndex)
let bytes = buf.writeStaticString(testString)
XCTAssertEqual(testString.utf8CodeUnitCount, Int(bytes))
allBytes += bytes
XCTAssertEqual(allBytes - bytes, buf.readerIndex)
let expected = testString.withUTF8Buffer { buf in
String(decoding: buf, as: Unicode.UTF8.self)
}
buf.withUnsafeReadableBytes { ptr in
let actual = String(decoding: ptr, as: Unicode.UTF8.self)
XCTAssertEqual(expected, actual)
}
let d = buf.readData(length: testString.utf8CodeUnitCount)
XCTAssertEqual(allBytes, buf.readerIndex)
XCTAssertNotNil(d)
XCTAssertEqual(d?.count, testString.utf8CodeUnitCount)
XCTAssertEqual(expected, String(decoding: d!, as: Unicode.UTF8.self))
}
}
func testString() {
let written = buf.writeString("Hello")
let string = buf.getString(at: 0, length: written)
XCTAssertEqual("Hello", string)
}
func testSliceEasy() {
buf.writeString("0123456789abcdefg")
for i in 0..<16 {
let slice = buf.getSlice(at: i, length: 1)
XCTAssertEqual(1, slice?.capacity)
XCTAssertEqual(buf.getData(at: i, length: 1), slice?.getData(at: 0, length: 1))
}
}
func testWriteStringMovesWriterIndex() throws {
var buf = allocator.buffer(capacity: 1024)
buf.writeString("hello")
XCTAssertEqual(5, buf.writerIndex)
buf.withUnsafeReadableBytes { (ptr: UnsafeRawBufferPointer) -> Void in
let s = String(decoding: ptr, as: Unicode.UTF8.self)
XCTAssertEqual("hello", s)
}
}
func testSetExpandsBufferOnUpperBoundsCheckFailure() {
let initialCapacity = buf.capacity
XCTAssertEqual(5, buf.setString("oh hi", at: buf.capacity))
XCTAssert(initialCapacity < buf.capacity)
}
func testCoWWorks() {
buf.writeString("Hello")
var a = buf!
let b = buf!
a.writeString(" World")
XCTAssertEqual(buf, b)
XCTAssertNotEqual(buf, a)
}
func testWithMutableReadPointerMovesReaderIndexAndReturnsNumBytesConsumed() {
XCTAssertEqual(0, buf.readerIndex)
// We use mutable read pointers when we're consuming the data
// so first we need some data there!
buf.writeString("hello again")
let bytesConsumed = buf.readWithUnsafeReadableBytes { dst in
// Pretend we did some operation which made use of entire 11 byte string
return 11
}
XCTAssertEqual(11, bytesConsumed)
XCTAssertEqual(11, buf.readerIndex)
}
func testWithMutableWritePointerMovesWriterIndexAndReturnsNumBytesWritten() {
XCTAssertEqual(0, buf.writerIndex)
let bytesWritten = buf.writeWithUnsafeMutableBytes(minimumWritableBytes: 5) {
XCTAssertTrue($0.count >= 5)
return 5
}
XCTAssertEqual(5, bytesWritten)
XCTAssertEqual(5, buf.writerIndex)
}
func testWithMutableWritePointerWithMinimumSpecifiedAdjustsCapacity() {
XCTAssertEqual(0, buf.writerIndex)
XCTAssertEqual(1024, buf.capacity)
var bytesWritten = buf.writeWithUnsafeMutableBytes(minimumWritableBytes: 256) {
XCTAssertTrue($0.count >= 256)
return 256
}
XCTAssertEqual(256, bytesWritten)
XCTAssertEqual(256, buf.writerIndex)
XCTAssertEqual(1024, buf.capacity)
bytesWritten += buf.writeWithUnsafeMutableBytes(minimumWritableBytes: 1024) {
XCTAssertTrue($0.count >= 1024)
return 1024
}
let expectedBytesWritten = 256 + 1024
XCTAssertEqual(expectedBytesWritten, bytesWritten)
XCTAssertEqual(expectedBytesWritten, buf.writerIndex)
XCTAssertTrue(buf.capacity >= expectedBytesWritten)
}
func testWithMutableWritePointerWithMinimumSpecifiedWhileAtMaxCapacity() {
XCTAssertEqual(0, buf.writerIndex)
XCTAssertEqual(1024, buf.capacity)
var bytesWritten = buf.writeWithUnsafeMutableBytes(minimumWritableBytes: 512) {
XCTAssertTrue($0.count >= 512)
return 512
}
XCTAssertEqual(512, bytesWritten)
XCTAssertEqual(512, buf.writerIndex)
XCTAssertEqual(1024, buf.capacity)
bytesWritten += buf.writeWithUnsafeMutableBytes(minimumWritableBytes: 1) {
XCTAssertTrue($0.count >= 1)
return 1
}
let expectedBytesWritten = 512 + 1
XCTAssertEqual(expectedBytesWritten, bytesWritten)
XCTAssertEqual(expectedBytesWritten, buf.writerIndex)
XCTAssertTrue(buf.capacity >= expectedBytesWritten)
}
func testSetGetInt8() throws {
try setGetInt(index: 0, v: Int8.max)
}
func testSetGetInt16() throws {
try setGetInt(index: 1, v: Int16.max)
}
func testSetGetInt32() throws {
try setGetInt(index: 2, v: Int32.max)
}
func testSetGetInt64() throws {
try setGetInt(index: 3, v: Int64.max)
}
func testSetGetUInt8() throws {
try setGetInt(index: 4, v: UInt8.max)
}
func testSetGetUInt16() throws {
try setGetInt(index: 5, v: UInt16.max)
}
func testSetGetUInt32() throws {
try setGetInt(index: 6, v: UInt32.max)
}
func testSetGetUInt64() throws {
try setGetInt(index: 7, v: UInt64.max)
}
func testWriteReadInt8() throws {
try writeReadInt(v: Int8.max)
}
func testWriteReadInt16() throws {
try writeReadInt(v: Int16.max)
}
func testWriteReadInt32() throws {
try writeReadInt(v: Int32.max)
}
func testWriteReadInt64() throws {
try writeReadInt(v: Int32.max)
}
func testWriteReadUInt8() throws {
try writeReadInt(v: UInt8.max)
}
func testWriteReadUInt16() throws {
try writeReadInt(v: UInt16.max)
}
func testWriteReadUInt32() throws {
try writeReadInt(v: UInt32.max)
}
func testWriteReadUInt64() throws {
try writeReadInt(v: UInt32.max)
}
func testSlice() throws {
var buffer = allocator.buffer(capacity: 32)
XCTAssertEqual(MemoryLayout<UInt64>.size, buffer.writeInteger(UInt64.max))
var slice = buffer.slice()
XCTAssertEqual(MemoryLayout<UInt64>.size, slice.readableBytes)
XCTAssertEqual(UInt64.max, slice.readInteger())
XCTAssertEqual(MemoryLayout<UInt64>.size, buffer.readableBytes)
XCTAssertEqual(UInt64.max, buffer.readInteger())
}
func testSliceWithParams() throws {
var buffer = allocator.buffer(capacity: 32)
XCTAssertEqual(MemoryLayout<UInt64>.size, buffer.writeInteger(UInt64.max))
var slice = buffer.getSlice(at: 0, length: MemoryLayout<UInt64>.size)!
XCTAssertEqual(MemoryLayout<UInt64>.size, slice.readableBytes)
XCTAssertEqual(UInt64.max, slice.readInteger())
XCTAssertEqual(MemoryLayout<UInt64>.size, buffer.readableBytes)
XCTAssertEqual(UInt64.max, buffer.readInteger())
}
func testReadSlice() throws {
var buffer = allocator.buffer(capacity: 32)
XCTAssertEqual(MemoryLayout<UInt64>.size, buffer.writeInteger(UInt64.max))
var slice = buffer.readSlice(length: buffer.readableBytes)!
XCTAssertEqual(MemoryLayout<UInt64>.size, slice.readableBytes)
XCTAssertEqual(UInt64.max, slice.readInteger())
XCTAssertEqual(0, buffer.readableBytes)
let value: UInt64? = buffer.readInteger()
XCTAssertTrue(value == nil)
}
func testSliceNoCopy() throws {
var buffer = allocator.buffer(capacity: 32)
XCTAssertEqual(MemoryLayout<UInt64>.size, buffer.writeInteger(UInt64.max))
let slice = buffer.readSlice(length: buffer.readableBytes)!
buffer.withVeryUnsafeBytes { ptr1 in
slice.withVeryUnsafeBytes { ptr2 in
XCTAssertEqual(ptr1.baseAddress, ptr2.baseAddress)
}
}
}
func testSetGetData() throws {
var buffer = allocator.buffer(capacity: 32)
let data = Data([1, 2, 3])
XCTAssertEqual(3, buffer.setBytes(data, at: 0))
XCTAssertEqual(0, buffer.readableBytes)
buffer.moveReaderIndex(to: 0)
buffer.moveWriterIndex(to: 3)
XCTAssertEqual(data, buffer.getData(at: 0, length: 3))
}
func testWriteReadData() throws {
var buffer = allocator.buffer(capacity: 32)
let data = Data([1, 2, 3])
XCTAssertEqual(3, buffer.writeBytes(data))
XCTAssertEqual(3, buffer.readableBytes)
XCTAssertEqual(data, buffer.readData(length: 3))
}
func testDiscardReadBytes() throws {
var buffer = allocator.buffer(capacity: 32)
buffer.writeInteger(1, as: UInt8.self)
buffer.writeInteger(UInt8(2))
buffer.writeInteger(3 as UInt8)
buffer.writeInteger(4, as: UInt8.self)
XCTAssertEqual(4, buffer.readableBytes)
buffer.moveReaderIndex(forwardBy: 2)
XCTAssertEqual(2, buffer.readableBytes)
XCTAssertEqual(2, buffer.readerIndex)
XCTAssertEqual(4, buffer.writerIndex)
XCTAssertTrue(buffer.discardReadBytes())
XCTAssertEqual(2, buffer.readableBytes)
XCTAssertEqual(0, buffer.readerIndex)
XCTAssertEqual(2, buffer.writerIndex)
XCTAssertEqual(UInt8(3), buffer.readInteger())
XCTAssertEqual(UInt8(4), buffer.readInteger())
XCTAssertEqual(0, buffer.readableBytes)
XCTAssertTrue(buffer.discardReadBytes())
XCTAssertFalse(buffer.discardReadBytes())
}
func testDiscardReadBytesCoW() throws {
var buffer = allocator.buffer(capacity: 32)
let bytesWritten = buffer.writeBytes("0123456789abcdef0123456789ABCDEF".data(using: .utf8)!)
XCTAssertEqual(32, bytesWritten)
func testAssumptionOriginalBuffer(_ buf: inout ByteBuffer) {
XCTAssertEqual(32, buf.capacity)
XCTAssertEqual(0, buf.readerIndex)
XCTAssertEqual(32, buf.writerIndex)
XCTAssertEqual("0123456789abcdef0123456789ABCDEF".data(using: .utf8)!, buf.getData(at: 0, length: 32)!)
}
testAssumptionOriginalBuffer(&buffer)
var buffer10Missing = buffer
let first10Bytes = buffer10Missing.readData(length: 10) /* make the first 10 bytes disappear */
let otherBuffer10Missing = buffer10Missing
XCTAssertEqual("0123456789".data(using: .utf8)!, first10Bytes)
testAssumptionOriginalBuffer(&buffer)
XCTAssertEqual(10, buffer10Missing.readerIndex)
XCTAssertEqual(32, buffer10Missing.writerIndex)
let nextBytes1 = buffer10Missing.getData(at: 10, length: 22)
XCTAssertEqual("abcdef0123456789ABCDEF".data(using: .utf8)!, nextBytes1)
buffer10Missing.discardReadBytes()
XCTAssertEqual(0, buffer10Missing.readerIndex)
XCTAssertEqual(22, buffer10Missing.writerIndex)
testAssumptionOriginalBuffer(&buffer)
XCTAssertEqual(10, otherBuffer10Missing.readerIndex)
XCTAssertEqual(32, otherBuffer10Missing.writerIndex)
let nextBytes2 = buffer10Missing.getData(at: 0, length: 22)
XCTAssertEqual("abcdef0123456789ABCDEF".data(using: .utf8)!, nextBytes2)
let nextBytes3 = otherBuffer10Missing.getData(at: 10, length: 22)
XCTAssertEqual("abcdef0123456789ABCDEF".data(using: .utf8)!, nextBytes3)
testAssumptionOriginalBuffer(&buffer)
}
func testDiscardReadBytesSlice() throws {
var buffer = allocator.buffer(capacity: 32)
buffer.writeInteger(UInt8(1))
buffer.writeInteger(UInt8(2))
buffer.writeInteger(UInt8(3))
buffer.writeInteger(UInt8(4))
XCTAssertEqual(4, buffer.readableBytes)
var slice = buffer.getSlice(at: 1, length: 3)!
XCTAssertEqual(3, slice.readableBytes)
XCTAssertEqual(0, slice.readerIndex)
slice.moveReaderIndex(forwardBy: 1)
XCTAssertEqual(2, slice.readableBytes)
XCTAssertEqual(1, slice.readerIndex)
XCTAssertEqual(3, slice.writerIndex)
XCTAssertTrue(slice.discardReadBytes())
XCTAssertEqual(2, slice.readableBytes)
XCTAssertEqual(0, slice.readerIndex)
XCTAssertEqual(2, slice.writerIndex)
XCTAssertEqual(UInt8(3), slice.readInteger())
XCTAssertEqual(UInt8(4), slice.readInteger())
XCTAssertEqual(0,slice.readableBytes)
XCTAssertTrue(slice.discardReadBytes())
XCTAssertFalse(slice.discardReadBytes())
}
func testWithDataSlices() throws {
let testStringPrefix = "0123456789"
let testStringSuffix = "abcdef"
let testString = "\(testStringPrefix)\(testStringSuffix)"
var buffer = allocator.buffer(capacity: testString.utf8.count)
buffer.writeString(testStringPrefix)
buffer.writeString(testStringSuffix)
XCTAssertEqual(testString.utf8.count, buffer.capacity)
func runTestForRemaining(string: String, buffer: ByteBuffer) {
buffer.withUnsafeReadableBytes { ptr in
XCTAssertEqual(string.utf8.count, ptr.count)
for (idx, expected) in zip(0..<string.utf8.count, string.utf8) {
let actual = ptr[idx]
XCTAssertEqual(expected, actual, "character at index \(idx) is \(actual) but should be \(expected)")
}
}
buffer.withUnsafeReadableBytes { data -> Void in
XCTAssertEqual(string.utf8.count, data.count)
for (idx, expected) in zip(data.startIndex..<data.startIndex+string.utf8.count, string.utf8) {
XCTAssertEqual(expected, data[idx])
}
}
buffer.withUnsafeReadableBytes { slice in
XCTAssertEqual(string, String(decoding: slice, as: Unicode.UTF8.self))
}
}
runTestForRemaining(string: testString, buffer: buffer)
let prefixBuffer = buffer.readSlice(length: testStringPrefix.utf8.count)
XCTAssertNotNil(prefixBuffer)
if let prefixBuffer = prefixBuffer {
runTestForRemaining(string: testStringPrefix, buffer: prefixBuffer)
}
runTestForRemaining(string: testStringSuffix, buffer: buffer)
}
func testEndianness() throws {
let value: UInt32 = 0x12345678
buf.writeInteger(value)
let actualRead: UInt32 = buf.readInteger()!
XCTAssertEqual(value, actualRead)
buf.writeInteger(value, endianness: .big)
buf.writeInteger(value, endianness: .little)
buf.writeInteger(value)
let actual = buf.getData(at: 4, length: 12)!
let expected = Data([0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0x12, 0x12, 0x34, 0x56, 0x78])
XCTAssertEqual(expected, actual)
let actualA: UInt32 = buf.readInteger(endianness: .big)!
let actualB: UInt32 = buf.readInteger(endianness: .little)!
let actualC: UInt32 = buf.readInteger()!
XCTAssertEqual(value, actualA)
XCTAssertEqual(value, actualB)
XCTAssertEqual(value, actualC)
}
func testExpansion() throws {
var buf = allocator.buffer(capacity: 16)
XCTAssertEqual(16, buf.capacity)
buf.writeBytes("0123456789abcdef".data(using: .utf8)!)
XCTAssertEqual(16, buf.capacity)
XCTAssertEqual(16, buf.writerIndex)
XCTAssertEqual(0, buf.readerIndex)
buf.writeBytes("X".data(using: .utf8)!)
XCTAssertGreaterThan(buf.capacity, 16)
XCTAssertEqual(17, buf.writerIndex)
XCTAssertEqual(0, buf.readerIndex)
buf.withUnsafeReadableBytes { ptr in
let bPtr = UnsafeBufferPointer(start: ptr.baseAddress!.bindMemory(to: UInt8.self, capacity: ptr.count),
count: ptr.count)
XCTAssertEqual("0123456789abcdefX".data(using: .utf8)!, Data(buffer: bPtr))
}
}
func testExpansion2() throws {
var buf = allocator.buffer(capacity: 2)
XCTAssertEqual(2, buf.capacity)
buf.writeBytes("0123456789abcdef".data(using: .utf8)!)
XCTAssertEqual(16, buf.capacity)
XCTAssertEqual(16, buf.writerIndex)
buf.withUnsafeReadableBytes { ptr in
let bPtr = UnsafeBufferPointer(start: ptr.baseAddress!.bindMemory(to: UInt8.self, capacity: ptr.count),
count: ptr.count)
XCTAssertEqual("0123456789abcdef".data(using: .utf8)!, Data(buffer: bPtr))
}
}
func testNotEnoughBytesToReadForIntegers() throws {
let byteCount = 15
func initBuffer() {
let written = buf.writeBytes(Data(Array(repeating: 0, count: byteCount)))
XCTAssertEqual(byteCount, written)
}
func tryWith<T: FixedWidthInteger>(_ type: T.Type) {
initBuffer()
let tooMany = (byteCount + 1)/MemoryLayout<T>.size
for _ in 1..<tooMany {
/* read just enough ones that we should be able to read in one go */
XCTAssertNotNil(buf.getInteger(at: buf.readerIndex) as T?)
let actual: T? = buf.readInteger()
XCTAssertNotNil(actual)
XCTAssertEqual(0, actual)
}
/* now see that trying to read one more fails */
let actual: T? = buf.readInteger()
XCTAssertNil(actual)
buf.clear()
}
tryWith(UInt16.self)
tryWith(UInt32.self)
tryWith(UInt64.self)
}
func testNotEnoughBytesToReadForData() throws {
let cap = buf.capacity
let expected = Data(Array(repeating: 0, count: cap))
let written = buf.writeBytes(expected)
XCTAssertEqual(cap, written)
XCTAssertEqual(cap, buf.capacity)
XCTAssertNil(buf.readData(length: cap+1)) /* too many */
XCTAssertEqual(expected, buf.readData(length: cap)) /* to make sure it can work */
}
func testSlicesThatAreOutOfBands() throws {
self.buf.moveReaderIndex(to: 0)
self.buf.moveWriterIndex(to: self.buf.capacity)
let goodSlice = self.buf.getSlice(at: 0, length: self.buf.capacity)
XCTAssertNotNil(goodSlice)
let badSlice1 = self.buf.getSlice(at: 0, length: self.buf.capacity+1)
XCTAssertNil(badSlice1)
let badSlice2 = self.buf.getSlice(at: self.buf.capacity-1, length: 2)
XCTAssertNil(badSlice2)
}
func testMutableBytesCoW() throws {
let cap = buf.capacity
var otherBuf = buf
XCTAssertEqual(otherBuf, buf)
otherBuf?.writeWithUnsafeMutableBytes(minimumWritableBytes: 0) { ptr in
XCTAssertEqual(cap, ptr.count)
let intPtr = ptr.baseAddress!.bindMemory(to: UInt8.self, capacity: ptr.count)
for i in 0..<ptr.count {
intPtr[i] = UInt8(truncatingIfNeeded: i)
}
return ptr.count
}
XCTAssertEqual(cap, otherBuf?.capacity)
XCTAssertNotEqual(buf, otherBuf)
otherBuf?.withUnsafeReadableBytes { ptr in
XCTAssertEqual(cap, ptr.count)
for i in 0..<cap {
XCTAssertEqual(ptr[i], UInt8(truncatingIfNeeded: i))
}
}
}
func testWritableBytesTriggersCoW() throws {
let cap = buf.capacity
var otherBuf = buf
XCTAssertEqual(otherBuf, buf)
// Write to both buffers.
let firstResult = buf!.withUnsafeMutableWritableBytes { (ptr: UnsafeMutableRawBufferPointer) -> Bool in
XCTAssertEqual(cap, ptr.count)
memset(ptr.baseAddress!, 0, ptr.count)
return false
}
let secondResult = otherBuf!.withUnsafeMutableWritableBytes { (ptr: UnsafeMutableRawBufferPointer) -> Bool in
XCTAssertEqual(cap, ptr.count)
let intPtr = ptr.baseAddress!.bindMemory(to: UInt8.self, capacity: ptr.count)
for i in 0..<ptr.count {
intPtr[i] = UInt8(truncatingIfNeeded: i)
}
return true
}
XCTAssertFalse(firstResult)
XCTAssertTrue(secondResult)
XCTAssertEqual(cap, otherBuf!.capacity)
XCTAssertEqual(buf!.readableBytes, 0)
XCTAssertEqual(otherBuf!.readableBytes, 0)
// Move both writer indices forwards by the amount of data we wrote.
buf!.moveWriterIndex(forwardBy: cap)
otherBuf!.moveWriterIndex(forwardBy: cap)
// These should now be unequal. Check their bytes to be sure.
XCTAssertNotEqual(buf, otherBuf)
buf!.withUnsafeReadableBytes { ptr in
XCTAssertEqual(cap, ptr.count)
for i in 0..<cap {
XCTAssertEqual(ptr[i], 0)
}
}
otherBuf!.withUnsafeReadableBytes { ptr in
XCTAssertEqual(cap, ptr.count)
for i in 0..<cap {
XCTAssertEqual(ptr[i], UInt8(truncatingIfNeeded: i))
}
}
}
func testBufferWithZeroBytes() throws {
var buf = allocator.buffer(capacity: 0)
XCTAssertEqual(0, buf.capacity)
var otherBuf = buf
otherBuf.setBytes(Data(), at: 0)
buf.setBytes(Data(), at: 0)
XCTAssertEqual(0, buf.capacity)
XCTAssertEqual(0, otherBuf.capacity)
XCTAssertNil(otherBuf.readData(length: 1))
XCTAssertNil(buf.readData(length: 1))
}
func testPastEnd() throws {
let buf = allocator.buffer(capacity: 4)
XCTAssertEqual(4, buf.capacity)
XCTAssertNil(buf.getInteger(at: 0) as UInt64?)
XCTAssertNil(buf.getData(at: 0, length: 5))
}
func testReadDataNotEnoughAvailable() throws {
/* write some bytes */
self.buf.writeBytes(Data([0, 1, 2, 3]))
/* make more available in the buffer that should not be readable */
self.buf.setBytes(Data([4, 5, 6, 7]), at: 4)
let actualNil = buf.readData(length: 5)
XCTAssertNil(actualNil)
self.buf.moveReaderIndex(to: 0)
self.buf.moveWriterIndex(to: 5)
let actualGoodDirect = buf.getData(at: 0, length: 5)
XCTAssertEqual(Data([0, 1, 2, 3, 4]), actualGoodDirect)
let actualGood = self.buf.readData(length: 4)
XCTAssertEqual(Data([0, 1, 2, 3]), actualGood)
}
func testReadSliceNotEnoughAvailable() throws {
/* write some bytes */
self.buf.writeBytes(Data([0, 1, 2, 3]))
/* make more available in the buffer that should not be readable */
self.buf.setBytes(Data([4, 5, 6, 7]), at: 4)
let actualNil = self.buf.readSlice(length: 5)
XCTAssertNil(actualNil)
self.buf.moveWriterIndex(forwardBy: 1)
let actualGoodDirect = self.buf.getSlice(at: 0, length: 5)
XCTAssertEqual(Data([0, 1, 2, 3, 4]), actualGoodDirect?.getData(at: 0, length: 5))
var actualGood = self.buf.readSlice(length: 4)
XCTAssertEqual(Data([0, 1, 2, 3]), actualGood?.readData(length: 4))
}
func testSetBuffer() throws {
var src = self.allocator.buffer(capacity: 4)
src.writeBytes(Data([0, 1, 2, 3]))
self.buf.setBuffer(src, at: 1)
/* Should bit increase the writerIndex of the src buffer */
XCTAssertEqual(4, src.readableBytes)
XCTAssertEqual(0, self.buf.readableBytes)
self.buf.moveWriterIndex(to: 5)
self.buf.moveReaderIndex(to: 1)
XCTAssertEqual(Data([0, 1, 2, 3]), self.buf.getData(at: 1, length: 4))
}
func testWriteBuffer() throws {
var src = allocator.buffer(capacity: 4)
src.writeBytes(Data([0, 1, 2, 3]))
buf.writeBuffer(&src)
/* Should increase the writerIndex of the src buffer */
XCTAssertEqual(0, src.readableBytes)
XCTAssertEqual(4, buf.readableBytes)
XCTAssertEqual(Data([0, 1, 2, 3]), buf.readData(length: 4))
}
func testMisalignedIntegerRead() throws {
let value = UInt64(7)
buf.writeBytes(Data([1]))
buf.writeInteger(value)
let actual = buf.readData(length: 1)
XCTAssertEqual(Data([1]), actual)
buf.withUnsafeReadableBytes { ptr in
/* make sure pointer is actually misaligned for an integer */
let pointerBitPattern = UInt(bitPattern: ptr.baseAddress!)
let lastBit = pointerBitPattern & 0x1
XCTAssertEqual(1, lastBit) /* having a 1 as the last bit makes that pointer clearly misaligned for UInt64 */
}
XCTAssertEqual(value, buf.readInteger())
}
func testSetAndWriteBytes() throws {
let str = "hello world!"
let hwData = str.data(using: .utf8)!
/* write once, ... */
buf.writeString(str)
var written1: Int = -1
var written2: Int = -1
hwData.withUnsafeBytes { ptr in
/* ... write a second time and ...*/
written1 = buf.setBytes(ptr, at: buf.writerIndex)
buf.moveWriterIndex(forwardBy: written1)
/* ... a lucky third time! */
written2 = buf.writeBytes(ptr)
}
XCTAssertEqual(written1, written2)
XCTAssertEqual(str.utf8.count, written1)
XCTAssertEqual(3 * str.utf8.count, buf.readableBytes)
let actualData = buf.readData(length: 3 * str.utf8.count)!
let actualString = String(decoding: actualData, as: Unicode.UTF8.self)
XCTAssertEqual(Array(repeating: str, count: 3).joined(), actualString)
}
func testCopyBytesWithNegativeLength() {
self.buf.writeBytes([0x0, 0x1])
XCTAssertThrowsError(try self.buf.copyBytes(at: self.buf.readerIndex, to: self.buf.readerIndex + 1, length: -1)) {
XCTAssertEqual($0 as? ByteBuffer.CopyBytesError, .negativeLength)
}
}
func testCopyBytesNonReadable() {
let oldReaderIndex = self.buf.readerIndex
self.buf.writeBytes([0x0, 0x1, 0x2])
// Partially consume the bytes.
self.buf.moveReaderIndex(forwardBy: 2)
// Copy two read bytes.
XCTAssertThrowsError(try self.buf.copyBytes(at: oldReaderIndex, to: self.buf.writerIndex, length: 2)) {
XCTAssertEqual($0 as? ByteBuffer.CopyBytesError, .unreadableSourceBytes)
}
// Copy one read byte and one readable byte.
XCTAssertThrowsError(try self.buf.copyBytes(at: oldReaderIndex + 1, to: self.buf.writerIndex, length: 2)) {
XCTAssertEqual($0 as? ByteBuffer.CopyBytesError, .unreadableSourceBytes)
}
// Copy one readable byte and one uninitialized byte.
XCTAssertThrowsError(try self.buf.copyBytes(at: oldReaderIndex + 3, to: self.buf.writerIndex, length: 2)) {
XCTAssertEqual($0 as? ByteBuffer.CopyBytesError, .unreadableSourceBytes)
}
// Copy two uninitialized bytes.
XCTAssertThrowsError(try self.buf.copyBytes(at: self.buf.writerIndex, to: oldReaderIndex, length: 2)) {
XCTAssertEqual($0 as? ByteBuffer.CopyBytesError, .unreadableSourceBytes)
}
}
func testCopyBytes() throws {
self.buf.writeBytes([0, 1, 2, 3])
XCTAssertNoThrow(try self.buf.copyBytes(at: self.buf.readerIndex, to: self.buf.readerIndex + 2, length: 2))
XCTAssertEqual(self.buf.readableBytes, 4)
XCTAssertEqual(self.buf.readBytes(length: self.buf.readableBytes), [0, 1, 0, 1])
}
func testCopyZeroBytesOutOfBoundsIsOk() throws {
XCTAssertEqual(try self.buf.copyBytes(at: self.buf.writerIndex, to: self.buf.writerIndex + 42, length: 0), 0)
}
func testCopyBytesBeyondWriterIndex() throws {
self.buf.writeBytes([0, 1, 2, 3])
// Write beyond the writerIndex
XCTAssertNoThrow(try self.buf.copyBytes(at: self.buf.readerIndex, to: self.buf.readerIndex + 4, length: 2))
XCTAssertEqual(self.buf.readableBytes, 4)
XCTAssertEqual(self.buf.readBytes(length: 2), [0, 1])
XCTAssertNotNil(self.buf.readBytes(length: 2)) // could be anything!
self.buf.moveWriterIndex(forwardBy: 2) // We know these have been written.
XCTAssertEqual(self.buf.readBytes(length: 2), [0, 1])
}
func testCopyBytesOverSelf() throws {
self.buf.writeBytes([0, 1, 2, 3])
XCTAssertNoThrow(try self.buf.copyBytes(at: self.buf.readerIndex, to: self.buf.readerIndex + 1, length: 3))
XCTAssertEqual(self.buf.readableBytes, 4)
XCTAssertEqual(self.buf.readBytes(length: self.buf.readableBytes), [0, 0, 1, 2])
self.buf.writeBytes([0, 1, 2, 3])
XCTAssertNoThrow(try self.buf.copyBytes(at: self.buf.readerIndex + 1, to: self.buf.readerIndex, length: 3))
XCTAssertEqual(self.buf.readableBytes, 4)
XCTAssertEqual(self.buf.readBytes(length: self.buf.readableBytes), [1, 2, 3, 3])
}
func testCopyBytesCoWs() throws {
let bytes: [UInt8] = (0..<self.buf.writableBytes).map { UInt8($0 % Int(UInt8.max)) }
self.buf.writeBytes(bytes)
var otherBuf = self.buf!
XCTAssertEqual(self.buf.writableBytes, 0)
XCTAssertNoThrow(try self.buf.copyBytes(at: self.buf.readerIndex, to: self.buf.readerIndex + 2, length: 1))
XCTAssertNotEqual(self.buf.readBytes(length: self.buf.readableBytes),
otherBuf.readBytes(length: otherBuf.readableBytes))
}
func testWriteABunchOfCollections() throws {
let overallData = "0123456789abcdef".data(using: .utf8)!
buf.writeBytes("0123".utf8)
"4567".withCString { ptr in
ptr.withMemoryRebound(to: UInt8.self, capacity: 4) { ptr in
_ = buf.writeBytes(UnsafeBufferPointer<UInt8>(start: ptr, count: 4))
}
}
buf.writeBytes(Array("89ab".utf8))
buf.writeBytes("cdef".data(using: .utf8)!)
let actual = buf.getData(at: 0, length: buf.readableBytes)
XCTAssertEqual(overallData, actual)
}
func testSetABunchOfCollections() throws {
let overallData = "0123456789abcdef".data(using: .utf8)!
self.buf.setBytes("0123".utf8, at: 0)
_ = "4567".withCString { ptr in
ptr.withMemoryRebound(to: UInt8.self, capacity: 4) { ptr in
self.buf.setBytes(UnsafeBufferPointer<UInt8>(start: ptr, count: 4), at: 4)
}
}
self.buf.setBytes(Array("89ab".utf8), at: 8)
self.buf.setBytes("cdef".data(using: .utf8)!, at: 12)
self.buf.moveReaderIndex(to: 0)
self.buf.moveWriterIndex(to: 16)
let actual = self.buf.getData(at: 0, length: 16)
XCTAssertEqual(overallData, actual)
}
func testTryStringTooLong() throws {
let capacity = self.buf.capacity
for i in 0..<self.buf.capacity {
self.buf.setString("x", at: i)
}
XCTAssertEqual(capacity, self.buf.capacity,
"buffer capacity needlessly changed from \(capacity) to \(self.buf.capacity)")
XCTAssertNil(self.buf.getString(at: 0, length: capacity+1))
}
func testSetGetBytesAllFine() throws {
self.buf.moveReaderIndex(to: 0)
self.buf.setBytes([1, 2, 3, 4], at: 0)
self.buf.moveWriterIndex(to: 4)
XCTAssertEqual([1, 2, 3, 4], self.buf.getBytes(at: 0, length: 4) ?? [])
let capacity = self.buf.capacity
for i in 0..<self.buf.capacity {
self.buf.setBytes([0xFF], at: i)
}
self.buf.moveWriterIndex(to: self.buf.capacity)
XCTAssertEqual(capacity, buf.capacity, "buffer capacity needlessly changed from \(capacity) to \(buf.capacity)")
XCTAssertEqual(Array(repeating: 0xFF, count: capacity), self.buf.getBytes(at: 0, length: capacity))
}
func testGetBytesTooLong() throws {
XCTAssertNil(buf.getBytes(at: 0, length: buf.capacity+1))
XCTAssertNil(buf.getBytes(at: buf.capacity, length: 1))
}
func testReadWriteBytesOkay() throws {
buf.reserveCapacity(24)
buf.clear()
let capacity = buf.capacity
for i in 0..<capacity {
let expected = Array(repeating: UInt8(i % 255), count: i)
buf.writeBytes(expected)
let actual = buf.readBytes(length: i)!
XCTAssertEqual(expected, actual)
XCTAssertEqual(capacity, buf.capacity, "buffer capacity needlessly changed from \(capacity) to \(buf.capacity)")
buf.clear()
}
}
func testReadTooLong() throws {
XCTAssertNotNil(buf.readBytes(length: buf.readableBytes))
XCTAssertNil(buf.readBytes(length: buf.readableBytes+1))
}
func testReadWithUnsafeReadableBytesVariantsNothingToRead() throws {
buf.reserveCapacity(1024)
buf.clear()
XCTAssertEqual(0, buf.readerIndex)
XCTAssertEqual(0, buf.writerIndex)
var rInt = buf.readWithUnsafeReadableBytes { ptr in
XCTAssertEqual(0, ptr.count)
return 0
}
XCTAssertEqual(0, rInt)
rInt = buf.readWithUnsafeMutableReadableBytes { ptr in
XCTAssertEqual(0, ptr.count)
return 0
}
XCTAssertEqual(0, rInt)
var rString = buf.readWithUnsafeReadableBytes { (ptr: UnsafeRawBufferPointer) -> (Int, String) in
XCTAssertEqual(0, ptr.count)
return (0, "blah")
}
XCTAssert(rString == "blah")
rString = buf.readWithUnsafeMutableReadableBytes { (ptr: UnsafeMutableRawBufferPointer) -> (Int, String) in
XCTAssertEqual(0, ptr.count)
return (0, "blah")
}
XCTAssert(rString == "blah")
}
func testReadWithUnsafeReadableBytesVariantsSomethingToRead() throws {
var buf = ByteBufferAllocator().buffer(capacity: 1)
buf.clear()
buf.writeBytes([1, 2, 3, 4, 5, 6, 7, 8])
XCTAssertEqual(0, buf.readerIndex)
XCTAssertEqual(8, buf.writerIndex)
var rInt = buf.readWithUnsafeReadableBytes { ptr in
XCTAssertEqual(8, ptr.count)
return 0
}
XCTAssertEqual(0, rInt)
rInt = buf.readWithUnsafeMutableReadableBytes { ptr in
XCTAssertEqual(8, ptr.count)
return 1
}
XCTAssertEqual(1, rInt)
var rString = buf.readWithUnsafeReadableBytes { (ptr: UnsafeRawBufferPointer) -> (Int, String) in
XCTAssertEqual(7, ptr.count)
return (2, "blah")
}
XCTAssert(rString == "blah")
rString = buf.readWithUnsafeMutableReadableBytes { (ptr: UnsafeMutableRawBufferPointer) -> (Int, String) in
XCTAssertEqual(5, ptr.count)
return (3, "blah")
}
XCTAssert(rString == "blah")
XCTAssertEqual(6, buf.readerIndex)
XCTAssertEqual(8, buf.writerIndex)
}
func testSomePotentialIntegerUnderOrOverflows() throws {
buf.reserveCapacity(1024)
buf.writeStaticString("hello world, just some trap bytes here")
func testIndexAndLengthFunc<T>(_ body: (Int, Int) -> T?, file: StaticString = #file, line: UInt = #line) {
XCTAssertNil(body(Int.max, 1), file: file, line: line)
XCTAssertNil(body(Int.max - 1, 2), file: file, line: line)
XCTAssertNil(body(1, Int.max), file: file, line: line)
XCTAssertNil(body(2, Int.max - 1), file: file, line: line)
XCTAssertNil(body(Int.max, Int.max), file: file, line: line)
XCTAssertNil(body(Int.min, Int.min), file: file, line: line)
XCTAssertNil(body(Int.max, Int.min), file: file, line: line)
XCTAssertNil(body(Int.min, Int.max), file: file, line: line)
}
func testIndexOrLengthFunc<T>(_ body: (Int) -> T?, file: StaticString = #file, line: UInt = #line) {
XCTAssertNil(body(Int.max))
XCTAssertNil(body(Int.max - 1))
XCTAssertNil(body(Int.min))
}
testIndexOrLengthFunc({ buf.readBytes(length: $0) })
testIndexOrLengthFunc({ buf.readData(length: $0) })
testIndexOrLengthFunc({ buf.readSlice(length: $0) })
testIndexOrLengthFunc({ buf.readString(length: $0) })
testIndexOrLengthFunc({ buf.readDispatchData(length: $0) })
testIndexOrLengthFunc({ buf.getInteger(at: $0, as: UInt8.self) })
testIndexOrLengthFunc({ buf.getInteger(at: $0, as: UInt16.self) })
testIndexOrLengthFunc({ buf.getInteger(at: $0, as: UInt32.self) })
testIndexOrLengthFunc({ buf.getInteger(at: $0, as: UInt64.self) })
testIndexAndLengthFunc(buf.getBytes)
testIndexAndLengthFunc(buf.getData)
testIndexAndLengthFunc(buf.getSlice)
testIndexAndLengthFunc(buf.getString)
testIndexAndLengthFunc(buf.getDispatchData)
testIndexAndLengthFunc(buf.viewBytes(at:length:))
}
func testWriteForContiguousCollections() throws {
buf.clear()
var written = buf.writeBytes([1, 2, 3, 4])
XCTAssertEqual(4, written)
// UnsafeRawBufferPointer
written += [5 as UInt8, 6, 7, 8].withUnsafeBytes { ptr in
buf.writeBytes(ptr)
}
XCTAssertEqual(8, written)
// UnsafeBufferPointer<UInt8>
written += [9 as UInt8, 10, 11, 12].withUnsafeBufferPointer { ptr in
buf.writeBytes(ptr)
}
XCTAssertEqual(12, written)
// ContiguousArray
written += buf.writeBytes(ContiguousArray<UInt8>([13, 14, 15, 16]))
XCTAssertEqual(16, written)
// Data
written += buf.writeBytes("EFGH".data(using: .utf8)!)
XCTAssertEqual(20, written)
var more = Array("IJKL".utf8)
// UnsafeMutableRawBufferPointer
written += more.withUnsafeMutableBytes { ptr in
buf.writeBytes(ptr)
}
more = Array("MNOP".utf8)
// UnsafeMutableBufferPointer<UInt8>
written += more.withUnsafeMutableBufferPointer { ptr in
buf.writeBytes(ptr)
}
more = Array("mnopQRSTuvwx".utf8)
// ArraySlice
written += buf.writeBytes(more.dropFirst(4).dropLast(4))
let moreCA = ContiguousArray("qrstUVWXyz01".utf8)
// ContiguousArray's slice (== ArraySlice)
written += buf.writeBytes(moreCA.dropFirst(4).dropLast(4))
// Slice<UnsafeRawBufferPointer>
written += Array("uvwxYZ01abcd".utf8).withUnsafeBytes { ptr in
buf.writeBytes(ptr.dropFirst(4).dropLast(4) as UnsafeRawBufferPointer.SubSequence)
}
more = Array("2345".utf8)
written += more.withUnsafeMutableBytes { ptr in
buf.writeBytes(ptr.dropFirst(0)) + buf.writeBytes(ptr.dropFirst(4 /* drop all of them */))
}
let expected = Array(1...16) + Array("EFGHIJKLMNOPQRSTUVWXYZ012345".utf8)
XCTAssertEqual(expected, buf.readBytes(length: written)!)
}
func testWriteForNonContiguousCollections() throws {
buf.clear()
let written = buf.writeBytes("ABCD".utf8)
XCTAssertEqual(4, written)
let expected = ["A".utf8.first!, "B".utf8.first!, "C".utf8.first!, "D".utf8.first!]
XCTAssertEqual(expected, buf.readBytes(length: written)!)
}
func testReadStringOkay() throws {
buf.clear()
let expected = "hello"
buf.writeString(expected)
let actual = buf.readString(length: expected.utf8.count)
XCTAssertEqual(expected, actual)
XCTAssertEqual("", buf.readString(length: 0))
XCTAssertNil(buf.readString(length: 1))
}
func testReadStringTooMuch() throws {
buf.clear()
XCTAssertNil(buf.readString(length: 1))
buf.writeString("a")
XCTAssertNil(buf.readString(length: 2))
XCTAssertEqual("a", buf.readString(length: 1))
}
func testSetIntegerBeyondCapacity() throws {
var buf = ByteBufferAllocator().buffer(capacity: 32)
XCTAssertLessThan(buf.capacity, 200)
buf.setInteger(17, at: 201)
buf.moveWriterIndex(to: 201 + MemoryLayout<Int>.size)
buf.moveReaderIndex(to: 201)
let i: Int = buf.getInteger(at: 201)!
XCTAssertEqual(17, i)
XCTAssertGreaterThanOrEqual(buf.capacity, 200 + MemoryLayout.size(ofValue: i))
}
func testGetIntegerBeyondCapacity() throws {
let buf = ByteBufferAllocator().buffer(capacity: 32)
XCTAssertLessThan(buf.capacity, 200)
let i: Int? = buf.getInteger(at: 201)
XCTAssertNil(i)
}
func testSetStringBeyondCapacity() throws {
var buf = ByteBufferAllocator().buffer(capacity: 32)
XCTAssertLessThan(buf.capacity, 200)
buf.setString("HW", at: 201)
buf.moveWriterIndex(to: 201 + 2)
buf.moveReaderIndex(to: 201)
let s = buf.getString(at: 201, length: 2)!
XCTAssertEqual("HW", s)
XCTAssertGreaterThanOrEqual(buf.capacity, 202)
}
func testGetStringBeyondCapacity() throws {
let buf = ByteBufferAllocator().buffer(capacity: 32)
XCTAssertLessThan(buf.capacity, 200)
let i: String? = buf.getString(at: 201, length: 1)
XCTAssertNil(i)
}
func testAllocationOfReallyBigByteBuffer() throws {
#if arch(arm) || arch(i386)
// this test doesn't work on 32-bit platforms because the address space is only 4GB large and we're trying
// to make a 4GB ByteBuffer which just won't fit. Even going down to 2GB won't make it better.
return
#endif
let alloc = ByteBufferAllocator(hookedMalloc: { testAllocationOfReallyBigByteBuffer_mallocHook($0) },
hookedRealloc: { testAllocationOfReallyBigByteBuffer_reallocHook($0, $1) },
hookedFree: { testAllocationOfReallyBigByteBuffer_freeHook($0) },
hookedMemcpy: { testAllocationOfReallyBigByteBuffer_memcpyHook($0, $1, $2) })
let reallyBigSize = Int(Int32.max)
XCTAssertEqual(AllocationExpectationState.begin, testAllocationOfReallyBigByteBuffer_state)
var buf = alloc.buffer(capacity: reallyBigSize)
XCTAssertEqual(AllocationExpectationState.mallocDone, testAllocationOfReallyBigByteBuffer_state)
XCTAssertGreaterThanOrEqual(buf.capacity, reallyBigSize)
buf.setBytes([1], at: 0)
/* now make it expand (will trigger realloc) */
buf.setBytes([1], at: buf.capacity)
XCTAssertEqual(AllocationExpectationState.reallocDone, testAllocationOfReallyBigByteBuffer_state)
XCTAssertEqual(buf.capacity, Int(UInt32.max))
}
func testWritableBytesAccountsForSlicing() throws {
var buf = ByteBufferAllocator().buffer(capacity: 32)
XCTAssertEqual(buf.capacity, 32)
XCTAssertEqual(buf.writableBytes, 32)
let oldWriterIndex = buf.writerIndex
buf.writeStaticString("01234567")
let newBuf = buf.getSlice(at: oldWriterIndex, length: 8)!
XCTAssertEqual(newBuf.capacity, 8)
XCTAssertEqual(newBuf.writableBytes, 0)
}
func testClearDupesStorageIfTheresTwoBuffersSharingStorage() throws {
let alloc = ByteBufferAllocator()
var buf1 = alloc.buffer(capacity: 16)
let buf2 = buf1
var buf1PtrVal: UInt = 1
var buf2PtrVal: UInt = 2
buf1.withUnsafeReadableBytes { ptr in
buf1PtrVal = UInt(bitPattern: ptr.baseAddress!)
}
buf2.withUnsafeReadableBytes { ptr in
buf2PtrVal = UInt(bitPattern: ptr.baseAddress!)
}
XCTAssertEqual(buf1PtrVal, buf2PtrVal)
buf1.clear()
buf1.withUnsafeReadableBytes { ptr in
buf1PtrVal = UInt(bitPattern: ptr.baseAddress!)
}
buf2.withUnsafeReadableBytes { ptr in
buf2PtrVal = UInt(bitPattern: ptr.baseAddress!)
}
XCTAssertNotEqual(buf1PtrVal, buf2PtrVal)
}
func testClearDoesNotDupeStorageIfTheresOnlyOneBuffer() throws {
let alloc = ByteBufferAllocator()
var buf = alloc.buffer(capacity: 16)
var bufPtrValPre: UInt = 1
var bufPtrValPost: UInt = 2
buf.withUnsafeReadableBytes { ptr in
bufPtrValPre = UInt(bitPattern: ptr.baseAddress!)
}
buf.clear()
buf.withUnsafeReadableBytes { ptr in
bufPtrValPost = UInt(bitPattern: ptr.baseAddress!)
}
XCTAssertEqual(bufPtrValPre, bufPtrValPost)
}
func testClearWithBiggerMinimumCapacityDupesStorageIfTheresTwoBuffersSharingStorage() throws {
let alloc = ByteBufferAllocator()
let buf1 = alloc.buffer(capacity: 16)
var buf2 = buf1
var buf1PtrVal: UInt = 1
var buf2PtrVal: UInt = 2
buf1PtrVal = buf1.storagePointerIntegerValue()
buf2PtrVal = buf2.storagePointerIntegerValue()
XCTAssertEqual(buf1PtrVal, buf2PtrVal)
buf2.clear(minimumCapacity: 32)
buf1PtrVal = buf1.storagePointerIntegerValue()
buf2PtrVal = buf2.storagePointerIntegerValue()
XCTAssertNotEqual(buf1PtrVal, buf2PtrVal)
XCTAssertLessThan(buf1.capacity, 32)
XCTAssertGreaterThanOrEqual(buf2.capacity, 32)
}
func testClearWithSmallerMinimumCapacityDupesStorageIfTheresTwoBuffersSharingStorage() throws {
let alloc = ByteBufferAllocator()
let buf1 = alloc.buffer(capacity: 16)
var buf2 = buf1
var buf1PtrVal: UInt = 1
var buf2PtrVal: UInt = 2
buf1PtrVal = buf1.storagePointerIntegerValue()
buf2PtrVal = buf2.storagePointerIntegerValue()
XCTAssertEqual(buf1PtrVal, buf2PtrVal)
buf2.clear(minimumCapacity: 4)
buf1PtrVal = buf1.storagePointerIntegerValue()
buf2PtrVal = buf2.storagePointerIntegerValue()
XCTAssertNotEqual(buf1PtrVal, buf2PtrVal)
XCTAssertGreaterThanOrEqual(buf1.capacity, 16)
XCTAssertLessThan(buf2.capacity, 16)
}
func testClearWithBiggerMinimumCapacityDoesNotDupeStorageIfTheresOnlyOneBuffer() throws {
let alloc = ByteBufferAllocator()
var buf = alloc.buffer(capacity: 16)
var bufPtrValPre: UInt = 1
var bufPtrValPost: UInt = 2
XCTAssertLessThan(buf.capacity, 32)
let preCapacity = buf.capacity
bufPtrValPre = buf.storagePointerIntegerValue()
buf.clear(minimumCapacity: 32)
bufPtrValPost = buf.storagePointerIntegerValue()
let postCapacity = buf.capacity
XCTAssertNotEqual(bufPtrValPre, bufPtrValPost)
XCTAssertGreaterThanOrEqual(buf.capacity, 32)
XCTAssertNotEqual(preCapacity, postCapacity)
}
func testClearWithSmallerMinimumCapacityDoesNotDupeStorageIfTheresOnlyOneBuffer() throws {
let alloc = ByteBufferAllocator()
var buf = alloc.buffer(capacity: 16)
var bufPtrValPre: UInt = 1
var bufPtrValPost: UInt = 2
let preCapacity = buf.capacity
XCTAssertGreaterThanOrEqual(buf.capacity, 16)
bufPtrValPre = buf.storagePointerIntegerValue()
buf.clear(minimumCapacity: 8)
bufPtrValPost = buf.storagePointerIntegerValue()
let postCapacity = buf.capacity
XCTAssertEqual(bufPtrValPre, bufPtrValPost)
XCTAssertEqual(preCapacity, postCapacity)
}
func testClearWithBiggerCapacityDoesReallocateStorageCorrectlyIfTheresOnlyOneBuffer() throws {
let alloc = ByteBufferAllocator()
var buf = alloc.buffer(capacity: 16)
buf.clear(minimumCapacity: 32)
XCTAssertEqual(buf._storage.capacity, 32)
}
func testClearWithSmallerCapacityDoesReallocateStorageCorrectlyIfTheresOnlyOneBuffer() throws {
let alloc = ByteBufferAllocator()
var buf = alloc.buffer(capacity: 16)
buf.clear(minimumCapacity: 8)
XCTAssertEqual(buf._storage.capacity, 16)
}
func testClearDoesAllocateStorageCorrectlyIfTheresTwoBuffersSharingStorage() throws {
let alloc = ByteBufferAllocator()
var buf1 = alloc.buffer(capacity: 16)
let buf2 = buf1
buf1.clear(minimumCapacity: 8)
XCTAssertEqual(buf1._storage.capacity, 8)
XCTAssertEqual(buf2._storage.capacity, 16)
}
func testClearResetsTheSliceCapacityIfTheresOnlyOneBuffer() {
let alloc = ByteBufferAllocator()
var buf = alloc.buffer(capacity: 16)
buf.writeString("qwertyuiop")
XCTAssertEqual(buf.capacity, 16)
var slice = buf.getSlice(at: 3, length: 4)!
XCTAssertEqual(slice.capacity, 4)
slice.clear()
XCTAssertEqual(slice.capacity, 16)
}
func testClearResetsTheSliceCapacityIfTheresTwoSlicesSharingStorage() {
let alloc = ByteBufferAllocator()
var buf = alloc.buffer(capacity: 16)
buf.writeString("qwertyuiop")
var slice1 = buf.getSlice(at: 3, length: 4)!
let slice2 = slice1
slice1.clear()
XCTAssertEqual(slice1.capacity, 16)
XCTAssertEqual(slice2.capacity, 4)
}
func testWeUseFastWriteForContiguousCollections() throws {
struct WrongCollection: Collection {
let storage: [UInt8] = [1, 2, 3]
typealias Element = UInt8
typealias Index = Array<UInt8>.Index
typealias SubSequence = Array<UInt8>.SubSequence
typealias Indices = Array<UInt8>.Indices
public var indices: Indices {
return self.storage.indices
}
public subscript(bounds: Range<Index>) -> SubSequence {
return self.storage[bounds]
}
public subscript(position: Index) -> Element {
/* this is wrong but we need to check that we don't access this */
XCTFail("shouldn't have been called")
return 0xff
}
public var startIndex: Index {
return self.storage.startIndex
}
public var endIndex: Index {
return self.storage.endIndex
}
func index(after i: Index) -> Index {
return self.storage.index(after: i)
}
func withContiguousStorageIfAvailable<R>(_ body: (UnsafeBufferPointer<UInt8>) throws -> R) rethrows -> R? {
return try self.storage.withUnsafeBufferPointer(body)
}
}
buf.clear()
buf.writeBytes(WrongCollection())
XCTAssertEqual(3, buf.readableBytes)
XCTAssertEqual(1, buf.readInteger()! as UInt8)
XCTAssertEqual(2, buf.readInteger()! as UInt8)
XCTAssertEqual(3, buf.readInteger()! as UInt8)
buf.setBytes(WrongCollection(), at: 0)
XCTAssertEqual(0, buf.readableBytes)
buf.moveWriterIndex(to: 3)
buf.moveReaderIndex(to: 0)
XCTAssertEqual(1, buf.getInteger(at: 0, as: UInt8.self))
XCTAssertEqual(2, buf.getInteger(at: 1, as: UInt8.self))
XCTAssertEqual(3, buf.getInteger(at: 2, as: UInt8.self))
}
func testUnderestimatingSequenceWorks() throws {
struct UnderestimatingSequence: Sequence {
let storage: [UInt8] = Array(0...255)
typealias Element = UInt8
public var indices: CountableRange<Int> {
return self.storage.indices
}
public subscript(position: Int) -> Element {
return self.storage[position]
}
public var underestimatedCount: Int {
return 8
}
func makeIterator() -> Array<UInt8>.Iterator {
return self.storage.makeIterator()
}
}
buf = self.allocator.buffer(capacity: 4)
buf.clear()
buf.writeBytes(UnderestimatingSequence())
XCTAssertEqual(256, buf.readableBytes)
for i in 0..<256 {
let actual = Int(buf.readInteger()! as UInt8)
XCTAssertEqual(i, actual)
}
buf = self.allocator.buffer(capacity: 4)
buf.setBytes(UnderestimatingSequence(), at: 0)
XCTAssertEqual(0, buf.readableBytes)
buf.moveWriterIndex(to: 256)
buf.moveReaderIndex(to: 0)
for i in 0..<256 {
XCTAssertEqual(i, buf.getInteger(at: i, as: UInt8.self).map(Int.init))
}
}
func testZeroSizeByteBufferResizes() {
var buf = ByteBufferAllocator().buffer(capacity: 0)
buf.writeStaticString("x")
XCTAssertEqual(buf.writerIndex, 1)
}
func testSpecifyTypesAndEndiannessForIntegerMethods() {
self.buf.clear()
self.buf.writeInteger(-1, endianness: .big, as: Int64.self)
XCTAssertEqual(-1, self.buf.readInteger(endianness: .big, as: Int64.self))
self.buf.setInteger(0xdeadbeef, at: 0, endianness: .little, as: UInt64.self)
self.buf.moveWriterIndex(to: 8)
self.buf.moveReaderIndex(to: 0)
XCTAssertEqual(0xdeadbeef, self.buf.getInteger(at: 0, endianness: .little, as: UInt64.self))
}
func testByteBufferFitsInACoupleOfEnums() throws {
enum Level4 {
case case1(ByteBuffer)
case case2(ByteBuffer)
case case3(ByteBuffer)
case case4(ByteBuffer)
}
enum Level3 {
case case1(Level4)
case case2(Level4)
case case3(Level4)
case case4(Level4)
}
enum Level2 {
case case1(Level3)
case case2(Level3)
case case3(Level3)
case case4(Level3)
}
enum Level1 {
case case1(Level2)
case case2(Level2)
case case3(Level2)
case case4(Level2)
}
XCTAssertLessThanOrEqual(MemoryLayout<ByteBuffer>.size, 23)
XCTAssertLessThanOrEqual(MemoryLayout<Level1>.size, 24)
XCTAssertLessThanOrEqual(MemoryLayout.size(ofValue: Level1.case1(.case2(.case3(.case4(self.buf))))), 24)
XCTAssertLessThanOrEqual(MemoryLayout.size(ofValue: Level1.case1(.case3(.case4(.case1(self.buf))))), 24)
}
func testLargeSliceBegin16MBIsOkayAndDoesNotCopy() throws {
var fourMBBuf = self.allocator.buffer(capacity: 4 * 1024 * 1024)
fourMBBuf.writeBytes(repeatElement(0xff, count: fourMBBuf.capacity))
let totalBufferSize = 5 * fourMBBuf.readableBytes
XCTAssertEqual(4 * 1024 * 1024, fourMBBuf.readableBytes)
var buf = self.allocator.buffer(capacity: totalBufferSize)
for _ in 0..<5 {
var fresh = fourMBBuf
buf.writeBuffer(&fresh)
}
let offset = Int(_UInt24.max)
// mark some special bytes
buf.setInteger(0xaa, at: 0, as: UInt8.self)
buf.setInteger(0xbb, at: offset - 1, as: UInt8.self)
buf.setInteger(0xcc, at: offset, as: UInt8.self)
buf.setInteger(0xdd, at: buf.writerIndex - 1, as: UInt8.self)
XCTAssertEqual(totalBufferSize, buf.readableBytes)
let oldPtrVal = buf.withUnsafeReadableBytes {
UInt(bitPattern: $0.baseAddress!.advanced(by: offset))
}
let expectedReadableBytes = totalBufferSize - offset
let slice = buf.getSlice(at: offset, length: expectedReadableBytes)!
XCTAssertEqual(expectedReadableBytes, slice.readableBytes)
let newPtrVal = slice.withUnsafeReadableBytes {
UInt(bitPattern: $0.baseAddress!)
}
XCTAssertEqual(oldPtrVal, newPtrVal)
XCTAssertEqual(0xcc, slice.getInteger(at: 0, as: UInt8.self))
XCTAssertEqual(0xdd, slice.getInteger(at: slice.writerIndex - 1, as: UInt8.self))
}
func testLargeSliceBeginMoreThan16MBIsOkay() throws {
var fourMBBuf = self.allocator.buffer(capacity: 4 * 1024 * 1024)
fourMBBuf.writeBytes(repeatElement(0xff, count: fourMBBuf.capacity))
let totalBufferSize = 5 * fourMBBuf.readableBytes + 1
XCTAssertEqual(4 * 1024 * 1024, fourMBBuf.readableBytes)
var buf = self.allocator.buffer(capacity: totalBufferSize)
for _ in 0..<5 {
var fresh = fourMBBuf
buf.writeBuffer(&fresh)
}
let offset = Int(_UInt24.max) + 1
// mark some special bytes
buf.setInteger(0xaa, at: 0, as: UInt8.self)
buf.setInteger(0xbb, at: offset - 1, as: UInt8.self)
buf.setInteger(0xcc, at: offset, as: UInt8.self)
buf.writeInteger(0xdd, as: UInt8.self) // write extra byte so the slice is the same length as above
XCTAssertEqual(totalBufferSize, buf.readableBytes)
let expectedReadableBytes = totalBufferSize - offset
let slice = buf.getSlice(at: offset, length: expectedReadableBytes)!
XCTAssertEqual(expectedReadableBytes, slice.readableBytes)
XCTAssertEqual(0, slice.readerIndex)
XCTAssertEqual(expectedReadableBytes, slice.writerIndex)
XCTAssertEqual(Int(UInt32(expectedReadableBytes).nextPowerOf2()), slice.capacity)
XCTAssertEqual(0xcc, slice.getInteger(at: 0, as: UInt8.self))
XCTAssertEqual(0xdd, slice.getInteger(at: slice.writerIndex - 1, as: UInt8.self))
}
func testDiscardReadBytesOnConsumedBuffer() {
var buffer = self.allocator.buffer(capacity: 8)
buffer.writeInteger(0xaa, as: UInt8.self)
XCTAssertEqual(1, buffer.readableBytes)
XCTAssertEqual(0xaa, buffer.readInteger(as: UInt8.self))
XCTAssertEqual(0, buffer.readableBytes)
let buffer2 = buffer
XCTAssertTrue(buffer.discardReadBytes())
XCTAssertEqual(0, buffer.readerIndex)
XCTAssertEqual(0, buffer.writerIndex)
// As we fully consumed the buffer we should only have adjusted the indices but not triggered a copy as result of CoW sematics.
// So we should still be able to also read the old data.
buffer.moveWriterIndex(to: 1)
buffer.moveReaderIndex(to: 0)
XCTAssertEqual(0xaa, buffer.getInteger(at: 0, as: UInt8.self))
XCTAssertEqual(0, buffer2.readableBytes)
}
func testDumpBytesFormat() throws {
self.buf.clear()
for f in UInt8.min...UInt8.max {
self.buf.writeInteger(f)
}
let actual = self.buf._storage.dumpBytes(slice: self.buf._slice, offset: 0, length: self.buf.readableBytes)
let expected = """
[ \
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f \
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f \
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f \
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f \
80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f \
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf \
c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df \
e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff ]
"""
XCTAssertEqual(expected, actual)
}
func testReadableBytesView() throws {
self.buf.clear()
self.buf.writeString("hello world 012345678")
XCTAssertEqual("hello ", self.buf.readString(length: 6))
self.buf.moveWriterIndex(to: self.buf.writerIndex - 10)
XCTAssertEqual("world", String(decoding: self.buf.readableBytesView, as: Unicode.UTF8.self))
XCTAssertEqual("world", self.buf.readString(length: self.buf.readableBytes))
}
func testReadableBytesViewNoReadableBytes() throws {
self.buf.clear()
let view = self.buf.readableBytesView
XCTAssertEqual(0, view.count)
}
func testBytesView() throws {
self.buf.clear()
self.buf.writeString("hello world 012345678")
XCTAssertEqual(self.buf.viewBytes(at: self.buf.readerIndex,
length: self.buf.writerIndex - self.buf.readerIndex).map { (view: ByteBufferView) -> String in
String(decoding: view, as: Unicode.UTF8.self)
},
self.buf.getString(at: self.buf.readerIndex, length: self.buf.readableBytes))
XCTAssertEqual(self.buf.viewBytes(at: 0, length: 0).map { Array($0) }, [])
XCTAssertEqual(Array("hello world 012345678".utf8),
self.buf.viewBytes(at: 0, length: self.buf.readableBytes).map(Array.init))
}
func testViewsStartIndexIsStable() throws {
self.buf.writeString("hello")
let view: ByteBufferView? = self.buf.viewBytes(at: 1, length: 3)
XCTAssertEqual(1, view?.startIndex)
XCTAssertEqual(3, view?.count)
XCTAssertEqual(4, view?.endIndex)
XCTAssertEqual("ell", view.map { String(decoding: $0, as: Unicode.UTF8.self) })
}
func testSlicesOfByteBufferViewsAreByteBufferViews() throws {
self.buf.writeString("hello")
let view: ByteBufferView? = self.buf.viewBytes(at: 1, length: 3)
XCTAssertEqual("ell", view.map { String(decoding: $0, as: Unicode.UTF8.self) })
let viewSlice: ByteBufferView? = view.map { $0[$0.startIndex + 1 ..< $0.endIndex] }
XCTAssertEqual("ll", viewSlice.map { String(decoding: $0, as: Unicode.UTF8.self) })
XCTAssertEqual("l", viewSlice.map { String(decoding: $0.dropFirst(), as: Unicode.UTF8.self) })
XCTAssertEqual("", viewSlice.map { String(decoding: $0.dropFirst().dropLast(), as: Unicode.UTF8.self) })
}
func testReadableBufferViewRangeEqualCapacity() throws {
self.buf.clear()
self.buf.moveWriterIndex(forwardBy: buf.capacity)
let view = self.buf.readableBytesView
let viewSlice: ByteBufferView = view[view.startIndex ..< view.endIndex]
XCTAssertEqual(buf.readableBytes, viewSlice.count)
}
func testBufferViewCoWs() throws {
self.buf.writeBytes([0x0, 0x1, 0x2])
var view = self.buf.readableBytesView
view.replaceSubrange(view.indices, with: [0xa, 0xb, 0xc])
XCTAssertEqual(self.buf.readBytes(length: 3), [0x0, 0x1, 0x2])
XCTAssertTrue(view.elementsEqual([0xa, 0xb, 0xc]))
self.buf.writeBytes([0x0, 0x1, 0x2])
view = self.buf.readableBytesView
view.replaceSubrange(view.indices, with: [0xa, 0xb])
XCTAssertEqual(self.buf.readBytes(length: 3), [0x0, 0x1, 0x2])
XCTAssertTrue(view.elementsEqual([0xa, 0xb]))
self.buf.writeBytes([0x0, 0x1, 0x2])
view = self.buf.readableBytesView
view.replaceSubrange(view.indices, with: [0xa, 0xb, 0xc, 0xd])
XCTAssertEqual(self.buf.readBytes(length: 3), [0x0, 0x1, 0x2])
XCTAssertTrue(view.elementsEqual([0xa, 0xb, 0xc, 0xd]))
}
func testBufferViewMutationViaSubscriptIndex() throws {
self.buf.writeBytes([0x0, 0x1, 0x2])
var view = self.buf.readableBytesView
view[0] = 0xa
view[1] = 0xb
view[2] = 0xc
XCTAssertTrue(view.elementsEqual([0xa, 0xb, 0xc]))
}
func testBufferViewReplaceBeyondEndOfRange() throws {
self.buf.writeBytes([1, 2, 3])
var view = self.buf.readableBytesView
view.replaceSubrange(2..<3, with: [2, 3, 4])
XCTAssertTrue(view.elementsEqual([1, 2, 2, 3, 4]))
}
func testBufferViewReplaceWithSubrangeOfSelf() throws {
let oneToNine: [UInt8] = (1...9).map { $0 }
self.buf.writeBytes(oneToNine)
var view = self.buf.readableBytesView
view[6..<9] = view[0..<3]
XCTAssertTrue(view.elementsEqual([1, 2, 3, 4, 5, 6, 1, 2, 3]))
view[0..<3] = view[1..<4]
XCTAssertTrue(view.elementsEqual([2, 3, 4, 4, 5, 6, 1, 2, 3]))
view[1..<4] = view[0..<3]
XCTAssertTrue(view.elementsEqual([2, 2, 3, 4, 5, 6, 1, 2, 3]))
}
func testBufferViewMutationViaSubscriptRange() throws {
let oneToNine: [UInt8] = (1...9).map { $0 }
var oneToNineBuffer = self.allocator.buffer(capacity: 9)
oneToNineBuffer.writeBytes(oneToNine)
let oneToNineView = oneToNineBuffer.readableBytesView
self.buf.writeBytes(Array(repeating: UInt8(0), count: 9))
var view = self.buf.readableBytesView
view[0..<3] = oneToNineView[0..<3]
XCTAssertTrue(view.elementsEqual([1, 2, 3, 0, 0, 0, 0, 0, 0]))
// Replace with shorter range
view[3..<6] = oneToNineView[3..<5]
XCTAssertTrue(view.elementsEqual([1, 2, 3, 4, 5, 0, 0, 0]))
// Replace with longer range
view[5..<8] = oneToNineView[5..<9]
XCTAssertTrue(view.elementsEqual(oneToNine))
}
func testBufferViewReplaceSubrangeWithEqualLengthBytes() throws {
self.buf.writeBytes([0x0, 0x1, 0x2, 0x3, 0x4])
var view = ByteBufferView(self.buf)
XCTAssertEqual(view.count, self.buf.readableBytes)
view.replaceSubrange(view.indices.suffix(3), with: [0xd, 0xe, 0xf])
var modifiedBuf = ByteBuffer(view)
XCTAssertEqual(self.buf.readerIndex, modifiedBuf.readerIndex)
XCTAssertEqual(self.buf.writerIndex, modifiedBuf.writerIndex)
XCTAssertEqual([0x0, 0x1, 0xd, 0xe, 0xf], modifiedBuf.readBytes(length: modifiedBuf.readableBytes)!)
}
func testBufferViewReplaceSubrangeWithFewerBytes() throws {
self.buf.writeBytes([0x0, 0x1, 0x2, 0x3, 0x4])
var view = ByteBufferView(self.buf)
view.replaceSubrange(view.indices.suffix(3), with: [0xd])
var modifiedBuf = ByteBuffer(view)
XCTAssertEqual(self.buf.readerIndex, modifiedBuf.readerIndex)
XCTAssertEqual(self.buf.writerIndex - 2, modifiedBuf.writerIndex)
XCTAssertEqual([0x0, 0x1, 0xd], modifiedBuf.readBytes(length: modifiedBuf.readableBytes)!)
}
func testBufferViewReplaceSubrangeWithMoreBytes() throws {
self.buf.writeBytes([0x0, 0x1, 0x2, 0x3])
var view = ByteBufferView(self.buf)
XCTAssertTrue(view.elementsEqual([0x0, 0x1, 0x2, 0x3]))
view.replaceSubrange(view.indices.suffix(1), with: [0xa, 0xb])
XCTAssertTrue(view.elementsEqual([0x0, 0x1, 0x2, 0xa, 0xb]))
var modifiedBuf = ByteBuffer(view)
XCTAssertEqual(self.buf.readerIndex, modifiedBuf.readerIndex)
XCTAssertEqual(self.buf.writerIndex + 1, modifiedBuf.writerIndex)
XCTAssertEqual([0x0, 0x1, 0x2, 0xa, 0xb], modifiedBuf.readBytes(length: modifiedBuf.readableBytes)!)
}
func testBufferViewEmpty() throws {
self.buf.writeBytes([0, 1, 2])
var view = ByteBufferView()
view[0..<0] = self.buf.readableBytesView
XCTAssertEqual(view.indices, 0..<3)
self.buf = ByteBuffer(view)
XCTAssertEqual(self.buf.readerIndex, 0)
XCTAssertEqual(self.buf.writerIndex, 3)
let anotherBuf = self.buf!
XCTAssertEqual([0, 1, 2], self.buf.readBytes(length: self.buf.readableBytes))
var anotherView = anotherBuf.readableBytesView
anotherView.replaceSubrange(0..<3, with: [])
XCTAssertTrue(anotherView.isEmpty)
self.buf = ByteBuffer(anotherView)
XCTAssertEqual(self.buf.readerIndex, 0)
XCTAssertEqual(self.buf.writerIndex, 0)
XCTAssertEqual([], self.buf.readBytes(length: self.buf.readableBytes))
}
func testByteBuffersCanBeInitializedFromByteBufferViews() throws {
self.buf.writeString("hello")
let readableByteBuffer = ByteBuffer(self.buf.readableBytesView)
XCTAssertEqual(self.buf, readableByteBuffer)
let sliceByteBuffer = self.buf.viewBytes(at: 1, length: 3).map(ByteBuffer.init)
XCTAssertEqual("ell", sliceByteBuffer.flatMap { $0.getString(at: 0, length: $0.readableBytes) })
self.buf.clear()
let emptyByteBuffer = ByteBuffer(self.buf.readableBytesView)
XCTAssertEqual(self.buf, emptyByteBuffer)
var fixedCapacityBuffer = self.allocator.buffer(capacity: 16)
let capacity = fixedCapacityBuffer.capacity
fixedCapacityBuffer.writeString(String(repeating: "x", count: capacity))
XCTAssertEqual(capacity, fixedCapacityBuffer.capacity)
XCTAssertEqual(fixedCapacityBuffer, ByteBuffer(fixedCapacityBuffer.readableBytesView))
}
func testReserveCapacityWhenOversize() throws {
let oldCapacity = buf.capacity
let oldPtrVal = buf.withVeryUnsafeBytes {
UInt(bitPattern: $0.baseAddress!)
}
buf.reserveCapacity(oldCapacity - 1)
let newPtrVal = buf.withVeryUnsafeBytes {
UInt(bitPattern: $0.baseAddress!)
}
XCTAssertEqual(buf.capacity, oldCapacity)
XCTAssertEqual(oldPtrVal, newPtrVal)
}
func testReserveCapacitySameCapacity() throws {
let oldCapacity = buf.capacity
let oldPtrVal = buf.withVeryUnsafeBytes {
UInt(bitPattern: $0.baseAddress!)
}
buf.reserveCapacity(oldCapacity)
let newPtrVal = buf.withVeryUnsafeBytes {
UInt(bitPattern: $0.baseAddress!)
}
XCTAssertEqual(buf.capacity, oldCapacity)
XCTAssertEqual(oldPtrVal, newPtrVal)
}
func testReserveCapacityLargerUniquelyReferencedCallsRealloc() throws {
testReserveCapacityLarger_reallocCount = 0
testReserveCapacityLarger_mallocCount = 0
let alloc = ByteBufferAllocator(hookedMalloc: testReserveCapacityLarger_mallocHook,
hookedRealloc: testReserveCapacityLarger_reallocHook,
hookedFree: testReserveCapacityLarger_freeHook,
hookedMemcpy: testReserveCapacityLarger_memcpyHook)
var buf = alloc.buffer(capacity: 16)
let oldCapacity = buf.capacity
XCTAssertEqual(testReserveCapacityLarger_mallocCount, 1)
XCTAssertEqual(testReserveCapacityLarger_reallocCount, 0)
buf.reserveCapacity(32)
XCTAssertEqual(testReserveCapacityLarger_mallocCount, 1)
XCTAssertEqual(testReserveCapacityLarger_reallocCount, 1)
XCTAssertNotEqual(buf.capacity, oldCapacity)
}
func testReserveCapacityLargerMultipleReferenceCallsMalloc() throws {
testReserveCapacityLarger_reallocCount = 0
testReserveCapacityLarger_mallocCount = 0
let alloc = ByteBufferAllocator(hookedMalloc: testReserveCapacityLarger_mallocHook,
hookedRealloc: testReserveCapacityLarger_reallocHook,
hookedFree: testReserveCapacityLarger_freeHook,
hookedMemcpy: testReserveCapacityLarger_memcpyHook)
var buf = alloc.buffer(capacity: 16)
let bufCopy = buf
withExtendedLifetime(bufCopy) {
let oldCapacity = buf.capacity
let oldPtrVal = buf.withVeryUnsafeBytes {
UInt(bitPattern: $0.baseAddress!)
}
XCTAssertEqual(testReserveCapacityLarger_mallocCount, 1)
XCTAssertEqual(testReserveCapacityLarger_reallocCount, 0)
buf.reserveCapacity(32)
XCTAssertEqual(testReserveCapacityLarger_mallocCount, 2)
XCTAssertEqual(testReserveCapacityLarger_reallocCount, 0)
let newPtrVal = buf.withVeryUnsafeBytes {
UInt(bitPattern: $0.baseAddress!)
}
XCTAssertNotEqual(buf.capacity, oldCapacity)
XCTAssertNotEqual(oldPtrVal, newPtrVal)
}
}
func testReadWithFunctionsThatReturnNumberOfReadBytesAreDiscardable() {
var buf = self.buf!
buf.writeString("ABCD")
/* deliberately not ignoring the result */ buf.readWithUnsafeReadableBytes { buffer in
XCTAssertEqual(4, buffer.count)
return 2
}
/* deliberately not ignoring the result */ buf.readWithUnsafeMutableReadableBytes { buffer in
XCTAssertEqual(2, buffer.count)
return 2
}
XCTAssertEqual(0, buf.readableBytes)
}
func testWriteAndSetAndGetAndReadEncoding() throws {
var buf = self.buf!
buf.clear()
var writtenBytes = try assertNoThrowWithValue(buf.writeString("ÆBCD", encoding: .utf16LittleEndian))
XCTAssertEqual(writtenBytes, 8)
XCTAssertEqual(buf.readableBytes, 8)
XCTAssertEqual(buf.getString(at: buf.readerIndex + 2, length: 6, encoding: .utf16LittleEndian), "BCD")
writtenBytes = try assertNoThrowWithValue(buf.setString("EFGH", encoding: .utf32BigEndian, at: buf.readerIndex))
XCTAssertEqual(writtenBytes, 16)
XCTAssertEqual(buf.readableBytes, 8)
XCTAssertEqual(buf.readString(length: 8, encoding: .utf32BigEndian), "EF")
XCTAssertEqual(buf.readableBytes, 0)
buf.clear()
// Confirm that we do throw.
XCTAssertThrowsError(try buf.setString("🤷‍♀️", encoding: .ascii, at: buf.readerIndex)) {
XCTAssertEqual($0 as? ByteBufferFoundationError, .failedToEncodeString)
}
XCTAssertThrowsError(try buf.writeString("🤷‍♀️", encoding: .ascii)) {
XCTAssertEqual($0 as? ByteBufferFoundationError, .failedToEncodeString)
}
}
func testPossiblyLazilyBridgedString() {
// won't hit the String writing fast path
let utf16Bytes = Data([0xfe, 0xff, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0xe4, 0x00, 0xe4, 0x00, 0xe4, 0x00, 0x0a])
let slowString = String(data: utf16Bytes, encoding: .utf16)!
self.buf.clear()
let written = self.buf.writeString(slowString as String)
XCTAssertEqual(10, written)
XCTAssertEqual("abcäää\n", String(decoding: self.buf.readableBytesView, as: Unicode.UTF8.self))
}
func testWithVeryUnsafeMutableBytesWorksOnEmptyByteBuffer() {
var buf = self.allocator.buffer(capacity: 0)
XCTAssertEqual(0, buf.capacity)
buf.withVeryUnsafeMutableBytes { ptr in
XCTAssertEqual(0, ptr.count)
}
}
func testWithVeryUnsafeMutableBytesYieldsPointerToWholeStorage() {
var buf = self.allocator.buffer(capacity: 16)
let capacity = buf.capacity
XCTAssertGreaterThanOrEqual(capacity, 16)
buf.writeString("1234")
XCTAssertEqual(capacity, buf.capacity)
buf.withVeryUnsafeMutableBytes { ptr in
XCTAssertEqual(capacity, ptr.count)
XCTAssertEqual("1234", String(decoding: ptr[0..<4], as: Unicode.UTF8.self))
}
}
func testWithVeryUnsafeMutableBytesYieldsPointerToWholeStorageAndCanBeWritenTo() {
var buf = self.allocator.buffer(capacity: 16)
let capacity = buf.capacity
XCTAssertGreaterThanOrEqual(capacity, 16)
buf.writeString("1234")
XCTAssertEqual(capacity, buf.capacity)
buf.withVeryUnsafeMutableBytes { ptr in
XCTAssertEqual(capacity, ptr.count)
XCTAssertEqual("1234", String(decoding: ptr[0..<4], as: Unicode.UTF8.self))
UnsafeMutableRawBufferPointer(rebasing: ptr[4..<8]).copyBytes(from: "5678".utf8)
}
buf.moveWriterIndex(forwardBy: 4)
XCTAssertEqual("12345678", buf.readString(length: buf.readableBytes))
buf.withVeryUnsafeMutableBytes { ptr in
XCTAssertEqual(capacity, ptr.count)
XCTAssertEqual("12345678", String(decoding: ptr[0..<8], as: Unicode.UTF8.self))
ptr[0] = "X".utf8.first!
UnsafeMutableRawBufferPointer(rebasing: ptr[8..<16]).copyBytes(from: "abcdefgh".utf8)
}
buf.moveWriterIndex(forwardBy: 8)
XCTAssertEqual("abcdefgh", buf.readString(length: buf.readableBytes))
buf.moveWriterIndex(to: 1)
buf.moveReaderIndex(to: 0)
XCTAssertEqual("X", buf.getString(at: 0, length: 1))
}
func testWithVeryUnsafeMutableBytesDoesCoW() {
var buf = self.allocator.buffer(capacity: 16)
let capacity = buf.capacity
XCTAssertGreaterThanOrEqual(capacity, 16)
buf.writeString("1234")
let bufCopy = buf
XCTAssertEqual(capacity, buf.capacity)
buf.withVeryUnsafeMutableBytes { ptr in
XCTAssertEqual(capacity, ptr.count)
XCTAssertEqual("1234", String(decoding: ptr[0..<4], as: Unicode.UTF8.self))
UnsafeMutableRawBufferPointer(rebasing: ptr[0..<8]).copyBytes(from: "abcdefgh".utf8)
}
buf.moveWriterIndex(forwardBy: 4)
XCTAssertEqual("1234", String(decoding: bufCopy.readableBytesView, as: Unicode.UTF8.self))
XCTAssertEqual("abcdefgh", String(decoding: buf.readableBytesView, as: Unicode.UTF8.self))
}
func testWithVeryUnsafeMutableBytesDoesCoWonSlices() {
var buf = self.allocator.buffer(capacity: 16)
let capacity = buf.capacity
XCTAssertGreaterThanOrEqual(capacity, 16)
buf.writeString("1234567890")
var buf2 = buf.getSlice(at: 4, length: 4)!
XCTAssertEqual(capacity, buf.capacity)
let capacity2 = buf2.capacity
buf2.withVeryUnsafeMutableBytes { ptr in
XCTAssertEqual(capacity2, ptr.count)
XCTAssertEqual("5678", String(decoding: ptr[0..<4], as: Unicode.UTF8.self))
UnsafeMutableRawBufferPointer(rebasing: ptr[0..<4]).copyBytes(from: "QWER".utf8)
}
XCTAssertEqual("QWER", String(decoding: buf2.readableBytesView, as: Unicode.UTF8.self))
XCTAssertEqual("1234567890", String(decoding: buf.readableBytesView, as: Unicode.UTF8.self))
}
func testGetDispatchDataWorks() {
self.buf.clear()
self.buf.writeString("abcdefgh")
XCTAssertEqual(0, self.buf.getDispatchData(at: 7, length: 0)!.count)
XCTAssertNil(self.buf.getDispatchData(at: self.buf.capacity, length: 1))
XCTAssertEqual("abcdefgh", String(decoding: self.buf.getDispatchData(at: 0, length: 8)!, as: Unicode.UTF8.self))
XCTAssertEqual("ef", String(decoding: self.buf.getDispatchData(at: 4, length: 2)!, as: Unicode.UTF8.self))
}
func testGetDispatchDataReadWrite() {
var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 4, alignment: 0)
buffer.copyBytes(from: "1234".utf8)
defer {
buffer.deallocate()
}
self.buf.clear()
self.buf.writeString("abcdefgh")
self.buf.writeDispatchData( DispatchData.empty)
self.buf.writeDispatchData( DispatchData(bytes: UnsafeRawBufferPointer(buffer)))
XCTAssertEqual(12, self.buf.readableBytes)
XCTAssertEqual("abcdefgh1234", String(decoding: self.buf.readDispatchData(length: 12)!, as: Unicode.UTF8.self))
XCTAssertNil(self.buf.readDispatchData(length: 1))
XCTAssertEqual(0, self.buf.readDispatchData(length: 0)?.count ?? 12)
}
func testVariousContiguousStorageAccessors() {
self.buf.clear()
self.buf.writeStaticString("abc0123456789efg")
self.buf.moveReaderIndex(to: 3)
self.buf.moveWriterIndex(to: 13)
func inspectContiguousBytes<Bytes: ContiguousBytes>(bytes: Bytes, expectedString: String) {
bytes.withUnsafeBytes { bytes in
XCTAssertEqual(expectedString, String(decoding: bytes, as: Unicode.UTF8.self))
}
}
inspectContiguousBytes(bytes: self.buf.readableBytesView, expectedString: "0123456789")
let r1 = self.buf.readableBytesView.withContiguousStorageIfAvailable { bytes -> String in
String(decoding: bytes, as: Unicode.UTF8.self)
}
XCTAssertEqual("0123456789", r1)
self.buf.clear()
inspectContiguousBytes(bytes: self.buf.readableBytesView, expectedString: "")
let r2 = self.buf.readableBytesView.withContiguousStorageIfAvailable { bytes -> String in
String(decoding: bytes, as: Unicode.UTF8.self)
}
XCTAssertEqual("", r2)
}
func testGetBytesThatAreNotReadable() {
var buf = self.allocator.buffer(capacity: 256)
// 1) Nothing available
// no `get*` should work
XCTAssertNil(buf.getInteger(at: 0, as: UInt8.self))
XCTAssertNil(buf.getInteger(at: 0, as: UInt16.self))
XCTAssertNil(buf.getInteger(at: 0, as: UInt32.self))
XCTAssertNil(buf.getInteger(at: 0, as: UInt64.self))
XCTAssertNil(buf.getString(at: 0, length: 1))
XCTAssertNil(buf.getSlice(at: 0, length: 1))
XCTAssertNil(buf.getData(at: 0, length: 1))
XCTAssertNil(buf.getDispatchData(at: 0, length: 1))
XCTAssertNil(buf.getBytes(at: 0, length: 1))
XCTAssertNil(buf.getString(at: 0, length: 1, encoding: .utf8))
XCTAssertNil(buf.viewBytes(at: 0, length: 1))
// but some `get*` should be able to produce empties
XCTAssertEqual("", buf.getString(at: 0, length: 0))
XCTAssertEqual(0, buf.getSlice(at: 0, length: 0)?.capacity)
XCTAssertEqual(Data(), buf.getData(at: 0, length: 0))
XCTAssertEqual(0, buf.getDispatchData(at: 0, length: 0)?.count)
XCTAssertEqual([], buf.getBytes(at: 0, length: 0))
XCTAssertEqual("", buf.getString(at: 0, length: 0, encoding: .utf8))
XCTAssertEqual("", buf.viewBytes(at: 0, length: 0).map { String(decoding: $0, as: Unicode.UTF8.self) })
// 2) One byte available at the beginning
buf.writeInteger(0x41, as: UInt8.self)
// for most `get*`s, we can make them just not work
XCTAssertNil(buf.getInteger(at: 0, as: UInt16.self))
XCTAssertNil(buf.getInteger(at: 0, as: UInt32.self))
XCTAssertNil(buf.getInteger(at: 0, as: UInt64.self))
XCTAssertNil(buf.getString(at: 0, length: 2))
XCTAssertNil(buf.getSlice(at: 0, length: 2))
XCTAssertNil(buf.getData(at: 0, length: 2))
XCTAssertNil(buf.getDispatchData(at: 0, length: 2))
XCTAssertNil(buf.getBytes(at: 0, length: 2))
XCTAssertNil(buf.getString(at: 0, length: 2, encoding: .utf8))
XCTAssertNil(buf.viewBytes(at: 0, length: 2))
XCTAssertNil(buf.getInteger(at: 1, as: UInt8.self))
XCTAssertNil(buf.getInteger(at: 1, as: UInt16.self))
XCTAssertNil(buf.getInteger(at: 1, as: UInt32.self))
XCTAssertNil(buf.getInteger(at: 1, as: UInt64.self))
XCTAssertNil(buf.getString(at: 1, length: 1))
XCTAssertNil(buf.getSlice(at: 1, length: 1))
XCTAssertNil(buf.getData(at: 1, length: 1))
XCTAssertNil(buf.getDispatchData(at: 1, length: 1))
XCTAssertNil(buf.getBytes(at: 1, length: 1))
XCTAssertNil(buf.getString(at: 1, length: 1, encoding: .utf8))
XCTAssertNil(buf.viewBytes(at: 1, length: 1))
// on the other hand, we should be able to take that one byte out
XCTAssertEqual(0x41, buf.getInteger(at: 0, as: UInt8.self))
XCTAssertEqual("A", buf.getString(at: 0, length: 1))
XCTAssertEqual(1, buf.getSlice(at: 0, length: 1)?.capacity)
XCTAssertEqual(Data("A".utf8), buf.getData(at: 0, length: 1))
XCTAssertEqual(1, buf.getDispatchData(at: 0, length: 1)?.count)
XCTAssertEqual(["A".utf8.first!], buf.getBytes(at: 0, length: 1))
XCTAssertEqual("A", buf.getString(at: 0, length: 1, encoding: .utf8))
XCTAssertEqual("A", buf.viewBytes(at: 0, length: 1).map { String(decoding: $0, as: Unicode.UTF8.self) })
// 3) Now let's have 4 bytes towards the end
// we can make pretty much all `get*`s fail
buf.setInteger(0x41414141, at: 251, as: UInt32.self)
buf.moveWriterIndex(to: 255)
buf.moveReaderIndex(to: 251)
XCTAssertNil(buf.getInteger(at: 251, as: UInt64.self))
XCTAssertNil(buf.getString(at: 251, length: 5))
XCTAssertNil(buf.getSlice(at: 251, length: 5))
XCTAssertNil(buf.getData(at: 251, length: 5))
XCTAssertNil(buf.getDispatchData(at: 251, length: 5))
XCTAssertNil(buf.getBytes(at: 251, length: 5))
XCTAssertNil(buf.getString(at: 251, length: 5, encoding: .utf8))
XCTAssertNil(buf.viewBytes(at: 251, length: 5))
XCTAssertEqual(0x41, buf.getInteger(at: 254, as: UInt8.self))
XCTAssertEqual(0x4141, buf.getInteger(at: 253, as: UInt16.self))
XCTAssertEqual(0x41414141, buf.getInteger(at: 251, as: UInt32.self))
XCTAssertEqual("AAAA", buf.getString(at: 251, length: 4))
XCTAssertEqual(4, buf.getSlice(at: 251, length: 4)?.capacity)
XCTAssertEqual(Data("AAAA".utf8), buf.getData(at: 251, length: 4))
XCTAssertEqual(4, buf.getDispatchData(at: 251, length: 4)?.count)
XCTAssertEqual(Array(repeating: "A".utf8.first!, count: 4), buf.getBytes(at: 251, length: 4))
XCTAssertEqual("AAAA", buf.getString(at: 251, length: 4, encoding: .utf8))
XCTAssertEqual("AAAA", buf.viewBytes(at: 251, length: 4).map { String(decoding: $0, as: Unicode.UTF8.self) })
}
func testByteBufferViewAsDataProtocol() {
func checkEquals<D: DataProtocol>(expected: String, actual: D) {
var actualBytesAsArray: [UInt8] = []
for region in actual.regions {
actualBytesAsArray += Array(region)
}
XCTAssertEqual(expected, String(decoding: actualBytesAsArray, as: Unicode.UTF8.self))
}
var buf = self.allocator.buffer(capacity: 32)
buf.writeStaticString("0123abcd4567")
buf.moveReaderIndex(forwardBy: 4)
buf.moveWriterIndex(to: buf.writerIndex - 4)
checkEquals(expected: "abcd", actual: buf.readableBytesView)
}
func testDataByteTransferStrategyNoCopy() {
let byteCount = 200_000 // this needs to be large because Data might also decide to copy.
self.buf.clear()
self.buf.writeString(String(repeating: "x", count: byteCount))
var byteBufferPointerValue: UInt = 0xbad
var dataPointerValue: UInt = 0xdead
self.buf.withUnsafeReadableBytes { ptr in
byteBufferPointerValue = UInt(bitPattern: ptr.baseAddress)
}
if let data = self.buf.readData(length: byteCount, byteTransferStrategy: .noCopy) {
data.withUnsafeBytes { ptr in
dataPointerValue = UInt(bitPattern: ptr.baseAddress)
}
} else {
XCTFail("unable to read Data from ByteBuffer")
}
XCTAssertEqual(byteBufferPointerValue, dataPointerValue)
}
func testDataByteTransferStrategyCopy() {
let byteCount = 200_000 // this needs to be large because Data might also decide to copy.
self.buf.clear()
self.buf.writeString(String(repeating: "x", count: byteCount))
var byteBufferPointerValue: UInt = 0xbad
var dataPointerValue: UInt = 0xdead
self.buf.withUnsafeReadableBytes { ptr in
byteBufferPointerValue = UInt(bitPattern: ptr.baseAddress)
}
if let data = self.buf.readData(length: byteCount, byteTransferStrategy: .copy) {
data.withUnsafeBytes { ptr in
dataPointerValue = UInt(bitPattern: ptr.baseAddress)
}
} else {
XCTFail("unable to read Data from ByteBuffer")
}
XCTAssertNotEqual(byteBufferPointerValue, dataPointerValue)
}
func testDataByteTransferStrategyAutomaticMayNotCopy() {
let byteCount = 500_000 // this needs to be larger than ByteBuffer's heuristic's threshold.
self.buf.clear()
self.buf.writeString(String(repeating: "x", count: byteCount))
var byteBufferPointerValue: UInt = 0xbad
var dataPointerValue: UInt = 0xdead
self.buf.withUnsafeReadableBytes { ptr in
byteBufferPointerValue = UInt(bitPattern: ptr.baseAddress)
}
if let data = self.buf.readData(length: byteCount, byteTransferStrategy: .automatic) {
data.withUnsafeBytes { ptr in
dataPointerValue = UInt(bitPattern: ptr.baseAddress)
}
} else {
XCTFail("unable to read Data from ByteBuffer")
}
XCTAssertEqual(byteBufferPointerValue, dataPointerValue)
}
func testDataByteTransferStrategyAutomaticMayCopy() {
let byteCount = 200_000 // above Data's 'do not copy' but less than ByteBuffer's 'do not copy' threshold.
self.buf.clear()
self.buf.writeString(String(repeating: "x", count: byteCount))
var byteBufferPointerValue: UInt = 0xbad
var dataPointerValue: UInt = 0xdead
self.buf.withUnsafeReadableBytes { ptr in
byteBufferPointerValue = UInt(bitPattern: ptr.baseAddress)
}
if let data = self.buf.readData(length: byteCount, byteTransferStrategy: .automatic) {
data.withUnsafeBytes { ptr in
dataPointerValue = UInt(bitPattern: ptr.baseAddress)
}
} else {
XCTFail("unable to read Data from ByteBuffer")
}
XCTAssertNotEqual(byteBufferPointerValue, dataPointerValue)
}
func testViewBytesIsHappyWithNegativeValues() {
self.buf.clear()
XCTAssertNil(self.buf.viewBytes(at: -1, length: 0))
XCTAssertNil(self.buf.viewBytes(at: 0, length: -1))
XCTAssertNil(self.buf.viewBytes(at: -1, length: -1))
self.buf.writeString("hello world")
self.buf.moveWriterIndex(forwardBy: 6)
XCTAssertNil(self.buf.viewBytes(at: -1, length: 0))
XCTAssertNil(self.buf.viewBytes(at: 0, length: -1))
XCTAssertNil(self.buf.viewBytes(at: -1, length: -1))
}
func testByteBufferAllocatorSize1Capacity() {
let buffer = ByteBufferAllocator().buffer(capacity: 1)
XCTAssertEqual(1, buffer.capacity)
}
func testByteBufferModifiedWithoutAllocationLogic() {
var buffer = ByteBufferAllocator().buffer(capacity: 1)
let firstResult = buffer.modifyIfUniquelyOwned {
$0.readableBytes
}
XCTAssertEqual(firstResult, 0)
withExtendedLifetime(buffer) {
var localCopy = buffer
let secondResult = localCopy.modifyIfUniquelyOwned {
$0.readableBytes
}
let thirdResult = buffer.modifyIfUniquelyOwned {
$0.readableBytes
}
XCTAssertNil(secondResult)
XCTAssertNil(thirdResult)
}
let fourthResult = buffer.modifyIfUniquelyOwned {
$0.readableBytes
}
XCTAssertEqual(fourthResult, 0)
let fifthResult = buffer.modifyIfUniquelyOwned {
$0.modifyIfUniquelyOwned {
$0.readableBytes
}
}
XCTAssertEqual(fifthResult, 0)
}
func testByteBufferModifyIfUniquelyOwnedMayThrow() {
struct MyError: Error { }
func doAThrow(_ b: inout ByteBuffer) throws {
throw MyError()
}
var buffer = ByteBufferAllocator().buffer(capacity: 1)
XCTAssertThrowsError(try buffer.modifyIfUniquelyOwned(doAThrow(_:))) { error in
XCTAssertTrue(error is MyError)
}
// This can't actually throw but XCTAssertNoThrow isn't doing well here.
try! withExtendedLifetime(buffer) {
var localCopy = buffer
XCTAssertNoThrow(try localCopy.modifyIfUniquelyOwned(doAThrow(_:)))
XCTAssertNoThrow(try buffer.modifyIfUniquelyOwned(doAThrow(_:)))
}
XCTAssertThrowsError(try buffer.modifyIfUniquelyOwned(doAThrow(_:))) { error in
XCTAssertTrue(error is MyError)
}
}
@available(*, deprecated, message: "deprecated because it tests deprecated functionality")
func testDeprecatedSetBytes() {
self.buf.clear()
self.buf.writeString("hello")
self.buf.set(buffer: self.buf, at: 5)
self.buf.moveWriterIndex(forwardBy: 5)
XCTAssertEqual("hellohello", self.buf.readString(length: 10))
}
}
private enum AllocationExpectationState: Int {
case begin
case mallocDone
case reallocDone
case freeDone
}
private var testAllocationOfReallyBigByteBuffer_state = AllocationExpectationState.begin
private func testAllocationOfReallyBigByteBuffer_freeHook(_ ptr: UnsafeMutableRawPointer?) -> Void {
precondition(AllocationExpectationState.reallocDone == testAllocationOfReallyBigByteBuffer_state)
testAllocationOfReallyBigByteBuffer_state = .freeDone
/* free the pointer initially produced by malloc and then rebased by realloc offsetting it back */
free(ptr?.advanced(by: Int(Int32.max)))
}
private func testAllocationOfReallyBigByteBuffer_mallocHook(_ size: Int) -> UnsafeMutableRawPointer? {
precondition(AllocationExpectationState.begin == testAllocationOfReallyBigByteBuffer_state)
testAllocationOfReallyBigByteBuffer_state = .mallocDone
/* return a 16 byte pointer here, good enough to write an integer in there */
return malloc(16)
}
private func testAllocationOfReallyBigByteBuffer_reallocHook(_ ptr: UnsafeMutableRawPointer?, _ count: Int) -> UnsafeMutableRawPointer? {
precondition(AllocationExpectationState.mallocDone == testAllocationOfReallyBigByteBuffer_state)
testAllocationOfReallyBigByteBuffer_state = .reallocDone
/* rebase this pointer by -Int32.max so that the byte copy extending the ByteBuffer below will land at actual index 0 into this buffer ;) */
return ptr!.advanced(by: -Int(Int32.max))
}
private func testAllocationOfReallyBigByteBuffer_memcpyHook(_ dst: UnsafeMutableRawPointer, _ src: UnsafeRawPointer, _ count: Int) -> Void {
/* not actually doing any copies */
}
private var testReserveCapacityLarger_reallocCount = 0
private var testReserveCapacityLarger_mallocCount = 0
private func testReserveCapacityLarger_freeHook( _ ptr: UnsafeMutableRawPointer?) -> Void {
free(ptr)
}
private func testReserveCapacityLarger_mallocHook(_ size: Int) -> UnsafeMutableRawPointer? {
testReserveCapacityLarger_mallocCount += 1
return malloc(size)
}
private func testReserveCapacityLarger_reallocHook(_ ptr: UnsafeMutableRawPointer?, _ count: Int) -> UnsafeMutableRawPointer? {
testReserveCapacityLarger_reallocCount += 1
return realloc(ptr, count)
}
private func testReserveCapacityLarger_memcpyHook(_ dst: UnsafeMutableRawPointer, _ src: UnsafeRawPointer, _ count: Int) -> Void {
// No copying
}
extension ByteBuffer {
func storagePointerIntegerValue() -> UInt {
var pointer: UInt = 0
self.withUnsafeReadableBytes { ptr in
pointer = UInt(bitPattern: ptr.baseAddress!)
}
return pointer
}
}