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
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
internal import _FoundationICU
#if canImport(FoundationEssentials)
import FoundationEssentials
#endif
extension ICU {
final class CaseMap : @unchecked Sendable {
let casemap: OpaquePointer
let lock: LockedState<Void>
// Empty locale ("") means root locale
init(localeID: String) throws {
var status = U_ZERO_ERROR
casemap = ucasemap_open(localeID, UInt32(), &status)
try status.checkSuccess()
lock = LockedState()
}
deinit {
ucasemap_close(casemap)
}
private static let _cache: LockedState<[String : CaseMap]> = LockedState(initialState: [:])
// Create and cache a new case mapping object for the specified locale
internal static func caseMappingForLocale(_ localeID: String?) -> CaseMap? {
let localeID = localeID ?? ""
if let cached = _cache.withLock({ cache in cache[localeID] }) {
return cached
}
guard let new = try? CaseMap(localeID: localeID) else {
return nil
}
_cache.withLock { cache in
cache[localeID] = new
}
return new
}
func lowercase(_ s: String) -> String? {
s.utf8CString.withUnsafeBufferPointer { srcBuf in
_withResizingCharBuffer { destBuf, destSize, status in
ucasemap_utf8ToLower(casemap, destBuf, destSize, srcBuf.baseAddress!, Int32(srcBuf.count), &status)
}
}
}
func uppercase(_ s: String) -> String? {
s.utf8CString.withUnsafeBufferPointer { srcBuf in
_withResizingCharBuffer { destBuf, destSize, status in
ucasemap_utf8ToUpper(casemap, destBuf, destSize, srcBuf.baseAddress!, Int32(srcBuf.count), &status)
}
}
}
func titlecase(_ s: Substring) -> String? {
lock.withLock {
var s = s
return s.withUTF8 { srcBuf in
srcBuf.withMemoryRebound(to: CChar.self) { buffer in
_withResizingCharBuffer { destBuf, destSize, status in
ucasemap_utf8ToTitle(casemap, destBuf, destSize, buffer.baseAddress!, Int32(buffer.count), &status)
}
}
}
}
}
func titlecase(_ s: String) -> String? {
// `ucasemap_utf8ToTitle` isn't thread-safe
lock.withLock {
s.utf8CString.withUnsafeBufferPointer { srcBuf in
_withResizingCharBuffer { destBuf, destSize, status in
ucasemap_utf8ToTitle(casemap, destBuf, destSize, srcBuf.baseAddress!, Int32(srcBuf.count), &status)
}
}
}
}
func foldcase(_ s: String) -> String? {
s.utf8CString.withUnsafeBufferPointer { srcBuf in
_withResizingCharBuffer { destBuf, destSize, status in
ucasemap_utf8FoldCase(casemap, destBuf, destSize, srcBuf.baseAddress!, Int32(srcBuf.count), &status)
}
}
}
}
}
|