Skip to content

Commit e9886bf

Browse files
committed
Merge branch 'main' into lorentey/post-back-ios15-system
# Conflicts: # Sources/System/FilePath/FilePathString.swift
2 parents ca70046 + 63cdebe commit e9886bf

18 files changed

+652
-60
lines changed

Sources/System/FileDescriptor.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ extension FileDescriptor {
107107
/// the system doesn't wait for the device or file
108108
/// to be ready or available.
109109
/// If the
110-
/// <doc:System/FileDescriptor/open(_:_:options:permissions:)-10dcs>
110+
/// <doc:FileDescriptor/open(_:_:options:permissions:retryOnInterrupt:)-2266j>
111111
/// call would result in the process being blocked for some reason,
112112
/// that method returns immediately.
113113
/// This flag also has the effect of making all
@@ -165,14 +165,14 @@ extension FileDescriptor {
165165
/// expecting that it doesn't exist.
166166
///
167167
/// If this option and ``create`` are both specified and the file exists,
168-
/// <doc:System/FileDescriptor/open(_:_:options:permissions:)-10dcs>
168+
/// <doc:FileDescriptor/open(_:_:options:permissions:retryOnInterrupt:)-2266j>
169169
/// returns an error instead of creating the file.
170170
/// You can use this, for example,
171171
/// to implement a simple exclusive-access locking mechanism.
172172
///
173173
/// If this option and ``create`` are both specified
174174
/// and the last component of the file's path is a symbolic link,
175-
/// <doc:System/FileDescriptor/open(_:_:options:permissions:)-10dcs>
175+
/// <doc:FileDescriptor/open(_:_:options:permissions:retryOnInterrupt:)-2266j>
176176
/// fails even if the symbolic link points to a nonexistent name.
177177
///
178178
/// The corresponding C constant is `O_EXCL`.
@@ -224,7 +224,7 @@ extension FileDescriptor {
224224
///
225225
/// If you specify this option
226226
/// and the file path you pass to
227-
/// <doc:System/FileDescriptor/open(_:_:options:permissions:)-10dcs>
227+
/// <doc:FileDescriptor/open(_:_:options:permissions:retryOnInterrupt:)-2266j>
228228
/// is a symbolic link,
229229
/// then that open operation fails.
230230
///
@@ -239,7 +239,7 @@ extension FileDescriptor {
239239
/// Indicates that opening the file only succeeds if the file is a directory.
240240
///
241241
/// If you specify this option and the file path you pass to
242-
/// <doc:System/FileDescriptor/open(_:_:options:permissions:)-10dcs>
242+
/// <doc:FileDescriptor/open(_:_:options:permissions:retryOnInterrupt:)-2266j>
243243
/// is a not a directory, then that open operation fails.
244244
///
245245
/// The corresponding C constant is `O_DIRECTORY`.
@@ -257,7 +257,7 @@ extension FileDescriptor {
257257
///
258258
/// If you specify this option
259259
/// and the file path you pass to
260-
/// <doc:System/FileDescriptor/open(_:_:options:permissions:)-10dcs>
260+
/// <doc:FileDescriptor/open(_:_:options:permissions:retryOnInterrupt:)-2266j>
261261
/// is a symbolic link,
262262
/// then the link itself is opened instead of what it links to.
263263
///

Sources/System/FileHelpers.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ extension FileDescriptor {
4646
/// increments that position by the number of bytes written.
4747
/// See also ``seek(offset:from:)``.
4848
///
49-
/// This method either writes the entire contents of `sequence`,
50-
/// or throws an error if only part of the content was written.
51-
///
5249
/// If `sequence` doesn't implement
5350
/// the <doc://com.apple.documentation/documentation/swift/sequence/3128824-withcontiguousstorageifavailable> method,
5451
/// temporary space will be allocated as needed.

Sources/System/FileOperations.swift

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ extension FileDescriptor {
244244
/// The <doc://com.apple.documentation/documentation/swift/unsafemutablerawbufferpointer/3019191-count> property of `buffer`
245245
/// determines the maximum number of bytes that are read into that buffer.
246246
///
247-
/// Unlike <doc:System/FileDescriptor/read(into:retryOnInterrupt:)>,
247+
/// Unlike <doc:FileDescriptor/read(into:retryOnInterrupt:)>,
248248
/// this method leaves the file's existing offset unchanged.
249249
///
250250
/// The corresponding C function is `pread`.
@@ -434,9 +434,9 @@ extension FileDescriptor {
434434
}
435435
}
436436

437+
#if !os(Windows)
437438
/*System 1.1.0, @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)*/
438439
extension FileDescriptor {
439-
#if !os(Windows)
440440
/// Create a pipe, a unidirectional data channel which can be used for interprocess communication.
441441
///
442442
/// - Returns: The pair of file descriptors.
@@ -453,10 +453,60 @@ extension FileDescriptor {
453453
internal static func _pipe() -> Result<(readEnd: FileDescriptor, writeEnd: FileDescriptor), Errno> {
454454
var fds: (Int32, Int32) = (-1, -1)
455455
return withUnsafeMutablePointer(to: &fds) { pointer in
456-
valueOrErrno(retryOnInterrupt: false) {
457-
system_pipe(UnsafeMutableRawPointer(pointer).assumingMemoryBound(to: Int32.self))
456+
pointer.withMemoryRebound(to: Int32.self, capacity: 2) { fds in
457+
valueOrErrno(retryOnInterrupt: false) {
458+
system_pipe(fds)
459+
}.map { _ in (.init(rawValue: fds[0]), .init(rawValue: fds[1])) }
458460
}
459-
}.map { _ in (.init(rawValue: fds.0), .init(rawValue: fds.1)) }
461+
}
462+
}
463+
}
464+
#endif
465+
466+
#if !os(Windows)
467+
/*System 1.2.0, @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)*/
468+
extension FileDescriptor {
469+
/// Truncate or extend the file referenced by this file descriptor.
470+
///
471+
/// - Parameters:
472+
/// - newSize: The length in bytes to resize the file to.
473+
/// - retryOnInterrupt: Whether to retry the write operation
474+
/// if it throws ``Errno/interrupted``. The default is `true`.
475+
/// Pass `false` to try only once and throw an error upon interruption.
476+
///
477+
/// The file referenced by this file descriptor will by truncated (or extended) to `newSize`.
478+
///
479+
/// If the current size of the file exceeds `newSize`, any extra data is discarded. If the current
480+
/// size of the file is smaller than `newSize`, the file is extended and filled with zeros to the
481+
/// provided size.
482+
///
483+
/// This function requires that the file has been opened for writing.
484+
///
485+
/// - Note: This function does not modify the current offset for any open file descriptors
486+
/// associated with the file.
487+
///
488+
/// The corresponding C function is `ftruncate`.
489+
/*System 1.2.0, @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)*/
490+
@_alwaysEmitIntoClient
491+
public func resize(
492+
to newSize: Int64,
493+
retryOnInterrupt: Bool = true
494+
) throws {
495+
try _resize(
496+
to: newSize,
497+
retryOnInterrupt: retryOnInterrupt
498+
).get()
499+
}
500+
501+
/*System 1.2.0, @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)*/
502+
@usableFromInline
503+
internal func _resize(
504+
to newSize: Int64,
505+
retryOnInterrupt: Bool
506+
) -> Result<(), Errno> {
507+
nothingOrErrno(retryOnInterrupt: retryOnInterrupt) {
508+
system_ftruncate(self.rawValue, _COffT(newSize))
509+
}
460510
}
461-
#endif
462511
}
512+
#endif

Sources/System/FilePath/FilePathString.swift

Lines changed: 167 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,50 @@ extension FilePath {
2020
self.init(_platformString: platformString)
2121
}
2222

23-
#if !os(Windows)
23+
/// Creates a file path by copying bytes from a null-terminated platform
24+
/// string.
25+
///
26+
/// - Note It is a precondition that `platformString` must be null-terminated.
27+
/// The absence of a null byte will trigger a runtime error.
28+
///
29+
/// - Parameter platformString: A null-terminated platform string.
30+
@inlinable
31+
@_alwaysEmitIntoClient
32+
public init(platformString: [CInterop.PlatformChar]) {
33+
guard let _ = platformString.firstIndex(of: 0) else {
34+
fatalError(
35+
"input of FilePath.init(platformString:) must be null-terminated"
36+
)
37+
}
38+
self = platformString.withUnsafeBufferPointer {
39+
FilePath(platformString: $0.baseAddress!)
40+
}
41+
}
42+
43+
@inlinable
44+
@_alwaysEmitIntoClient
45+
@available(*, deprecated, message: "Use FilePath.init(_ scalar: Unicode.Scalar)")
46+
public init(platformString: inout CInterop.PlatformChar) {
47+
guard platformString == 0 else {
48+
fatalError(
49+
"input of FilePath.init(platformString:) must be null-terminated"
50+
)
51+
}
52+
self = FilePath()
53+
}
54+
55+
@inlinable
56+
@_alwaysEmitIntoClient
57+
@available(*, deprecated, message: "Use FilePath(_: String) to create a path from a String")
58+
public init(platformString: String) {
59+
if let nullLoc = platformString.firstIndex(of: "\0") {
60+
self = FilePath(String(platformString[..<nullLoc]))
61+
} else {
62+
self = FilePath(platformString)
63+
}
64+
}
65+
66+
#if !os(Windows)
2467
// Note: This function should have been opaque, but it shipped as
2568
// `@_alwaysEmitIntoClient` in macOS 12/iOS 15, and now it is stuck
2669
// this way forever. (Or until the language provides a way for us
@@ -81,6 +124,59 @@ extension FilePath.Component {
81124
self.init(_platformString: platformString)
82125
}
83126

127+
/// Creates a file path component by copying bytes from a null-terminated
128+
/// platform string. It is a precondition that a null byte indicates the end of
129+
/// the string. The absence of a null byte will trigger a runtime error.
130+
///
131+
/// Returns `nil` if `platformString` is empty, is a root, or has more than
132+
/// one component in it.
133+
///
134+
/// - Note It is a precondition that `platformString` must be null-terminated.
135+
/// The absence of a null byte will trigger a runtime error.
136+
///
137+
/// - Parameter platformString: A null-terminated platform string.
138+
@inlinable
139+
@_alwaysEmitIntoClient
140+
public init?(platformString: [CInterop.PlatformChar]) {
141+
guard let _ = platformString.firstIndex(of: 0) else {
142+
fatalError(
143+
"input of FilePath.Component.init?(platformString:) must be null-terminated"
144+
)
145+
}
146+
guard let component = platformString.withUnsafeBufferPointer({
147+
FilePath.Component(platformString: $0.baseAddress!)
148+
}) else {
149+
return nil
150+
}
151+
self = component
152+
}
153+
154+
@inlinable
155+
@_alwaysEmitIntoClient
156+
@available(*, deprecated, message: "Use FilePath.Component.init(_ scalar: Unicode.Scalar)")
157+
public init?(platformString: inout CInterop.PlatformChar) {
158+
guard platformString == 0 else {
159+
fatalError(
160+
"input of FilePath.Component.init?(platformString:) must be null-terminated"
161+
)
162+
}
163+
return nil
164+
}
165+
166+
@inlinable
167+
@_alwaysEmitIntoClient
168+
@available(*, deprecated, message: "Use FilePath.Component.init(_: String)")
169+
public init?(platformString: String) {
170+
let string: String
171+
if let nullLoc = platformString.firstIndex(of: "\0") {
172+
string = String(platformString[..<nullLoc])
173+
} else {
174+
string = platformString
175+
}
176+
guard let component = FilePath.Component(string) else { return nil }
177+
self = component
178+
}
179+
84180
/// Calls the given closure with a pointer to the contents of the file path
85181
/// component, represented as a null-terminated platform string.
86182
///
@@ -117,6 +213,58 @@ extension FilePath.Root {
117213
self.init(_platformString: platformString)
118214
}
119215

216+
/// Creates a file path root by copying bytes from a null-terminated platform
217+
/// string. It is a precondition that a null byte indicates the end of
218+
/// the string. The absence of a null byte will trigger a runtime error.
219+
///
220+
/// Returns `nil` if `platformString` is empty or is not a root.
221+
///
222+
/// - Note It is a precondition that `platformString` must be null-terminated.
223+
/// The absence of a null byte will trigger a runtime error.
224+
///
225+
/// - Parameter platformString: A null-terminated platform string.
226+
@inlinable
227+
@_alwaysEmitIntoClient
228+
public init?(platformString: [CInterop.PlatformChar]) {
229+
guard let _ = platformString.firstIndex(of: 0) else {
230+
fatalError(
231+
"input of FilePath.Root.init?(platformString:) must be null-terminated"
232+
)
233+
}
234+
guard let component = platformString.withUnsafeBufferPointer({
235+
FilePath.Root(platformString: $0.baseAddress!)
236+
}) else {
237+
return nil
238+
}
239+
self = component
240+
}
241+
242+
@inlinable
243+
@_alwaysEmitIntoClient
244+
@available(*, deprecated, message: "Use FilePath.Root.init(_ scalar: Unicode.Scalar)")
245+
public init?(platformString: inout CInterop.PlatformChar) {
246+
guard platformString == 0 else {
247+
fatalError(
248+
"input of FilePath.Root.init?(platformString:) must be null-terminated"
249+
)
250+
}
251+
return nil
252+
}
253+
254+
@inlinable
255+
@_alwaysEmitIntoClient
256+
@available(*, deprecated, message: "Use FilePath.Root.init(_: String)")
257+
public init?(platformString: String) {
258+
let string: String
259+
if let nullLoc = platformString.firstIndex(of: "\0") {
260+
string = String(platformString[..<nullLoc])
261+
} else {
262+
string = platformString
263+
}
264+
guard let root = FilePath.Root(string) else { return nil }
265+
self = root
266+
}
267+
120268
/// Calls the given closure with a pointer to the contents of the file path
121269
/// root, represented as a null-terminated platform string.
122270
///
@@ -169,7 +317,7 @@ extension FilePath.Component: ExpressibleByStringLiteral {
169317
public init(stringLiteral: String) {
170318
guard let s = FilePath.Component(stringLiteral) else {
171319
// TODO: static assert
172-
preconditionFailure("""
320+
fatalError("""
173321
FilePath.Component must be created from exactly one non-root component
174322
""")
175323
}
@@ -193,7 +341,7 @@ extension FilePath.Root: ExpressibleByStringLiteral {
193341
public init(stringLiteral: String) {
194342
guard let s = FilePath.Root(stringLiteral) else {
195343
// TODO: static assert
196-
preconditionFailure("""
344+
fatalError("""
197345
FilePath.Root must be created from a root
198346
""")
199347
}
@@ -426,10 +574,26 @@ extension String {
426574
extension FilePath {
427575
/// For backwards compatibility only. This initializer is equivalent to
428576
/// the preferred `FilePath(platformString:)`.
577+
@available(*, deprecated, renamed: "init(platformString:)")
429578
public init(cString: UnsafePointer<CChar>) {
430579
self.init(platformString: cString)
431580
}
432581

582+
@available(*, deprecated, renamed: "init(platformString:)")
583+
public init(cString: [CChar]) {
584+
self.init(platformString: cString)
585+
}
586+
587+
@available(*, deprecated, renamed: "init(platformString:)")
588+
public init(cString: inout CChar) {
589+
self.init(platformString: &cString)
590+
}
591+
592+
@available(*, deprecated, renamed: "init(platformString:)")
593+
public init(cString: String) {
594+
self.init(platformString: cString)
595+
}
596+
433597
/// For backwards compatibility only. This function is equivalent to
434598
/// the preferred `withPlatformString`.
435599
public func withCString<Result>(

Sources/System/Internals/CInterop.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
1111
import Darwin
1212
#elseif os(Linux) || os(FreeBSD) || os(Android)
13-
import CSystem
13+
@_implementationOnly import CSystem
1414
import Glibc
1515
#elseif os(Windows)
1616
import CSystem

Sources/System/Internals/Constants.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
1515
import Darwin
1616
#elseif os(Linux) || os(FreeBSD) || os(Android)
17-
import CSystem
1817
import Glibc
1918
#elseif os(Windows)
2019
import CSystem

Sources/System/Internals/Exports.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
1616
import Darwin
1717
#elseif os(Linux) || os(FreeBSD) || os(Android)
18-
import CSystem
18+
@_implementationOnly import CSystem
1919
import Glibc
2020
#elseif os(Windows)
2121
import CSystem

0 commit comments

Comments
 (0)