Swift 6.4 migration and exhancements (#1)

This PR contains the work done to update the library and the attached example project to use the Swift 6.4 computer as a minimum supported version and also, to use the latest features introduced in it.

Reviewed-on: #1
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-09-18 13:15:08 +00:00
committed by javier
parent d0a561b91f
commit c5037dd716
105 changed files with 1390 additions and 1397 deletions
+45 -45
View File
@@ -1,15 +1,14 @@
internal import CPlaydate
extension File {
/// An open file. Wraps `SDFile`. The file is closed on deinit if it has
/// not been closed explicitly.
public final class Handle {
/// An open file. Wraps `SDFile`. Non-copyable: closes when the handle goes out of
/// scope, or earlier via consuming `close()`. At most 64 files may be open.
public struct Handle: ~Copyable {
let pointer: UnsafeMutableRawPointer
private var isClosed = false
/// Opens the file at `path`.
/// Opens the file at `path` in `mode`.
public init(path: String, mode: Options) throws(PlaydateError) {
let pointer = path.withPlaydateCString {
let pointer = path.withCString {
fileAPI.pointee.open.unsafelyUnwrapped($0, mode.cValue)
}
guard let pointer else { throw lastFileError() }
@@ -17,68 +16,69 @@ extension File {
}
deinit {
if !isClosed {
_ = fileAPI.pointee.close.unsafelyUnwrapped(pointer)
}
_ = fileAPI.pointee.close.unsafelyUnwrapped(pointer)
}
/// Closes the file. Further operations are invalid.
public func close() throws(PlaydateError) {
guard !isClosed else { return }
isClosed = true
/// Closes the file, consuming the handle.
// `@export(interface)` lets `discard` compile in Embedded Swift on the 6.4 toolchain.
@export(interface)
public consuming func close() throws(PlaydateError) {
let pointer = self.pointer
discard self
if fileAPI.pointee.close.unsafelyUnwrapped(pointer) != 0 { throw lastFileError() }
}
/// Reads up to `buffer.count` bytes into `buffer`. Returns the number
/// of bytes read; 0 indicates end of file.
public func read(into buffer: UnsafeMutableRawBufferPointer) throws(PlaydateError) -> Int {
let result = fileAPI.pointee.read.unsafelyUnwrapped(
pointer, buffer.baseAddress, UInt32(buffer.count))
if result < 0 { throw lastFileError() }
return Int(result)
}
/// 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
/// Reads up to `buffer.count` bytes; returns the count read, 0 at end of file.
public func read(into buffer: inout MutableSpan<UInt8>) throws(PlaydateError) -> Int {
let result = buffer.withUnsafeMutableBufferPointer { buffer in
fileAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
}
if result < 0 { throw lastFileError() }
bytes.removeLast(length - Int(result))
return bytes
}
/// Writes the buffer to the file. Returns the number of bytes written.
@discardableResult
public func write(_ buffer: UnsafeRawBufferPointer) throws(PlaydateError) -> Int {
let result = fileAPI.pointee.write.unsafelyUnwrapped(
pointer, buffer.baseAddress, UInt32(buffer.count))
if result < 0 { throw lastFileError() }
return Int(result)
}
/// Writes the bytes to the file. Returns the number of bytes written.
/// Reads up to `length` bytes; shorter near end of file, empty at it.
public func read(length: Int) throws(PlaydateError) -> [UInt8] {
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() }
}
}
/// Writes `bytes`; returns the count written.
@discardableResult
public func write(_ bytes: [UInt8]) throws(PlaydateError) -> Int {
let result = bytes.withUnsafeBytes { buffer in
public func write(_ bytes: Span<UInt8>) throws(PlaydateError) -> Int {
let result = bytes.withUnsafeBufferPointer { buffer in
fileAPI.pointee.write.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
}
if result < 0 { throw lastFileError() }
return Int(result)
}
/// Writes the string's UTF-8 to the file. Returns the bytes written.
/// Writes `bytes`; returns the count written.
@discardableResult
public func write(_ bytes: [UInt8]) throws(PlaydateError) -> Int {
try bytes.withUnsafeBufferPointer { buffer throws(PlaydateError) in
try write(buffer.span)
}
}
/// Writes `string` as UTF-8, without a NUL terminator; returns the count written.
@discardableResult
public func write(_ string: String) throws(PlaydateError) -> Int {
let result = string.withPlaydateUTF8 { bytes, count in
fileAPI.pointee.write.unsafelyUnwrapped(pointer, bytes, UInt32(count))
let result = string.withCString { cString in
fileAPI.pointee.write.unsafelyUnwrapped(pointer, cString, UInt32(string.utf8.count))
}
if result < 0 { throw lastFileError() }
return Int(result)
}
/// Flushes buffered writes to disk. Returns the bytes written.
/// Flushes buffered writes; returns the count written.
@discardableResult
public func flush() throws(PlaydateError) -> Int {
let result = fileAPI.pointee.flush.unsafelyUnwrapped(pointer)
@@ -86,14 +86,14 @@ extension File {
return Int(result)
}
/// The current read/write offset.
/// The current read/write offset, in bytes.
public func tell() throws(PlaydateError) -> Int {
let result = fileAPI.pointee.tell.unsafelyUnwrapped(pointer)
if result < 0 { throw lastFileError() }
return Int(result)
}
/// Moves the read/write offset to `offset` relative to `origin`.
/// Moves the read/write offset to `offset` bytes from `origin`.
public func seek(to offset: Int, from origin: SeekOrigin = .start) throws(PlaydateError) {
if fileAPI.pointee.seek.unsafelyUnwrapped(pointer, Int32(offset), origin.rawValue) != 0 {
throw lastFileError()