Skip to content

Commit 46e957c

Browse files
ashvardanianvmanot
andcommitted
Merge branch 'swift' into main-dev
Co-authored-by: Vatsal Manot <[email protected]>
2 parents ff6a660 + 1c4ffda commit 46e957c

File tree

3 files changed

+81
-56
lines changed

3 files changed

+81
-56
lines changed

Package.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import PackageDescription
44
let package = Package(
55
name: "StringZilla",
66
products: [
7-
.library(name: "StringZilla", targets: ["StringZillaC", "StringZilla"])
7+
.library(
8+
name: "StringZilla",
9+
targets: ["StringZillaC", "StringZilla"]
10+
)
811
],
912
targets: [
1013
.target(

include/stringzilla/spm-fix.c

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

swift/StringProtocol+StringZilla.swift

+76-55
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
// - Stable pointer into a C string without copying it? Aug 2021
1515
// https://forums.swift.org/t/stable-pointer-into-a-c-string-without-copying-it/51244/1
1616

17-
import Foundation
1817
import StringZillaC
1918

2019
// We need to link the standard libraries.
@@ -25,10 +24,12 @@ import Darwin.C
2524
#endif
2625

2726
/// Protocol defining a single-byte data type.
28-
protocol SingleByte {}
27+
fileprivate protocol SingleByte {}
28+
2929
extension UInt8: SingleByte {}
3030
extension Int8: SingleByte {} // This would match `CChar` as well.
3131

32+
@usableFromInline
3233
enum StringZillaError: Error {
3334
case contiguousStorageUnavailable
3435
case memoryAllocationFailed
@@ -51,47 +52,52 @@ enum StringZillaError: Error {
5152
/// https://developer.apple.com/documentation/swift/stringprotocol/withcstring(_:)
5253
/// https://developer.apple.com/documentation/swift/stringprotocol/withcstring(encodedas:_:)
5354
/// 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+
///
5758
/// Executes a closure with a pointer to the string's UTF8 C representation and its length.
59+
///
5860
/// - Parameters:
5961
/// - body: A closure that takes a pointer to a C string and its length.
6062
/// - Throws: Can throw an error.
6163
/// - 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
6365

6466
/// Calculates the offset index for a given byte pointer relative to a start pointer.
67+
///
6568
/// - Parameters:
6669
/// - bytePointer: A pointer to the byte for which the offset is calculated.
6770
/// - startPointer: The starting pointer for the calculation, previously obtained from `szScope`.
6871
/// - 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
7073
}
7174

72-
extension String: SZViewable {
73-
public typealias SZIndex = String.Index
75+
extension String: StringZillaViewable {
76+
public typealias Index = String.Index
7477

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)
7781
return try self.withCString { cString in
7882
try body(cString, cLength)
7983
}
8084
}
8185

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)
8489
}
8590
}
8691

87-
extension Substring.UTF8View: SZViewable {
88-
public typealias SZIndex = Substring.UTF8View.Index
92+
extension Substring.UTF8View: StringZillaViewable {
93+
public typealias Index = Substring.UTF8View.Index
8994

9095
/// Executes a closure with a pointer to the UTF8View's contiguous storage of single-byte elements (UTF-8 code units).
9196
/// - Parameters:
9297
/// - body: A closure that takes a pointer to the contiguous storage and its size.
9398
/// - 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 {
95101
return try withContiguousStorageIfAvailable { bufferPointer -> R in
96102
let cLength = sz_size_t(bufferPointer.count)
97103
let cString = UnsafeRawPointer(bufferPointer.baseAddress!).assumingMemoryBound(to: CChar.self)
@@ -106,19 +112,20 @@ extension Substring.UTF8View: SZViewable {
106112
/// - bytePointer: A pointer to the byte for which the offset is calculated.
107113
/// - startPointer: The starting pointer for the calculation, previously obtained from `szScope`.
108114
/// - 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 {
110117
return self.index(self.startIndex, offsetBy: bytePointer - startPointer)
111118
}
112119
}
113120

114-
extension String.UTF8View: SZViewable {
115-
public typealias SZIndex = String.UTF8View.Index
121+
extension String.UTF8View: StringZillaViewable {
122+
public typealias Index = String.UTF8View.Index
116123

117124
/// Executes a closure with a pointer to the UTF8View's contiguous storage of single-byte elements (UTF-8 code units).
118125
/// - Parameters:
119126
/// - body: A closure that takes a pointer to the contiguous storage and its size.
120127
/// - 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 {
122129
return try withContiguousStorageIfAvailable { bufferPointer -> R in
123130
let cLength = sz_size_t(bufferPointer.count)
124131
let cString = UnsafeRawPointer(bufferPointer.baseAddress!).assumingMemoryBound(to: CChar.self)
@@ -133,22 +140,24 @@ extension String.UTF8View: SZViewable {
133140
/// - bytePointer: A pointer to the byte for which the offset is calculated.
134141
/// - startPointer: The starting pointer for the calculation, previously obtained from `szScope`.
135142
/// - 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 {
137144
return self.index(self.startIndex, offsetBy: bytePointer - startPointer)
138145
}
139146
}
140147

141-
public extension SZViewable {
148+
public extension StringZillaViewable {
142149

143150
/// Finds the first occurrence of the specified substring within the receiver.
144151
/// - Parameter needle: The substring to search for.
145152
/// - 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
150159
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)
152161
}
153162
}
154163
}
@@ -158,12 +167,14 @@ public extension SZViewable {
158167
/// Finds the last occurrence of the specified substring within the receiver.
159168
/// - Parameter needle: The substring to search for.
160169
/// - 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
165176
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)
167178
}
168179
}
169180
}
@@ -173,12 +184,14 @@ public extension SZViewable {
173184
/// Finds the first occurrence of the specified character-set members within the receiver.
174185
/// - Parameter characters: A string-like collection of characters to match.
175186
/// - 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
180193
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)
182195
}
183196
}
184197
}
@@ -188,12 +201,14 @@ public extension SZViewable {
188201
/// Finds the last occurrence of the specified character-set members within the receiver.
189202
/// - Parameter characters: A string-like collection of characters to match.
190203
/// - 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
195210
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)
197212
}
198213
}
199214
}
@@ -203,12 +218,14 @@ public extension SZViewable {
203218
/// Finds the first occurrence of a character outside of the the given character-set within the receiver.
204219
/// - Parameter characters: A string-like collection of characters to exclude.
205220
/// - 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
210227
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)
212229
}
213230
}
214231
}
@@ -218,12 +235,14 @@ public extension SZViewable {
218235
/// Finds the last occurrence of a character outside of the the given character-set within the receiver.
219236
/// - Parameter characters: A string-like collection of characters to exclude.
220237
/// - 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
225244
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)
227246
}
228247
}
229248
}
@@ -234,13 +253,15 @@ public extension SZViewable {
234253
/// - Parameter other: A string-like collection of characters to exclude.
235254
/// - Returns: The edit distance, as an unsigned integer.
236255
/// - 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? {
238259
var result: UInt64?
239260

240261
// Use a do-catch block to handle potential errors
241262
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
244265
result = UInt64(sz_edit_distance(hPointer, hLength, nPointer, nLength, sz_size_t(bound), nil))
245266
if result == SZ_SIZE_MAX {
246267
result = nil

0 commit comments

Comments
 (0)