From 95ae01f97ae2b24cb356a37c83654892a874e37b Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 18 Sep 2026 12:10:16 +0200 Subject: [PATCH] Adopted `Array(capacity:)` for the read wrappers throughout the library to adapt to Swift 6.4. --- Sources/PlaydateKit/File/Classes/Handle.swift | 14 ++++++++------ .../Network/Classes/HTTPConnection.swift | 18 ++++++++++-------- .../Network/Classes/TCPConnection.swift | 17 +++++++++-------- Tests/PlaydateKit/MockPlaydate.swift | 9 +++++++-- Tests/PlaydateKit/WrapperTests.swift | 13 +++++++++++++ 5 files changed, 47 insertions(+), 24 deletions(-) diff --git a/Sources/PlaydateKit/File/Classes/Handle.swift b/Sources/PlaydateKit/File/Classes/Handle.swift index 357d5c1..15115bb 100644 --- a/Sources/PlaydateKit/File/Classes/Handle.swift +++ b/Sources/PlaydateKit/File/Classes/Handle.swift @@ -41,13 +41,15 @@ extension File { /// Reads up to `length` bytes and returns them. public func read(length: Int) throws(PlaydateError) -> [UInt8] { - var bytes = [UInt8](repeating: 0, count: length) - let result = bytes.withUnsafeMutableBytes { buffer in - fileAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count)) + try [UInt8](capacity: length) { output throws(PlaydateError) in + let result = output.withUnsafeMutableBufferPointer { buffer, initializedCount in + let result = fileAPI.pointee.read.unsafelyUnwrapped( + pointer, buffer.baseAddress, UInt32(buffer.count)) + initializedCount = max(Int(result), 0) + return result + } + if result < 0 { throw lastFileError() } } - if result < 0 { throw lastFileError() } - bytes.removeLast(length - Int(result)) - return bytes } /// Writes the bytes to the file. Returns the number of bytes written. diff --git a/Sources/PlaydateKit/Network/Classes/HTTPConnection.swift b/Sources/PlaydateKit/Network/Classes/HTTPConnection.swift index ed967d9..efb9d51 100644 --- a/Sources/PlaydateKit/Network/Classes/HTTPConnection.swift +++ b/Sources/PlaydateKit/Network/Classes/HTTPConnection.swift @@ -164,15 +164,17 @@ extension Network { /// Reads up to `length` available response bytes. public func read(length: Int) throws(NetError) -> [UInt8] { - var bytes = [UInt8](repeating: 0, count: length) - let result = bytes.withUnsafeMutableBytes { buffer in - httpAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count)) + try [UInt8](capacity: length) { output throws(NetError) in + let result = output.withUnsafeMutableBufferPointer { buffer, initializedCount in + let result = httpAPI.pointee.read.unsafelyUnwrapped( + pointer, buffer.baseAddress, UInt32(buffer.count)) + initializedCount = max(Int(result), 0) + return result + } + if result < 0 { + throw NetError(rawValue: result) ?? .unknown + } } - if result < 0 { - throw NetError(rawValue: result) ?? .unknown - } - bytes.removeLast(length - Int(result)) - return bytes } /// Closes the connection. diff --git a/Sources/PlaydateKit/Network/Classes/TCPConnection.swift b/Sources/PlaydateKit/Network/Classes/TCPConnection.swift index 8814154..d093c6b 100644 --- a/Sources/PlaydateKit/Network/Classes/TCPConnection.swift +++ b/Sources/PlaydateKit/Network/Classes/TCPConnection.swift @@ -124,15 +124,16 @@ extension Network { /// Reads up to `length` bytes, waiting up to the read timeout. public func read(length: Int) throws(NetError) -> [UInt8] { - var bytes = [UInt8](repeating: 0, count: length) - let result = bytes.withUnsafeMutableBytes { buffer in - tcpAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, buffer.count) + try [UInt8](capacity: length) { output throws(NetError) in + let result = output.withUnsafeMutableBufferPointer { buffer, initializedCount in + let result = tcpAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, buffer.count) + initializedCount = max(Int(result), 0) + return result + } + if result < 0 { + throw NetError(rawValue: result) ?? .unknown + } } - if result < 0 { - throw NetError(rawValue: result) ?? .unknown - } - bytes.removeLast(length - Int(result)) - return bytes } /// Writes the bytes to the connection. Returns the number of bytes diff --git a/Tests/PlaydateKit/MockPlaydate.swift b/Tests/PlaydateKit/MockPlaydate.swift index 8d21f1a..1420be4 100644 --- a/Tests/PlaydateKit/MockPlaydate.swift +++ b/Tests/PlaydateKit/MockPlaydate.swift @@ -35,6 +35,9 @@ enum Mock { nonisolated(unsafe) static var buttonState: (current: UInt32, pushed: UInt32, released: UInt32) = (0, 0, 0) /// The 16 bytes behind the last pattern `LCDColor` seen by a stub. nonisolated(unsafe) static var patternBytes: [UInt8] = [] + /// Caps the bytes a file read returns (0 = end of file, negative = + /// error); `nil` fills the whole request. + nonisolated(unsafe) static var fileReadLimit: Int32? /// Userdata stored per sprite / menu item, as the OS would keep it. nonisolated(unsafe) static var spriteUserdata: [OpaquePointer: UnsafeMutableRawPointer] = [:] nonisolated(unsafe) static var menuUserdata: [OpaquePointer: UnsafeMutableRawPointer] = [:] @@ -83,6 +86,7 @@ enum Mock { events = [] buttonState = (0, 0, 0) patternBytes = [] + fileReadLimit = nil spriteUserdata = [:] menuUserdata = [:] menuCallback = nil @@ -400,9 +404,10 @@ enum Mock { return 0 } fileAPI.pointee.read = { _, buffer, length in - memset(buffer, 0xAB, Int(length)) Mock.record("read(\(length))") - return Int32(length) + let count = min(Int32(length), Mock.fileReadLimit ?? Int32(length)) + if count > 0 { memset(buffer, 0xAB, Int(count)) } + return count } fileAPI.pointee.write = { _, _, length in Mock.record("write(\(length))") diff --git a/Tests/PlaydateKit/WrapperTests.swift b/Tests/PlaydateKit/WrapperTests.swift index ac2bcc7..df5cf3f 100644 --- a/Tests/PlaydateKit/WrapperTests.swift +++ b/Tests/PlaydateKit/WrapperTests.swift @@ -345,6 +345,19 @@ struct WrapperTests { #expect(Mock.eventCount("close") == 1) } + @Test func fileHandleReadLengthReturnsOnlyTheBytesRead() throws { + let handle = try File.Handle(path: "save.dat", mode: [.read, .readData]) + + Mock.fileReadLimit = 3 + #expect(try handle.read(length: 8) == [0xAB, 0xAB, 0xAB]) + + Mock.fileReadLimit = 0 + #expect(try handle.read(length: 8).isEmpty) + + Mock.fileReadLimit = -1 + #expect(throws: PlaydateError.self) { try handle.read(length: 8) } + } + // MARK: JSON @Test func jsonDecodeBuildsTheValueTree() throws {