diff --git a/Sources/System/FilePath/FilePath.swift b/Sources/System/FilePath/FilePath.swift index c1dfd7f3..5668405f 100644 --- a/Sources/System/FilePath/FilePath.swift +++ b/Sources/System/FilePath/FilePath.swift @@ -41,18 +41,18 @@ public struct FilePath { // TODO(docs): Section on all the new syntactic operations, lexical normalization, decomposition, // components, etc. - internal var _storage: SystemString + internal var _storage: _SystemString /// Creates an empty, null-terminated path. public init() { - self._storage = SystemString() + self._storage = _SystemString() _invariantCheck() } // In addition to the empty init, this init will properly normalize // separators. All other initializers should be implemented by // ultimately deferring to a normalizing init. - internal init(_ str: SystemString) { + internal init(_ str: _SystemString) { self._storage = str self._normalizeSeparators() _invariantCheck() diff --git a/Sources/System/FilePath/FilePathComponentView.swift b/Sources/System/FilePath/FilePathComponentView.swift index efdd084a..90168256 100644 --- a/Sources/System/FilePath/FilePathComponentView.swift +++ b/Sources/System/FilePath/FilePathComponentView.swift @@ -30,7 +30,7 @@ extension FilePath { /// // path is "/home/username/bin/scripts/tree" public struct ComponentView { internal var _path: FilePath - internal var _start: SystemString.Index + internal var _start: _SystemString.Index internal init(_ path: FilePath) { self._path = path @@ -50,7 +50,7 @@ extension FilePath { // TODO(perf): Small-form root (especially on Unix). Have Root // always copy out (not worth ref counting). Make sure that we're // not needlessly sliding values around or triggering a COW - let rootStr = self.root?._systemString ?? SystemString() + let rootStr = self.root?._systemString ?? _SystemString() var comp = ComponentView(self) self = FilePath() defer { @@ -73,7 +73,7 @@ extension FilePath { // TODO(perf): Small-form root (especially on Unix). Have Root // always copy out (not worth ref counting). Make sure that we're // not needlessly sliding values around or triggering a COW - let rootStr = self.root?._systemString ?? SystemString() + let rootStr = self.root?._systemString ?? _SystemString() var comp = ComponentView(self) self = FilePath() defer { @@ -92,7 +92,7 @@ extension FilePath { extension FilePath.ComponentView: BidirectionalCollection { public typealias Element = FilePath.Component public struct Index: Comparable, Hashable { - internal typealias Storage = SystemString.Index + internal typealias Storage = _SystemString.Index internal var _storage: Storage @@ -159,7 +159,7 @@ extension FilePath.ComponentView: RangeReplaceableCollection { // filling in the bytes ourselves. // If we're inserting at the end, we need a leading separator. - var str = SystemString() + var str = _SystemString() let atEnd = subrange.lowerBound == endIndex if atEnd { str.append(platformSeparator) @@ -178,7 +178,7 @@ extension FilePath { public init( root: Root?, _ components: C ) where C.Element == Component { - var str = root?._systemString ?? SystemString() + var str = root?._systemString ?? _SystemString() str.appendComponents(components: components) self.init(str) } @@ -191,7 +191,7 @@ extension FilePath { /// Create a file path from an optional root and a slice of another path's /// components. public init(root: Root?, _ components: ComponentView.SubSequence) { - var str = root?._systemString ?? SystemString() + var str = root?._systemString ?? _SystemString() let (start, end) = (components.startIndex._storage, components.endIndex._storage) str.append(contentsOf: components.base._slice[start.. { + public var _range: Range<_SystemString.Index> { _start ..< _path._storage.endIndex } - internal init(_ str: SystemString) { + public init(_ str: _SystemString) { fatalError("TODO: consider dropping proto req") } } @@ -216,7 +216,7 @@ extension FilePath.ComponentView: _PathSlice { @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension FilePath.ComponentView { - internal func _invariantCheck() { + public func _invariantCheck() { #if DEBUG if isEmpty { precondition(_path.isEmpty == (_path.root == nil)) diff --git a/Sources/System/FilePath/FilePathComponents.swift b/Sources/System/FilePath/FilePathComponents.swift index ecd713ea..b8d47e45 100644 --- a/Sources/System/FilePath/FilePathComponents.swift +++ b/Sources/System/FilePath/FilePathComponents.swift @@ -30,9 +30,9 @@ extension FilePath { /// * `\\?\Volume{12345678-abcd-1111-2222-123445789abc}\` public struct Root { internal var _path: FilePath - internal var _rootEnd: SystemString.Index + internal var _rootEnd: _SystemString.Index - internal init(_ path: FilePath, rootEnd: SystemString.Index) { + internal init(_ path: FilePath, rootEnd: _SystemString.Index) { self._path = path self._rootEnd = rootEnd _invariantCheck() @@ -56,14 +56,14 @@ extension FilePath { /// path.append(file) // path is "/tmp/foo.txt" public struct Component { internal var _path: FilePath - internal var _range: Range + public var _range: Range<_SystemString.Index> // TODO: Make a small-component form to save on ARC overhead when // extracted from a path, and especially to save on allocation overhead // when constructing one from a String literal. internal init(_ path: FilePath, _ range: RE) - where RE.Bound == SystemString.Index { + where RE.Bound == _SystemString.Index { self._path = path self._range = range.relative(to: path._storage) precondition(!self._range.isEmpty, "FilePath components cannot be empty") @@ -104,7 +104,7 @@ extension FilePath.Root { // MARK: - Internals -extension SystemString { +extension _SystemString { // TODO: take insertLeadingSlash: Bool // TODO: turn into an insert operation with slide internal mutating func appendComponents( @@ -127,21 +127,21 @@ extension SystemString { // Unifying protocol for common functionality between roots, components, // and views onto SystemString and FilePath. -internal protocol _StrSlice: _PlatformStringable, Hashable, Codable { - var _storage: SystemString { get } - var _range: Range { get } +public protocol _StrSlice: _PlatformStringable, Hashable, Codable { + var _storage: _SystemString { get } + var _range: Range<_SystemString.Index> { get } - init?(_ str: SystemString) + init?(_ str: _SystemString) func _invariantCheck() } extension _StrSlice { - internal var _slice: Slice { + internal var _slice: Slice<_SystemString> { Slice(base: _storage, bounds: _range) } internal func _withSystemChars( - _ f: (UnsafeBufferPointer) throws -> T + _ f: (UnsafeBufferPointer<_SystemChar>) throws -> T ) rethrows -> T { try _storage.withSystemChars { try f(UnsafeBufferPointer(rebasing: $0[_range])) @@ -153,17 +153,17 @@ extension _StrSlice { try _slice.withCodeUnits(f) } - internal init?(_platformString s: UnsafePointer) { - self.init(SystemString(platformString: s)) + public init?(_platformString s: UnsafePointer) { + self.init(_SystemString(platformString: s)) } - internal func _withPlatformString( + public func _withPlatformString( _ body: (UnsafePointer) throws -> Result ) rethrows -> Result { try _slice.withPlatformString(body) } - internal var _systemString: SystemString { SystemString(_slice) } + internal var _systemString: _SystemString { _SystemString(_slice) } } extension _StrSlice { public static func == (lhs: Self, rhs: Self) -> Bool { @@ -180,7 +180,7 @@ internal protocol _PathSlice: _StrSlice { var _path: FilePath { get } } extension _PathSlice { - internal var _storage: SystemString { _path._storage } + public var _storage: _SystemString { _path._storage } } @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) @@ -188,19 +188,19 @@ extension FilePath.Component: _PathSlice { } @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension FilePath.Root: _PathSlice { - internal var _range: Range { + public var _range: Range<_SystemString.Index> { (..<_rootEnd).relative(to: _path._storage) } } @available(/*System 0.0.1: macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0*/iOS 8, *) extension FilePath: _PlatformStringable { - func _withPlatformString(_ body: (UnsafePointer) throws -> Result) rethrows -> Result { + public func _withPlatformString(_ body: (UnsafePointer) throws -> Result) rethrows -> Result { try _storage.withPlatformString(body) } - init(_platformString: UnsafePointer) { - self.init(SystemString(platformString: _platformString)) + public init(_platformString: UnsafePointer) { + self.init(_SystemString(platformString: _platformString)) } } @@ -208,7 +208,7 @@ extension FilePath: _PlatformStringable { @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension FilePath.Component { // The index of the `.` denoting an extension - internal func _extensionIndex() -> SystemString.Index? { + internal func _extensionIndex() -> _SystemString.Index? { guard kind == .regular, let idx = _slice.lastIndex(of: .dot), idx != _slice.startIndex @@ -217,26 +217,26 @@ extension FilePath.Component { return idx } - internal func _extensionRange() -> Range? { + internal func _extensionRange() -> Range<_SystemString.Index>? { guard let idx = _extensionIndex() else { return nil } return _slice.index(after: idx) ..< _slice.endIndex } - internal func _stemRange() -> Range { + internal func _stemRange() -> Range<_SystemString.Index> { _slice.startIndex ..< (_extensionIndex() ?? _slice.endIndex) } } -internal func _makeExtension(_ ext: String) -> SystemString { - var result = SystemString() +internal func _makeExtension(_ ext: String) -> _SystemString { + var result = _SystemString() result.append(.dot) - result.append(contentsOf: ext.unicodeScalars.lazy.map(SystemChar.init)) + result.append(contentsOf: ext.unicodeScalars.lazy.map(_SystemChar.init)) return result } @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension FilePath.Component { - internal init?(_ str: SystemString) { + public init?(_ str: _SystemString) { // FIXME: explicit null root? Or something else? let path = FilePath(str) guard path.root == nil, path.components.count == 1 else { @@ -249,7 +249,7 @@ extension FilePath.Component { @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension FilePath.Root { - internal init?(_ str: SystemString) { + public init?(_ str: _SystemString) { // FIXME: explicit null root? Or something else? let path = FilePath(str) guard path.root != nil, path.components.isEmpty else { @@ -265,7 +265,7 @@ extension FilePath.Root { @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension FilePath.Component { // TODO: ensure this all gets easily optimized away in release... - internal func _invariantCheck() { + public func _invariantCheck() { #if DEBUG precondition(!_slice.isEmpty) precondition(_slice.last != .null) @@ -277,7 +277,7 @@ extension FilePath.Component { @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension FilePath.Root { - internal func _invariantCheck() { + public func _invariantCheck() { #if DEBUG precondition(self._rootEnd > _path._storage.startIndex) diff --git a/Sources/System/FilePath/FilePathParsing.swift b/Sources/System/FilePath/FilePathParsing.swift index 6d014774..eb18455b 100644 --- a/Sources/System/FilePath/FilePathParsing.swift +++ b/Sources/System/FilePath/FilePathParsing.swift @@ -11,30 +11,30 @@ // manages (and hides) the null terminator // The separator we use internally -private var genericSeparator: SystemChar { .slash } +private var genericSeparator: _SystemChar { .slash } // The platform preferred separator // // TODO: Make private -internal var platformSeparator: SystemChar { +internal var platformSeparator: _SystemChar { _windowsPaths ? .backslash : genericSeparator } // Whether the character is the canonical separator // TODO: Make private -internal func isSeparator(_ c: SystemChar) -> Bool { +internal func isSeparator(_ c: _SystemChar) -> Bool { c == platformSeparator } // Whether the character is a pre-normalized separator -internal func isPrenormalSeparator(_ c: SystemChar) -> Bool { +internal func isPrenormalSeparator(_ c: _SystemChar) -> Bool { c == genericSeparator || c == platformSeparator } // Separator normalization, checking, and root parsing is internally hosted // on SystemString for ease of unit testing. -extension SystemString { +extension _SystemString { // For invariant enforcing/checking. Should always return false on // a fully-formed path fileprivate func _hasTrailingSeparator() -> Bool { @@ -186,7 +186,7 @@ extension FilePath { } } -extension SystemString { +extension _SystemString { internal var _relativePathStart: Index { _parseRoot().relativeBegin } @@ -194,7 +194,7 @@ extension SystemString { @available(/*System 0.0.1: macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0*/iOS 8, *) extension FilePath { - internal var _relativeStart: SystemString.Index { + internal var _relativeStart: _SystemString.Index { _storage._relativePathStart } internal var _hasRoot: Bool { @@ -206,7 +206,7 @@ extension FilePath { @available(/*System 0.0.1: macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0*/iOS 8, *) extension FilePath { - internal typealias _Index = SystemString.Index + internal typealias _Index = _SystemString.Index // Parse a component that starts at `i`. Returns the end // of the component and the start of the next. Parsing terminates @@ -271,12 +271,12 @@ extension FilePath { @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension FilePath.ComponentView { // TODO: Store this... - internal var _relativeStart: SystemString.Index { + internal var _relativeStart: _SystemString.Index { _path._relativeStart } } -extension SystemString { +extension _SystemString { internal func _parseRoot() -> ( rootEnd: Index, relativeBegin: Index ) { @@ -303,7 +303,7 @@ extension FilePath.Root { // // TODO: public internal var isAbsolute: Bool { - assert(FilePath(SystemString(self._slice)).root == self, "not a root") + assert(FilePath(_SystemString(self._slice)).root == self, "not a root") guard _windowsPaths else { return true } @@ -354,8 +354,8 @@ extension FilePath { // Perform an append, inseting a separator if needed. // Note that this will not check whether `content` is a root - internal mutating func _append(unchecked content: Slice) { - assert(FilePath(SystemString(content)).root == nil) + internal mutating func _append(unchecked content: Slice<_SystemString>) { + assert(FilePath(_SystemString(content)).root == nil) if content.isEmpty { return } if _needsSeparatorForAppend { _storage.append(platformSeparator) diff --git a/Sources/System/FilePath/FilePathString.swift b/Sources/System/FilePath/FilePathString.swift index 4fe9c1fd..a14cae7a 100644 --- a/Sources/System/FilePath/FilePathString.swift +++ b/Sources/System/FilePath/FilePathString.swift @@ -302,7 +302,7 @@ extension FilePath: ExpressibleByStringLiteral { /// - Parameter string: A string /// whose Unicode encoded contents to use as the contents of the path. public init(_ string: String) { - self.init(SystemString(string)) + self.init(_SystemString(string)) } } @@ -327,7 +327,7 @@ extension FilePath.Component: ExpressibleByStringLiteral { /// Returns `nil` if `string` is empty, a root, or has more than one component /// in it. public init?(_ string: String) { - self.init(SystemString(string)) + self.init(_SystemString(string)) } } @@ -350,7 +350,7 @@ extension FilePath.Root: ExpressibleByStringLiteral { /// /// Returns `nil` if `string` is empty or is not a root. public init?(_ string: String) { - self.init(SystemString(string)) + self.init(_SystemString(string)) } } diff --git a/Sources/System/FilePath/FilePathSyntax.swift b/Sources/System/FilePath/FilePathSyntax.swift index 1c3fc097..a9ccdecf 100644 --- a/Sources/System/FilePath/FilePathSyntax.swift +++ b/Sources/System/FilePath/FilePathSyntax.swift @@ -320,11 +320,11 @@ extension FilePath { defer { _invariantCheck() } guard let base = lastComponent, base.kind == .regular else { return } - let suffix: SystemString + let suffix: _SystemString if let ext = newValue { suffix = _makeExtension(ext) } else { - suffix = SystemString() + suffix = _SystemString() } let extRange = ( diff --git a/Sources/System/FilePath/FilePathWindows.swift b/Sources/System/FilePath/FilePathWindows.swift index b725dd17..ac1332e8 100644 --- a/Sources/System/FilePath/FilePathWindows.swift +++ b/Sources/System/FilePath/FilePathWindows.swift @@ -8,24 +8,24 @@ */ internal struct _ParsedWindowsRoot { - var rootEnd: SystemString.Index + var rootEnd: _SystemString.Index // TODO: Remove when I normalize to always (except `C:`) // have trailing separator - var relativeBegin: SystemString.Index + var relativeBegin: _SystemString.Index - var drive: SystemChar? + var drive: _SystemChar? var fullyQualified: Bool - var deviceSigil: SystemChar? + var deviceSigil: _SystemChar? - var host: Range? - var volume: Range? + var host: Range<_SystemString.Index>? + var volume: Range<_SystemString.Index>? } extension _ParsedWindowsRoot { static func traditional( - drive: SystemChar?, fullQualified: Bool, endingAt idx: SystemString.Index + drive: _SystemChar?, fullQualified: Bool, endingAt idx: _SystemString.Index ) -> _ParsedWindowsRoot { _ParsedWindowsRoot( rootEnd: idx, @@ -38,11 +38,11 @@ extension _ParsedWindowsRoot { } static func unc( - deviceSigil: SystemChar?, - server: Range, - share: Range, - endingAt end: SystemString.Index, - relativeBegin relBegin: SystemString.Index + deviceSigil: _SystemChar?, + server: Range<_SystemString.Index>, + share: Range<_SystemString.Index>, + endingAt end: _SystemString.Index, + relativeBegin relBegin: _SystemString.Index ) -> _ParsedWindowsRoot { _ParsedWindowsRoot( rootEnd: end, @@ -55,10 +55,10 @@ extension _ParsedWindowsRoot { } static func device( - deviceSigil: SystemChar, - volume: Range, - endingAt end: SystemString.Index, - relativeBegin relBegin: SystemString.Index + deviceSigil: _SystemChar, + volume: Range<_SystemString.Index>, + endingAt end: _SystemString.Index, + relativeBegin relBegin: _SystemString.Index ) -> _ParsedWindowsRoot { _ParsedWindowsRoot( rootEnd: end, @@ -72,13 +72,13 @@ extension _ParsedWindowsRoot { } struct _Lexer { - var slice: Slice + var slice: Slice<_SystemString> - init(_ str: SystemString) { + init(_ str: _SystemString) { self.slice = str[...] } - var backslash: SystemChar { .backslash } + var backslash: _SystemChar { .backslash } // Try to eat a backslash, returns false if nothing happened mutating func eatBackslash() -> Bool { @@ -86,7 +86,7 @@ struct _Lexer { } // Try to consume a drive letter and subsequent `:`. - mutating func eatDrive() -> SystemChar? { + mutating func eatDrive() -> _SystemChar? { let copy = slice if let d = slice._eat(if: { $0.isLetter }), slice._eat(.colon) != nil { return d @@ -97,7 +97,7 @@ struct _Lexer { } // Try to consume a device sigil (stand-alone . or ?) - mutating func eatSigil() -> SystemChar? { + mutating func eatSigil() -> _SystemChar? { let copy = slice guard let sigil = slice._eat(.question) ?? slice._eat(.dot) else { return nil @@ -114,11 +114,11 @@ struct _Lexer { // Try to consume an explicit "UNC" directory mutating func eatUNC() -> Bool { - slice._eatSequence("UNC".unicodeScalars.lazy.map { SystemChar(ascii: $0) }) != nil + slice._eatSequence("UNC".unicodeScalars.lazy.map { _SystemChar(ascii: $0) }) != nil } // Eat everything up to but not including a backslash or null - mutating func eatComponent() -> Range { + mutating func eatComponent() -> Range<_SystemString.Index> { let backslash = self.backslash let component = slice._eatWhile({ $0 != backslash }) ?? slice[slice.startIndex ..< slice.startIndex] @@ -129,14 +129,14 @@ struct _Lexer { return slice.isEmpty } - var current: SystemString.Index { slice.startIndex } + var current: _SystemString.Index { slice.startIndex } mutating func clear() { // TODO: Intern empty system string - self = _Lexer(SystemString()) + self = _Lexer(_SystemString()) } - mutating func reset(to: SystemString, at: SystemString.Index) { + mutating func reset(to: _SystemString, at: _SystemString.Index) { self.slice = to[at...] } } @@ -209,7 +209,7 @@ internal struct WindowsRootInfo { } extension _ParsedWindowsRoot { - fileprivate func volumeInfo(_ root: SystemString) -> WindowsRootInfo.Volume { + fileprivate func volumeInfo(_ root: _SystemString) -> WindowsRootInfo.Volume { if let d = self.drive { return .drive(Character(d.asciiScalar!)) } @@ -223,7 +223,7 @@ extension _ParsedWindowsRoot { } extension WindowsRootInfo { - internal init(_ root: SystemString, _ parsed: _ParsedWindowsRoot) { + internal init(_ root: _SystemString, _ parsed: _ParsedWindowsRoot) { self.volume = parsed.volumeInfo(root) if let host = parsed.host { @@ -308,7 +308,7 @@ extension WindowsRootInfo { } -extension SystemString { +extension _SystemString { // TODO: Or, should I always inline this to remove some of the bookeeping? private func _parseWindowsRootInternal() -> _ParsedWindowsRoot? { assert(_windowsPaths) @@ -334,7 +334,7 @@ extension SystemString { var lexer = _Lexer(self) // Helper to parse a UNC root - func parseUNC(deviceSigil: SystemChar?) -> _ParsedWindowsRoot { + func parseUNC(deviceSigil: _SystemChar?) -> _ParsedWindowsRoot { let serverRange = lexer.eatComponent() guard lexer.eatBackslash() else { fatalError("expected normalized root to contain backslash") @@ -394,7 +394,7 @@ extension SystemString { @inline(never) internal func _parseWindowsRoot() -> ( - rootEnd: SystemString.Index, relativeBegin: SystemString.Index + rootEnd: _SystemString.Index, relativeBegin: _SystemString.Index ) { guard let parsed = _parseWindowsRootInternal() else { return (startIndex, startIndex) @@ -403,7 +403,7 @@ extension SystemString { } } -extension SystemString { +extension _SystemString { // UNC and device roots can have multiple repeated roots that are meaningful, // and extra backslashes may need to be inserted for partial roots (e.g. empty // volume). diff --git a/Sources/System/Internals/Exports.swift b/Sources/System/Internals/Exports.swift index 57abc6fc..9d485bff 100644 --- a/Sources/System/Internals/Exports.swift +++ b/Sources/System/Internals/Exports.swift @@ -101,7 +101,7 @@ internal func system_memset( // Interop between String and platfrom string extension String { - internal func _withPlatformString( + public func _withPlatformString( _ body: (UnsafePointer) throws -> Result ) rethrows -> Result { // Need to #if because CChar may be signed @@ -112,7 +112,7 @@ extension String { #endif } - internal init?(_platformString platformString: UnsafePointer) { + public init?(_platformString platformString: UnsafePointer) { // Need to #if because CChar may be signed #if os(Windows) guard let strRes = String.decodeCString( diff --git a/Sources/System/PlatformString.swift b/Sources/System/PlatformString.swift index 4e2e7ddf..b0a9fd83 100644 --- a/Sources/System/PlatformString.swift +++ b/Sources/System/PlatformString.swift @@ -186,7 +186,7 @@ extension CInterop.PlatformUnicodeEncoding.CodeUnit { } } -internal protocol _PlatformStringable { +public protocol _PlatformStringable { func _withPlatformString( _ body: (UnsafePointer) throws -> Result ) rethrows -> Result diff --git a/Sources/System/SystemString.swift b/Sources/System/SystemString.swift index 14651d52..d3145f55 100644 --- a/Sources/System/SystemString.swift +++ b/Sources/System/SystemString.swift @@ -8,21 +8,21 @@ */ // A platform-native character representation, currently used for file paths -internal struct SystemChar: RawRepresentable, Comparable, Hashable, Codable { - internal typealias RawValue = CInterop.PlatformChar +public struct _SystemChar: RawRepresentable, Comparable, Hashable, Codable { + public typealias RawValue = CInterop.PlatformChar - internal var rawValue: RawValue + public var rawValue: RawValue - internal init(rawValue: RawValue) { self.rawValue = rawValue } + public init(rawValue: RawValue) { self.rawValue = rawValue } - internal init(_ rawValue: RawValue) { self.init(rawValue: rawValue) } + public init(_ rawValue: RawValue) { self.init(rawValue: rawValue) } - static func < (lhs: SystemChar, rhs: SystemChar) -> Bool { + public static func < (lhs: _SystemChar, rhs: _SystemChar) -> Bool { lhs.rawValue < rhs.rawValue } } -extension SystemChar { +extension _SystemChar { internal init(ascii: Unicode.Scalar) { self.init(rawValue: numericCast(UInt8(ascii: ascii))) } @@ -30,12 +30,12 @@ extension SystemChar { self.init(rawValue: codeUnit._platformChar) } - internal static var null: SystemChar { SystemChar(0x0) } - internal static var slash: SystemChar { SystemChar(ascii: "/") } - internal static var backslash: SystemChar { SystemChar(ascii: #"\"#) } - internal static var dot: SystemChar { SystemChar(ascii: ".") } - internal static var colon: SystemChar { SystemChar(ascii: ":") } - internal static var question: SystemChar { SystemChar(ascii: "?") } + internal static var null: _SystemChar { _SystemChar(0x0) } + internal static var slash: _SystemChar { _SystemChar(ascii: "/") } + internal static var backslash: _SystemChar { _SystemChar(ascii: #"\"#) } + internal static var dot: _SystemChar { _SystemChar(ascii: ".") } + internal static var colon: _SystemChar { _SystemChar(ascii: ":") } + internal static var question: _SystemChar { _SystemChar(ascii: "?") } internal var codeUnit: CInterop.PlatformUnicodeEncoding.CodeUnit { rawValue._platformCodeUnit @@ -61,13 +61,14 @@ extension SystemChar { // A platform-native string representation, currently for file paths // // Always null-terminated. -internal struct SystemString { - internal typealias Storage = [SystemChar] + +public struct _SystemString { + public typealias Storage = [_SystemChar] internal var nullTerminatedStorage: Storage } -extension SystemString { - internal init() { +extension _SystemString { + public init() { self.nullTerminatedStorage = [.null] _invariantCheck() } @@ -85,7 +86,7 @@ extension SystemString { } // Ensures that result is null-terminated - internal init(_ chars: C) where C.Element == SystemChar { + internal init(_ chars: C) where C.Element == _SystemChar { var rawChars = Storage(chars) if rawChars.last != .null { rawChars.append(.null) @@ -94,7 +95,7 @@ extension SystemString { } } -extension SystemString { +extension _SystemString { fileprivate func _invariantCheck() { #if DEBUG precondition(nullTerminatedStorage.last! == .null) @@ -103,20 +104,20 @@ extension SystemString { } } -extension SystemString: RandomAccessCollection, MutableCollection { - internal typealias Element = SystemChar - internal typealias Index = Storage.Index - internal typealias Indices = Range +extension _SystemString: RandomAccessCollection, MutableCollection { + public typealias Element = _SystemChar + public typealias Index = Storage.Index + public typealias Indices = Range - internal var startIndex: Index { + public var startIndex: Index { nullTerminatedStorage.startIndex } - internal var endIndex: Index { + public var endIndex: Index { nullTerminatedStorage.index(before: nullTerminatedStorage.endIndex) } - internal subscript(position: Index) -> SystemChar { + public subscript(position: Index) -> _SystemChar { _read { precondition(position >= startIndex && position <= endIndex) yield nullTerminatedStorage[position] @@ -128,42 +129,42 @@ extension SystemString: RandomAccessCollection, MutableCollection { } } } -extension SystemString: RangeReplaceableCollection { - internal mutating func replaceSubrange( +extension _SystemString: RangeReplaceableCollection { + public mutating func replaceSubrange( _ subrange: Range, with newElements: C - ) where C.Element == SystemChar { + ) where C.Element == _SystemChar { defer { _invariantCheck() } nullTerminatedStorage.replaceSubrange(subrange, with: newElements) } - internal mutating func reserveCapacity(_ n: Int) { + public mutating func reserveCapacity(_ n: Int) { defer { _invariantCheck() } nullTerminatedStorage.reserveCapacity(1 + n) } // TODO: Below include null terminator, is this desired? - internal func withContiguousStorageIfAvailable( - _ body: (UnsafeBufferPointer) throws -> R + public func withContiguousStorageIfAvailable( + _ body: (UnsafeBufferPointer<_SystemChar>) throws -> R ) rethrows -> R? { try nullTerminatedStorage.withContiguousStorageIfAvailable(body) } - internal mutating func withContiguousMutableStorageIfAvailable( - _ body: (inout UnsafeMutableBufferPointer) throws -> R + public mutating func withContiguousMutableStorageIfAvailable( + _ body: (inout UnsafeMutableBufferPointer<_SystemChar>) throws -> R ) rethrows -> R? { defer { _invariantCheck() } return try nullTerminatedStorage.withContiguousMutableStorageIfAvailable(body) } } -extension SystemString: Hashable, Codable {} +extension _SystemString: Hashable, Codable {} -extension SystemString { +extension _SystemString { // TODO: Below include null terminator, is this desired? internal func withSystemChars( - _ f: (UnsafeBufferPointer) throws -> T + _ f: (UnsafeBufferPointer<_SystemChar>) throws -> T ) rethrows -> T { try withContiguousStorageIfAvailable(f)! } @@ -172,7 +173,7 @@ extension SystemString { _ f: (UnsafeBufferPointer) throws -> T ) rethrows -> T { try withSystemChars { chars in - let length = chars.count * MemoryLayout.stride + let length = chars.count * MemoryLayout<_SystemChar>.stride let count = length / MemoryLayout.stride return try chars.baseAddress!.withMemoryRebound( to: CInterop.PlatformUnicodeEncoding.CodeUnit.self, @@ -184,7 +185,7 @@ extension SystemString { } } -extension Slice where Base == SystemString { +extension Slice where Base == _SystemString { internal func withCodeUnits( _ f: (UnsafeBufferPointer) throws -> T ) rethrows -> T { @@ -201,19 +202,19 @@ extension Slice where Base == SystemString { _ f: (UnsafePointer) throws -> T ) rethrows -> T { // FIXME: avoid allocation if we're at the end - return try SystemString(self).withPlatformString(f) + return try _SystemString(self).withPlatformString(f) } } extension String { - internal init(decoding str: SystemString) { + internal init(decoding str: _SystemString) { // TODO: Can avoid extra strlen self = str.withPlatformString { String(platformString: $0) } } - internal init?(validating str: SystemString) { + internal init?(validating str: _SystemString) { // TODO: Can avoid extra strlen guard let str = str.withPlatformString(String.init(validatingPlatformString:)) else { return nil } @@ -222,27 +223,27 @@ extension String { } } -extension SystemString: ExpressibleByStringLiteral { - internal init(stringLiteral: String) { +extension _SystemString: ExpressibleByStringLiteral { + public init(stringLiteral: String) { self.init(stringLiteral) } internal init(_ string: String) { // TODO: can avoid extra strlen self = string.withPlatformString { - SystemString(platformString: $0) + _SystemString(platformString: $0) } } } -extension SystemString: CustomStringConvertible, CustomDebugStringConvertible { +extension _SystemString: CustomStringConvertible, CustomDebugStringConvertible { internal var string: String { String(decoding: self) } - internal var description: String { string } - internal var debugDescription: String { description.debugDescription } + public var description: String { string } + public var debugDescription: String { description.debugDescription } } -extension SystemString { +extension _SystemString { /// Creates a system string by copying bytes from a null-terminated platform string. /// /// - Parameter platformString: A pointer to a null-terminated platform string. @@ -250,8 +251,8 @@ extension SystemString { let count = 1 + system_platform_strlen(platformString) // TODO: Is this the right way? - let chars: Array = platformString.withMemoryRebound( - to: SystemChar.self, capacity: count + let chars: Array<_SystemChar> = platformString.withMemoryRebound( + to: _SystemChar.self, capacity: count ) { let bufPtr = UnsafeBufferPointer(start: $0, count: count) return Array(bufPtr) @@ -276,7 +277,7 @@ extension SystemString { _ f: (UnsafePointer) throws -> T ) rethrows -> T { try withSystemChars { chars in - let length = chars.count * MemoryLayout.stride + let length = chars.count * MemoryLayout<_SystemChar>.stride return try chars.baseAddress!.withMemoryRebound( to: CInterop.PlatformChar.self, capacity: length / MemoryLayout.stride @@ -289,10 +290,10 @@ extension SystemString { } #if compiler(>=5.5) && canImport(_Concurrency) -extension SystemChar: Sendable {} -extension SystemString: Sendable {} +extension _SystemChar: Sendable {} +extension _SystemString: Sendable {} #endif -// TODO: SystemString should use a COW-interchangable storage form rather +// TODO: _SystemString should use a COW-interchangable storage form rather // than array, so you could "borrow" the storage from a non-bridged String // or Data or whatever diff --git a/Tests/SystemTests/FilePathTests/FilePathExtras.swift b/Tests/SystemTests/FilePathTests/FilePathExtras.swift index 82f11373..2402ea0d 100644 --- a/Tests/SystemTests/FilePathTests/FilePathExtras.swift +++ b/Tests/SystemTests/FilePathTests/FilePathExtras.swift @@ -8,7 +8,7 @@ // Why can't I write this extension on `FilePath.ComponentView.SubSequence`? @available(/*System 0.0.2: macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0*/iOS 8, *) extension Slice where Base == FilePath.ComponentView { - internal var _storageSlice: SystemString.SubSequence { + internal var _storageSlice: _SystemString.SubSequence { base._path._storage[self.startIndex._storage ..< self.endIndex._storage] } } @@ -39,7 +39,7 @@ extension FilePath { let (tail, baseTail) = _dropCommonPrefix(components, base.components) - var prefix = SystemString() + var prefix = _SystemString() for _ in 0..?@[\]^_`{|}~"##) for char in invalid { XCTAssertFalse(char.isLetter) diff --git a/Tests/SystemTests/SystemStringTests.swift b/Tests/SystemTests/SystemStringTests.swift index a1891714..9a256eaa 100644 --- a/Tests/SystemTests/SystemStringTests.swift +++ b/Tests/SystemTests/SystemStringTests.swift @@ -74,13 +74,13 @@ struct StringTest: TestCase { } // Test String, SystemString, FilePath construction - let sysStr = SystemString(string) + let sysStr = _SystemString(string) expectEqualSequence(string.unicodeScalars, sysStr.string.unicodeScalars) expectEqual(string, String(decoding: sysStr)) expectEqual(string, String(validating: sysStr)) let sysRaw = raw.withUnsafeBufferPointer { - SystemString(platformString: $0.baseAddress!) + _SystemString(platformString: $0.baseAddress!) } expectEqual(string, String(decoding: sysRaw)) expectEqual(isValid, nil != String(validating: sysRaw)) @@ -99,8 +99,8 @@ struct StringTest: TestCase { "String(validatingPlatformString:)") // Test null insertion - let rawChars = raw.lazy.map { SystemChar($0) } - expectEqual(sysRaw, SystemString(rawChars.dropLast())) + let rawChars = raw.lazy.map { _SystemChar($0) } + expectEqual(sysRaw, _SystemString(rawChars.dropLast())) sysRaw.withSystemChars { // TODO: assuming we want null in withSysChars expectEqualSequence(rawChars, $0, "rawChars") } @@ -244,8 +244,8 @@ final class SystemStringTest: XCTestCase { // TODO: More exhaustive RAC+RRC SystemString tests func testAdHoc() { - var str: SystemString = "abc" - str.append(SystemChar(ascii: "d")) + var str: _SystemString = "abc" + str.append(_SystemChar(ascii: "d")) XCTAssert(str == "abcd") XCTAssert(str.count == 4) XCTAssert(str.count == str.length)