14
14
// - Stable pointer into a C string without copying it? Aug 2021
15
15
// https://forums.swift.org/t/stable-pointer-into-a-c-string-without-copying-it/51244/1
16
16
17
- import Foundation
18
17
import StringZillaC
19
18
20
19
// We need to link the standard libraries.
@@ -25,10 +24,12 @@ import Darwin.C
25
24
#endif
26
25
27
26
/// Protocol defining a single-byte data type.
28
- protocol SingleByte { }
27
+ fileprivate protocol SingleByte { }
28
+
29
29
extension UInt8 : SingleByte { }
30
30
extension Int8 : SingleByte { } // This would match `CChar` as well.
31
31
32
+ @usableFromInline
32
33
enum StringZillaError : Error {
33
34
case contiguousStorageUnavailable
34
35
case memoryAllocationFailed
@@ -51,47 +52,52 @@ enum StringZillaError: Error {
51
52
/// https://developer.apple.com/documentation/swift/stringprotocol/withcstring(_:)
52
53
/// https://developer.apple.com/documentation/swift/stringprotocol/withcstring(encodedas:_:)
53
54
/// https://developer.apple.com/documentation/swift/stringprotocol/data(using:allowlossyconversion:)
54
- public protocol SZViewable {
55
- associatedtype SZIndex
56
-
55
+ public protocol StringZillaViewable : Collection {
56
+ /// A type that represents a position in the collection.
57
+ ///
57
58
/// Executes a closure with a pointer to the string's UTF8 C representation and its length.
59
+ ///
58
60
/// - Parameters:
59
61
/// - body: A closure that takes a pointer to a C string and its length.
60
62
/// - Throws: Can throw an error.
61
63
/// - Returns: Returns a value of type R, which is the result of the closure.
62
- func szScope < R> ( _ body: ( sz_cptr_t , sz_size_t ) throws -> R ) rethrows -> R
64
+ func withStringZillaScope < R> ( _ body: ( sz_cptr_t , sz_size_t ) throws -> R ) rethrows -> R
63
65
64
66
/// Calculates the offset index for a given byte pointer relative to a start pointer.
67
+ ///
65
68
/// - Parameters:
66
69
/// - bytePointer: A pointer to the byte for which the offset is calculated.
67
70
/// - startPointer: The starting pointer for the calculation, previously obtained from `szScope`.
68
71
/// - Returns: The calculated index offset.
69
- func szOffset ( forByte bytePointer: sz_cptr_t , after startPointer: sz_cptr_t ) -> SZIndex
72
+ func stringZillaByteOffset ( forByte bytePointer: sz_cptr_t , after startPointer: sz_cptr_t ) -> Index
70
73
}
71
74
72
- extension String : SZViewable {
73
- public typealias SZIndex = String . Index
75
+ extension String : StringZillaViewable {
76
+ public typealias Index = String . Index
74
77
75
- public func szScope< R> ( _ body: ( sz_cptr_t , sz_size_t ) throws -> R ) rethrows -> R {
76
- let cLength = sz_size_t ( self . lengthOfBytes ( using: . utf8) )
78
+ @_transparent
79
+ public func withStringZillaScope< R> ( _ body: ( sz_cptr_t , sz_size_t ) throws -> R ) rethrows -> R {
80
+ let cLength = sz_size_t ( utf8. count)
77
81
return try self . withCString { cString in
78
82
try body ( cString, cLength)
79
83
}
80
84
}
81
85
82
- public func szOffset( forByte bytePointer: sz_cptr_t , after startPointer: sz_cptr_t ) -> SZIndex {
83
- return self . index ( self . startIndex, offsetBy: bytePointer - startPointer)
86
+ @_transparent
87
+ public func stringZillaByteOffset( forByte bytePointer: sz_cptr_t , after startPointer: sz_cptr_t ) -> Index {
88
+ self . utf8. index ( self . utf8. startIndex, offsetBy: bytePointer - startPointer)
84
89
}
85
90
}
86
91
87
- extension Substring . UTF8View : SZViewable {
88
- public typealias SZIndex = Substring . UTF8View . Index
92
+ extension Substring . UTF8View : StringZillaViewable {
93
+ public typealias Index = Substring . UTF8View . Index
89
94
90
95
/// Executes a closure with a pointer to the UTF8View's contiguous storage of single-byte elements (UTF-8 code units).
91
96
/// - Parameters:
92
97
/// - body: A closure that takes a pointer to the contiguous storage and its size.
93
98
/// - Throws: An error if the storage is not contiguous.
94
- public func szScope< R> ( _ body: ( sz_cptr_t , sz_size_t ) throws -> R ) rethrows -> R {
99
+ @_transparent
100
+ public func withStringZillaScope< R> ( _ body: ( sz_cptr_t , sz_size_t ) throws -> R ) rethrows -> R {
95
101
return try withContiguousStorageIfAvailable { bufferPointer -> R in
96
102
let cLength = sz_size_t ( bufferPointer. count)
97
103
let cString = UnsafeRawPointer ( bufferPointer. baseAddress!) . assumingMemoryBound ( to: CChar . self)
@@ -106,19 +112,20 @@ extension Substring.UTF8View: SZViewable {
106
112
/// - bytePointer: A pointer to the byte for which the offset is calculated.
107
113
/// - startPointer: The starting pointer for the calculation, previously obtained from `szScope`.
108
114
/// - Returns: The calculated index offset.
109
- public func szOffset( forByte bytePointer: sz_cptr_t , after startPointer: sz_cptr_t ) -> SZIndex {
115
+ @_transparent
116
+ public func stringZillaByteOffset( forByte bytePointer: sz_cptr_t , after startPointer: sz_cptr_t ) -> Index {
110
117
return self . index ( self . startIndex, offsetBy: bytePointer - startPointer)
111
118
}
112
119
}
113
120
114
- extension String . UTF8View : SZViewable {
115
- public typealias SZIndex = String . UTF8View . Index
121
+ extension String . UTF8View : StringZillaViewable {
122
+ public typealias Index = String . UTF8View . Index
116
123
117
124
/// Executes a closure with a pointer to the UTF8View's contiguous storage of single-byte elements (UTF-8 code units).
118
125
/// - Parameters:
119
126
/// - body: A closure that takes a pointer to the contiguous storage and its size.
120
127
/// - Throws: An error if the storage is not contiguous.
121
- public func szScope < R> ( _ body: ( sz_cptr_t , sz_size_t ) throws -> R ) rethrows -> R {
128
+ public func withStringZillaScope < R> ( _ body: ( sz_cptr_t , sz_size_t ) throws -> R ) rethrows -> R {
122
129
return try withContiguousStorageIfAvailable { bufferPointer -> R in
123
130
let cLength = sz_size_t ( bufferPointer. count)
124
131
let cString = UnsafeRawPointer ( bufferPointer. baseAddress!) . assumingMemoryBound ( to: CChar . self)
@@ -133,22 +140,24 @@ extension String.UTF8View: SZViewable {
133
140
/// - bytePointer: A pointer to the byte for which the offset is calculated.
134
141
/// - startPointer: The starting pointer for the calculation, previously obtained from `szScope`.
135
142
/// - Returns: The calculated index offset.
136
- public func szOffset ( forByte bytePointer: sz_cptr_t , after startPointer: sz_cptr_t ) -> SZIndex {
143
+ public func stringZillaByteOffset ( forByte bytePointer: sz_cptr_t , after startPointer: sz_cptr_t ) -> Index {
137
144
return self . index ( self . startIndex, offsetBy: bytePointer - startPointer)
138
145
}
139
146
}
140
147
141
- public extension SZViewable {
148
+ public extension StringZillaViewable {
142
149
143
150
/// Finds the first occurrence of the specified substring within the receiver.
144
151
/// - Parameter needle: The substring to search for.
145
152
/// - Returns: The index of the found occurrence, or `nil` if not found.
146
- func findFirst( substring needle: any SZViewable ) -> SZIndex ? {
147
- var result : SZIndex ?
148
- szScope { hPointer, hLength in
149
- needle. szScope { nPointer, nLength in
153
+ @_specialize ( where Self == String, S == String)
154
+ @_specialize ( where Self == String. UTF8View, S == String . UTF8View)
155
+ func findFirst< S: StringZillaViewable > ( substring needle: S ) -> Index ? {
156
+ var result : Index ?
157
+ withStringZillaScope { hPointer, hLength in
158
+ needle. withStringZillaScope { nPointer, nLength in
150
159
if let matchPointer = sz_find ( hPointer, hLength, nPointer, nLength) {
151
- result = self . szOffset ( forByte: matchPointer, after: hPointer)
160
+ result = self . stringZillaByteOffset ( forByte: matchPointer, after: hPointer)
152
161
}
153
162
}
154
163
}
@@ -158,12 +167,14 @@ public extension SZViewable {
158
167
/// Finds the last occurrence of the specified substring within the receiver.
159
168
/// - Parameter needle: The substring to search for.
160
169
/// - Returns: The index of the found occurrence, or `nil` if not found.
161
- func findLast( substring needle: any SZViewable ) -> SZIndex ? {
162
- var result : SZIndex ?
163
- szScope { hPointer, hLength in
164
- needle. szScope { nPointer, nLength in
170
+ @_specialize ( where Self == String, S == String)
171
+ @_specialize ( where Self == String. UTF8View, S == String . UTF8View)
172
+ func findLast< S: StringZillaViewable > ( substring needle: S ) -> Index ? {
173
+ var result : Index ?
174
+ withStringZillaScope { hPointer, hLength in
175
+ needle. withStringZillaScope { nPointer, nLength in
165
176
if let matchPointer = sz_rfind ( hPointer, hLength, nPointer, nLength) {
166
- result = self . szOffset ( forByte: matchPointer, after: hPointer)
177
+ result = self . stringZillaByteOffset ( forByte: matchPointer, after: hPointer)
167
178
}
168
179
}
169
180
}
@@ -173,12 +184,14 @@ public extension SZViewable {
173
184
/// Finds the first occurrence of the specified character-set members within the receiver.
174
185
/// - Parameter characters: A string-like collection of characters to match.
175
186
/// - Returns: The index of the found occurrence, or `nil` if not found.
176
- func findFirst( characterFrom characters: any SZViewable ) -> SZIndex ? {
177
- var result : SZIndex ?
178
- szScope { hPointer, hLength in
179
- characters. szScope { nPointer, nLength in
187
+ @_specialize ( where Self == String, S == String)
188
+ @_specialize ( where Self == String. UTF8View, S == String . UTF8View)
189
+ func findFirst< S: StringZillaViewable > ( characterFrom characters: S ) -> Index ? {
190
+ var result : Index ?
191
+ withStringZillaScope { hPointer, hLength in
192
+ characters. withStringZillaScope { nPointer, nLength in
180
193
if let matchPointer = sz_find_char_from ( hPointer, hLength, nPointer, nLength) {
181
- result = self . szOffset ( forByte: matchPointer, after: hPointer)
194
+ result = self . stringZillaByteOffset ( forByte: matchPointer, after: hPointer)
182
195
}
183
196
}
184
197
}
@@ -188,12 +201,14 @@ public extension SZViewable {
188
201
/// Finds the last occurrence of the specified character-set members within the receiver.
189
202
/// - Parameter characters: A string-like collection of characters to match.
190
203
/// - Returns: The index of the found occurrence, or `nil` if not found.
191
- func findLast( characterFrom characters: any SZViewable ) -> SZIndex ? {
192
- var result : SZIndex ?
193
- szScope { hPointer, hLength in
194
- characters. szScope { nPointer, nLength in
204
+ @_specialize ( where Self == String, S == String)
205
+ @_specialize ( where Self == String. UTF8View, S == String . UTF8View)
206
+ func findLast< S: StringZillaViewable > ( characterFrom characters: S ) -> Index ? {
207
+ var result : Index ?
208
+ withStringZillaScope { hPointer, hLength in
209
+ characters. withStringZillaScope { nPointer, nLength in
195
210
if let matchPointer = sz_rfind_char_from ( hPointer, hLength, nPointer, nLength) {
196
- result = self . szOffset ( forByte: matchPointer, after: hPointer)
211
+ result = self . stringZillaByteOffset ( forByte: matchPointer, after: hPointer)
197
212
}
198
213
}
199
214
}
@@ -203,12 +218,14 @@ public extension SZViewable {
203
218
/// Finds the first occurrence of a character outside of the the given character-set within the receiver.
204
219
/// - Parameter characters: A string-like collection of characters to exclude.
205
220
/// - Returns: The index of the found occurrence, or `nil` if not found.
206
- func findFirst( characterNotFrom characters: any SZViewable ) -> SZIndex ? {
207
- var result : SZIndex ?
208
- szScope { hPointer, hLength in
209
- characters. szScope { nPointer, nLength in
221
+ @_specialize ( where Self == String, S == String)
222
+ @_specialize ( where Self == String. UTF8View, S == String . UTF8View)
223
+ func findFirst< S: StringZillaViewable > ( characterNotFrom characters: S ) -> Index ? {
224
+ var result : Index ?
225
+ withStringZillaScope { hPointer, hLength in
226
+ characters. withStringZillaScope { nPointer, nLength in
210
227
if let matchPointer = sz_find_char_not_from ( hPointer, hLength, nPointer, nLength) {
211
- result = self . szOffset ( forByte: matchPointer, after: hPointer)
228
+ result = self . stringZillaByteOffset ( forByte: matchPointer, after: hPointer)
212
229
}
213
230
}
214
231
}
@@ -218,12 +235,14 @@ public extension SZViewable {
218
235
/// Finds the last occurrence of a character outside of the the given character-set within the receiver.
219
236
/// - Parameter characters: A string-like collection of characters to exclude.
220
237
/// - Returns: The index of the found occurrence, or `nil` if not found.
221
- func findLast( characterNotFrom characters: any SZViewable ) -> SZIndex ? {
222
- var result : SZIndex ?
223
- szScope { hPointer, hLength in
224
- characters. szScope { nPointer, nLength in
238
+ @_specialize ( where Self == String, S == String)
239
+ @_specialize ( where Self == String. UTF8View, S == String . UTF8View)
240
+ func findLast< S: StringZillaViewable > ( characterNotFrom characters: S ) -> Index ? {
241
+ var result : Index ?
242
+ withStringZillaScope { hPointer, hLength in
243
+ characters. withStringZillaScope { nPointer, nLength in
225
244
if let matchPointer = sz_rfind_char_not_from ( hPointer, hLength, nPointer, nLength) {
226
- result = self . szOffset ( forByte: matchPointer, after: hPointer)
245
+ result = self . stringZillaByteOffset ( forByte: matchPointer, after: hPointer)
227
246
}
228
247
}
229
248
}
@@ -234,13 +253,15 @@ public extension SZViewable {
234
253
/// - Parameter other: A string-like collection of characters to exclude.
235
254
/// - Returns: The edit distance, as an unsigned integer.
236
255
/// - Throws: If a memory allocation error has happened.
237
- func editDistance( from other: any SZViewable , bound: UInt64 = 0 ) throws -> UInt64 ? {
256
+ @_specialize ( where Self == String, S == String)
257
+ @_specialize ( where Self == String. UTF8View, S == String . UTF8View)
258
+ func editDistance< S: StringZillaViewable > ( from other: S , bound: UInt64 = 0 ) throws -> UInt64 ? {
238
259
var result : UInt64 ?
239
260
240
261
// Use a do-catch block to handle potential errors
241
262
do {
242
- try szScope { hPointer, hLength in
243
- try other. szScope { nPointer, nLength in
263
+ try withStringZillaScope { hPointer, hLength in
264
+ try other. withStringZillaScope { nPointer, nLength in
244
265
result = UInt64 ( sz_edit_distance ( hPointer, hLength, nPointer, nLength, sz_size_t ( bound) , nil ) )
245
266
if result == SZ_SIZE_MAX {
246
267
result = nil
0 commit comments