File: SystemString.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (300 lines) | stat: -rw-r--r-- 9,549 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
 This source file is part of the Swift System open source project

 Copyright (c) 2020 Apple Inc. and the Swift System project authors
 Licensed under Apache License v2.0 with Runtime Library Exception

 See https://swift.org/LICENSE.txt for license information
*/

// A platform-native character representation, currently used for file paths
internal struct SystemChar: RawRepresentable, Comparable, Hashable, Codable {
  internal typealias RawValue = CInterop.PlatformChar

  internal var rawValue: RawValue

  internal init(rawValue: RawValue) { self.rawValue = rawValue }

  internal init(_ rawValue: RawValue) { self.init(rawValue: rawValue) }

  static func < (lhs: SystemChar, rhs: SystemChar) -> Bool {
    lhs.rawValue < rhs.rawValue
  }
}

extension SystemChar {
  internal init(ascii: Unicode.Scalar) {
    self.init(rawValue: numericCast(UInt8(ascii: ascii)))
  }
  internal init(codeUnit: CInterop.PlatformUnicodeEncoding.CodeUnit) {
    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 var codeUnit: CInterop.PlatformUnicodeEncoding.CodeUnit {
    rawValue._platformCodeUnit
  }

  internal var asciiScalar: Unicode.Scalar? {
    guard isASCII else { return nil }
    return Unicode.Scalar(UInt8(truncatingIfNeeded: rawValue))
  }

  internal var isASCII: Bool {
    (0...0x7F).contains(rawValue)
  }

  internal var isLetter: Bool {
    guard isASCII else { return false }
    let asciiRaw: UInt8 = numericCast(rawValue)
    return (UInt8(ascii: "a") ... UInt8(ascii: "z")).contains(asciiRaw) ||
           (UInt8(ascii: "A") ... UInt8(ascii: "Z")).contains(asciiRaw)
  }
}

// A platform-native string representation, currently for file paths
//
// Always null-terminated.
internal struct SystemString {
  internal typealias Storage = [SystemChar]
  internal var nullTerminatedStorage: Storage
}

extension SystemString {
  internal init() {
    self.nullTerminatedStorage = [.null]
    _invariantCheck()
  }

  internal var length: Int {
    let len = nullTerminatedStorage.count - 1
    assert(len == self.count)
    return len
  }

  // Common funnel point. Ensure all non-empty inits go here.
  internal init(nullTerminated storage: Storage) {
    self.nullTerminatedStorage = storage
    _invariantCheck()
  }

  // Ensures that result is null-terminated
  internal init<C: Collection>(_ chars: C) where C.Element == SystemChar {
    var rawChars = Storage(chars)
    if rawChars.last != .null {
      rawChars.append(.null)
    }
    self.init(nullTerminated: rawChars)
  }
}

extension SystemString {
  fileprivate func _invariantCheck() {
    #if DEBUG
    precondition(nullTerminatedStorage.last! == .null)
    precondition(nullTerminatedStorage.firstIndex(of: .null) == length)
    #endif // DEBUG
  }
}

extension SystemString: RandomAccessCollection, MutableCollection {
  internal typealias Element = SystemChar
  internal typealias Index = Storage.Index
  internal typealias Indices = Range<Index>

  internal var startIndex: Index {
    nullTerminatedStorage.startIndex
  }

  internal var endIndex: Index {
    nullTerminatedStorage.index(before: nullTerminatedStorage.endIndex)
  }

  internal subscript(position: Index) -> SystemChar {
    _read {
      precondition(position >= startIndex && position <= endIndex)
      yield nullTerminatedStorage[position]
    }
    set(newValue) {
      precondition(position >= startIndex && position <= endIndex)
      nullTerminatedStorage[position] = newValue
      _invariantCheck()
    }
  }
}
extension SystemString: RangeReplaceableCollection {
  internal mutating func replaceSubrange<C: Collection>(
    _ subrange: Range<Index>, with newElements: C
  ) where C.Element == SystemChar {
    defer { _invariantCheck() }
    nullTerminatedStorage.replaceSubrange(subrange, with: newElements)
  }

  internal mutating func reserveCapacity(_ n: Int) {
    defer { _invariantCheck() }
    nullTerminatedStorage.reserveCapacity(1 + n)
  }

  internal func withContiguousStorageIfAvailable<R>(
    _ body: (UnsafeBufferPointer<SystemChar>) throws -> R
  ) rethrows -> R? {
    // Do not include the null terminator, it is outside the Collection
    try nullTerminatedStorage.withContiguousStorageIfAvailable {
      try body(.init(start: $0.baseAddress, count: $0.count-1))
    }
  }

