File: Util%2BStringArray.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (117 lines) | stat: -rw-r--r-- 4,548 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
/*
 This source file is part of the Swift System open source project

 Copyright (c) 2021 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
*/

extension Array where Element == String {
  internal typealias CStr = UnsafePointer<CChar>?

  /// Call `body` with a buffer of `UnsafePointer<CChar>?` values,
  /// suitable for passing to a C function that expects a C string array.
  /// The buffer is guaranteed to be followed by an extra storage slot
  /// containing a null pointer. (For C functions that expect an array
  /// terminated with a null pointer.)
  ///
  /// This function is careful not to heap allocate memory unless there are
  /// too many strings, or if it needs to copy too much character data.
  internal func _withCStringArray<R>(
    _ body: (UnsafeBufferPointer<UnsafePointer<CChar>?>) throws -> R
  ) rethrows -> R {
    if self.count == 0 {
      // Fast path: empty array.
      let p: CStr = nil
      return try Swift.withUnsafePointer(to: p) { array in
        try body(UnsafeBufferPointer(start: array, count: 0))
      }
    }
    #if SYSTEM_OS_BUILD // String._guts isn't accessible from SwiftPM or CMake
    if self.count == 1, self[0]._guts._isLargeZeroTerminatedContiguousUTF8 {
      // Fast path: Single fast string.
      let start = self[0]._guts._largeContiguousUTF8CodeUnits.baseAddress!
      var p: (CStr, CStr) = (
        UnsafeRawPointer(start).assumingMemoryBound(to: CChar.self),
        nil
      )
      return try Swift.withUnsafeBytes(of: &p) { buffer in
        let start = buffer.baseAddress!.assumingMemoryBound(to: CStr.self)
        return try body(UnsafeBufferPointer(start: start, count: 1))
      }
    }
    #endif
    // We need to create a buffer for the C array.
    return try _withStackBuffer(
      capacity: (self.count + 1) * MemoryLayout<CStr>.stride
    ) { array in
      let array = array.bindMemory(to: CStr.self)
      // Calculate number of bytes we need for character storage
      let bytes = self.reduce(into: 0) { count, string in
        #if SYSTEM_OS_BUILD
        if string._guts._isLargeZeroTerminatedContiguousUTF8 { return }
        #endif
        count += string.utf8.count + 1 // Plus one for terminating NUL
      }
      #if SYSTEM_OS_BUILD
      if bytes == 0 {
        // Fast path: we only contain strings with stable null-terminated storage
        for i in self.indices {
          let string = self[i]
          precondition(string._guts._isLargeZeroTerminatedContiguousUTF8)
          let address = string._guts._largeContiguousUTF8CodeUnits.baseAddress!
          array[i] = UnsafeRawPointer(address).assumingMemoryBound(to: CChar.self)
        }
        array[self.count] = nil
        return try body(UnsafeBufferPointer(rebasing: array.dropLast()))
      }
      #endif
      return try _withStackBuffer(capacity: bytes) { chars in
        var chars = chars
        for i in self.indices {
          let (cstr, scratchUsed) = self[i]._getCStr(with: chars)
          array[i] = cstr.assumingMemoryBound(to: CChar.self)
          chars = .init(rebasing: chars[scratchUsed...])
        }
        array[self.count] = nil
        return try body(UnsafeBufferPointer(rebasing: array.dropLast()))
      }
    }
  }
}

extension String {
  fileprivate func _getCStr(
    with scratch: UnsafeMutableRawBufferPointer
  ) -> (cstr: UnsafeRawPointer, scratchUsed: Int) {
    #if SYSTEM_OS_BUILD
    if _guts._isLargeZeroTerminatedContiguousUTF8 {
      // This is a wonderful string, we can just use its storage address.
      let address = _guts._largeContiguousUTF8CodeUnits.baseAddress!
      return (UnsafeRawPointer(address), 0)
    }
    #endif
    let r: (UnsafeRawPointer, Int)? = self.utf8.withContiguousStorageIfAvailable { source in
      // This is a somewhat okay string -- we need to use memcpy.
      precondition(source.count <= scratch.count)
      let start = scratch.baseAddress!
      start.copyMemory(from: source.baseAddress!, byteCount: source.count)
      start.storeBytes(of: 0, toByteOffset: source.count, as: UInt8.self)
      return (UnsafeRawPointer(start), source.count + 1)
    }
    if let r = r { return r }

    // What a horrible string; we need to copy individual bytes.
    precondition(self.utf8.count <= scratch.count)
    var c = 0
    for byte in self.utf8 {
      scratch[c] = byte
      c += 1
    }
    scratch[c] = 0
    c += 1
    return (UnsafeRawPointer(scratch.baseAddress!), c)
  }
}