  internal mutating func withContiguousMutableStorageIfAvailable<R>(
    _ body: (inout UnsafeMutableBufferPointer<SystemChar>) throws -> R
  ) rethrows -> R? {
    defer { _invariantCheck() }
    // Do not include the null terminator, it is outside the Collection
    return try nullTerminatedStorage.withContiguousMutableStorageIfAvailable {
      var buffer = UnsafeMutableBufferPointer<SystemChar>(
        start: $0.baseAddress, count: $0.count-1
      )
      return try body(&buffer)
    }
  }
}

extension SystemString: Hashable, Codable {}

extension SystemString {

  // withSystemChars includes the null terminator
  internal func withSystemChars<T>(
    _ f: (UnsafeBufferPointer<SystemChar>) throws -> T
  ) rethrows -> T {
    try nullTerminatedStorage.withContiguousStorageIfAvailable(f)!
  }

  internal func withCodeUnits<T>(
    _ f: (UnsafeBufferPointer<CInterop.PlatformUnicodeEncoding.CodeUnit>) throws -> T
  ) rethrows -> T {
    try withSystemChars { chars in
      let length = chars.count * MemoryLayout<SystemChar>.stride
      let count = length / MemoryLayout<CInterop.PlatformUnicodeEncoding.CodeUnit>.stride
      return try chars.baseAddress!.withMemoryRebound(
        to: CInterop.PlatformUnicodeEncoding.CodeUnit.self,
        capacity: count
      ) { pointer in
        try f(UnsafeBufferPointer(start: pointer, count: count))
      }
    }
  }
}

extension Slice where Base == SystemString {
  internal func withCodeUnits<T>(
    _ f: (UnsafeBufferPointer<CInterop.PlatformUnicodeEncoding.CodeUnit>) throws -> T
  ) rethrows -> T {
    try base.withCodeUnits {
      try f(UnsafeBufferPointer(rebasing: $0[indices]))
    }
  }

  internal var string: String {
    withCodeUnits { String(decoding: $0, as: CInterop.PlatformUnicodeEncoding.self) }
  }

  internal func withPlatformString<T>(
    _ f: (UnsafePointer<CInterop.PlatformChar>) throws -> T
  ) rethrows -> T {
    // FIXME: avoid allocation if we're at the end
    return try SystemString(self).withPlatformString(f)
  }

}

extension String {
  internal init(decoding str: SystemString) {
    // TODO: Can avoid extra strlen
    self = str.withPlatformString {
      String(platformString: $0)
    }
  }
  internal init?(validating str: SystemString) {
    // TODO: Can avoid extra strlen
    guard let str = str.withPlatformString(String.init(validatingPlatformString:))
    else { return nil }

    self = str
  }
}

extension SystemString: ExpressibleByStringLiteral {
  internal init(stringLiteral: String) {
    self.init(stringLiteral)
  }

  internal init(_ string: String) {
    // TODO: can avoid extra strlen
    self = string.withPlatformString {
      SystemString(platformString: $0)
    }
  }
}

extension SystemString: CustomStringConvertible, CustomDebugStringConvertible {
  internal var string: String { String(decoding: self) }

  internal var description: String { string }
  internal var debugDescription: String { description.debugDescription }
}

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.
  internal init(platformString: UnsafePointer<CInterop.PlatformChar>) {
    let count = 1 + system_platform_strlen(platformString)

    // TODO: Is this the right way?
    let chars: Array<SystemChar> = platformString.withMemoryRebound(
      to: SystemChar.self, capacity: count
    ) {
      let bufPtr = UnsafeBufferPointer(start: $0, count: count)
      return Array(bufPtr)
    }

    self.init(nullTerminated: chars)
  }

  /// Calls the given closure with a pointer to the contents of the sytem string,
  /// represented as a null-terminated platform string.
  ///
  /// - Parameter body: A closure with a pointer parameter
  ///   that points to a null-terminated platform string.
  ///   If `body` has a return value,
  ///   that value is also used as the return value for this method.
  /// - Returns: The return value, if any, of the `body` closure parameter.
  ///
  /// The pointer passed as an argument to `body` is valid
  /// only during the execution of this method.
  /// Don't try to store the pointer for later use.
  internal func withPlatformString<T>(
    _ f: (UnsafePointer<CInterop.PlatformChar>) throws -> T
  ) rethrows -> T {
    try withSystemChars { chars in
      let length = chars.count * MemoryLayout<SystemChar>.stride
      return try chars.baseAddress!.withMemoryRebound(
        to: CInterop.PlatformChar.self,
        capacity: length / MemoryLayout<CInterop.PlatformChar>.stride
      ) { pointer in
        assert(pointer[self.count] == 0)
        return try f(pointer)
      }
    }
  }
}

// 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