Skip to content

Commit b105ed6

Browse files
committed
1 parent 3785950 commit b105ed6

File tree

8 files changed

+361
-0
lines changed

8 files changed

+361
-0
lines changed

.github/workflows/rundemo.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: swift-foundation-icu-demo
2+
on:
3+
push:
4+
branches: [ main ]
5+
workflow_dispatch:
6+
pull_request:
7+
branches:
8+
- '*'
9+
jobs:
10+
linux:
11+
runs-on: ubuntu-24.04
12+
timeout-minutes: 30
13+
steps:
14+
- name: Checkout swift-foundation-icu-demo
15+
uses: actions/checkout@v4
16+
with:
17+
path: swift-foundation-icu-demo
18+
- name: Checkout swift-foundation
19+
uses: actions/checkout@v4
20+
with:
21+
repository: swiftlang/swift-foundation
22+
path: swift-corelibs-foundation
23+
- name: Checkout swift-corelibs-foundation
24+
uses: actions/checkout@v4
25+
with:
26+
repository: swiftlang/swift-corelibs-foundation
27+
path: swift-corelibs-foundation
28+
- name: Checkout swift-collections
29+
uses: actions/checkout@v4
30+
with:
31+
repository: apple/swift-collections
32+
path: swift-collections
33+
- name: Checkout swift-foundation-icu
34+
uses: actions/checkout@v4
35+
with:
36+
repository: swiftlang/swift-foundation-icu
37+
path: swift-corelibs-foundation-icu
38+
- name: "Test Swift Package on Linux"
39+
run: swift test --package-path swift-foundation-icu-demo
40+
- name: Apply swift-corelibs-foundation-icu PR #53
41+
working-directory: swift-corelibs-foundation-icu
42+
run: gh pr checkout 53
43+
- name: "Build Foundation"
44+
working-directory: swift-foundation
45+
run: SWIFTCI_USE_LOCAL_DEPS=.. swift build
46+
- name: "Run Foundation InternationalizationBenchmarks"
47+
working-directory: swift-foundation/Benchmarks
48+
run: SWIFTCI_USE_LOCAL_DEPS=.. swift run InternationalizationBenchmarks
49+
50+

.gitignore

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Xcode
2+
#
3+
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4+
5+
## User settings
6+
xcuserdata/
7+
8+
xcodebuild*.log
9+
10+
java_pid*.hprof
11+
12+
.*.swp
13+
.DS_Store
14+
15+
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
16+
*.xcscmblueprint
17+
*.xccheckout
18+
19+
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
20+
build/
21+
DerivedData/
22+
.android/
23+
.kotlin/
24+
*.moved-aside
25+
*.pbxuser
26+
!default.pbxuser
27+
*.mode1v3
28+
!default.mode1v3
29+
*.mode2v3
30+
!default.mode2v3
31+
*.perspectivev3
32+
!default.perspectivev3
33+
34+
## Obj-C/Swift specific
35+
*.hmap
36+
37+
## App packaging
38+
*.ipa
39+
*.dSYM.zip
40+
*.dSYM
41+
42+
## Playgrounds
43+
timeline.xctimeline
44+
playground.xcworkspace
45+
46+
# Swift Package Manager
47+
#
48+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
49+
Packages/
50+
Package.pins
51+
Package.resolved
52+
*.xcodeproj
53+
54+
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
55+
# hence it is not needed unless you have added a package configuration file to your project
56+
.swiftpm
57+
58+
.build/
59+
60+
# CocoaPods
61+
#
62+
# We recommend against adding the Pods directory to your .gitignore. However
63+
# you should judge for yourself, the pros and cons are mentioned at:
64+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
65+
#
66+
# Pods/
67+
#
68+
# Add this line if you want to avoid checking in source code from the Xcode workspace
69+
# *.xcworkspace
70+
71+
# Carthage
72+
#
73+
# Add this line if you want to avoid checking in source code from Carthage dependencies.
74+
# Carthage/Checkouts
75+
76+
Carthage/Build/
77+
78+
# Accio dependency management
79+
Dependencies/
80+
.accio/
81+
82+
# fastlane
83+
#
84+
# It is recommended to not store the screenshots in the git repo.
85+
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
86+
# For more information about the recommended setup visit:
87+
# https://docs.fastlane.tools/best-practices/source-control/#source-control
88+
89+
fastlane/report.xml
90+
fastlane/Preview.html
91+
fastlane/screenshots/**/*.png
92+
fastlane/test_output
93+
94+
# Code Injection
95+
#
96+
# After new code Injection tools there's a generated folder /iOSInjectionProject
97+
# https://github.com/johnno1962/injectionforxcode
98+
99+
iOSInjectionProject/
100+
101+
102+
103+
# Ignore Gradle project-specific cache directory
104+
.gradle
105+
106+
# Ignore Gradle build output directory
107+
build
108+
109+
# gradle properties
110+
local.properties
31.4 MB
Binary file not shown.

Package.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// swift-tools-version: 6.0
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "FoundationICUDemo",
6+
products: [
7+
.library(
8+
name: "FoundationICUDemo",
9+
targets: ["FoundationICUDemo"]),
10+
],
11+
dependencies: [
12+
//.package(url: "https://github.com/skiptools/swift-foundation-icu", branch: "main")
13+
.package(path: "../swift-foundation-icu")
14+
],
15+
targets: [
16+
.target(
17+
name: "FoundationICUDemo",
18+
dependencies: [.product(name: "_FoundationICU", package: "swift-foundation-icu")]),
19+
.testTarget(
20+
name: "FoundationICUDemoTests",
21+
dependencies: ["FoundationICUDemo"]
22+
),
23+
]
24+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// The Swift Programming Language
2+
// https://docs.swift.org/swift-book
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import _FoundationICU
2+
#if canImport(Darwin)
3+
import Darwin
4+
#elseif canImport(Glibc)
5+
import Glibc
6+
#endif
7+
8+
print("Foundation ICU Tool!")
9+
10+
func isAcceptable(context: UnsafeMutableRawPointer?,
11+
type: UnsafePointer<CChar>?,
12+
name: UnsafePointer<CChar>?,
13+
pInfo: UnsafePointer<UDataInfo>?) -> UBool {
14+
UBool(1) // always acceptable
15+
}
16+
17+
// do not attempt to load from the file system
18+
udata_setFileAccess(UDATA_NO_FILES, nil)
19+
20+
for ttype in ["dat", "icudt74l", "icu", "icudt", "res", "icu.icudt", "fr_FR"] {
21+
for name in ["icudt74l", "dat", "icu", "icudt", "res", "icu.icudt", "fr", "fr_FR", "", "."] {
22+
print("### trying ttype=\(ttype) name=\(name)")
23+
24+
var status = U_ZERO_ERROR
25+
let icuData = udata_openChoice(U_ICUDATA_ALIAS, ttype, name, isAcceptable, nil, &status)
26+
27+
// let rawData = udata_getMemory(icuData)
28+
// var rawInfo: UDataInfo = .init()
29+
// udata_getInfo(icuData, &rawInfo)
30+
31+
//var size: size_t = icuDa
32+
33+
print("data=\(icuData) opened type=\(ttype) name=\(name) with status \(status)")
34+
35+
if let icuData {
36+
let length = udata_getLength(icuData)
37+
let raw = udata_getRawMemory(icuData)
38+
print("")
39+
}
40+
}
41+
}
42+
43+
//let x = U_ICUDATA_ENTRY_POINT
44+
45+
46+
//guard let sym = dlsym(handle, "gCommonICUDataArray") else {
47+
48+
//let handle = dlopen(nil, RTLD_NOW)
49+
//defer { dlclose(handle) }
50+
//
51+
//let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
52+
//
53+
//let symbolPtr = dlsym(RTLD_DEFAULT, "udata_findCachedData")
54+
//print("Loaded udata_findCachedData: \(symbolPtr)")
55+
56+
57+
//udata_findCachedData
58+
59+
//udata_getMemory(nil)

Swift-ICU.xcworkspace/contents.xcworkspacedata

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import XCTest
2+
@testable import FoundationICUDemo
3+
import _FoundationICU
4+
#if canImport(Darwin)
5+
import Darwin
6+
#elseif canImport(Android)
7+
import Android
8+
#endif
9+
10+
class TestFoundationICUDemo: XCTestCase {
11+
func testICUFunctions() throws {
12+
//setenv("ICU_DATA_DIR_PREFIX", "/opt/src/github/skiptools/swift-foundation-icu-demo/LOCALE_DATA", 1)
13+
14+
let defloc = uloc_getDefault()!
15+
16+
XCTAssertEqual(String(validatingCString: defloc), "en_US_POSIX")
17+
XCTAssertEqual(uloc_getISO3Country(defloc).flatMap(String.init(validatingCString:)), "USA")
18+
19+
let name = try withICUString(ULOC_FULLNAME_CAPACITY) { uloc_getName(defloc, $0, $1, $2) }
20+
XCTAssertEqual(name, "en_US_POSIX")
21+
let country = try withICUString(ULOC_COUNTRY_CAPACITY) { uloc_getCountry(defloc, $0, $1, $2) }
22+
XCTAssertEqual(country, "US")
23+
let language = try withICUString(ULOC_LANG_CAPACITY) { uloc_getLanguage(defloc, $0, $1, $2) }
24+
XCTAssertEqual(language, "en")
25+
26+
//let testString = "This is a simple Test string"
27+
// seems to use a wide char
28+
//let upper = try withICUString(.init(testString.count)) { u_strToUpper($0, $1, testString, .init(testString.count), defloc, $2) }
29+
//XCTAssertEqual(upper, testString.uppercased())
30+
31+
let numberUK = try formatNumber(locale: "en_UK", style: UNUM_DECIMAL, number: 1234.5678)
32+
XCTAssertEqual(numberUK, "1,234.568")
33+
34+
let currencyUK = try formatNumber(locale: "en_UK", style: UNUM_CURRENCY, number: 1234.5678)
35+
XCTAssertEqual(currencyUK, "¤1,234.5")
36+
37+
let spelloutUK = try formatNumber(locale: "en_UK", style: UNUM_SPELLOUT, number: 1234.5678)
38+
XCTAssertEqual(spelloutUK, "one thousand two hundred thirty-four point five six seven eight")
39+
40+
let numberFR = try formatNumber(locale: "fr_FR", style: UNUM_DECIMAL, number: 1234.5678)
41+
XCTAssertEqual(numberFR, "1 234,5")
42+
43+
let currencyFR = try formatNumber(locale: "fr_FR", style: UNUM_CURRENCY, number: 1234.5678)
44+
XCTAssertEqual(currencyFR, "1 234,57")
45+
46+
let spelloutFR = try formatNumber(locale: "fr_FR", style: UNUM_SPELLOUT, number: 1234.5678)
47+
XCTAssertEqual(spelloutFR, "mille deux cent trente-quatre virgule cinq six sept huit")
48+
}
49+
}
50+
51+
func formatNumber(locale: String, style: UNumberFormatStyle, number: Double) throws -> String? {
52+
let numberFormat = try tryICU { unum_open(style, nil, 0, locale, nil, $0) }
53+
defer { unum_close(numberFormat) }
54+
let result = try withICUWideString(1024) { unum_formatDouble(numberFormat, number, $0, $1, nil, $2) }
55+
return result
56+
}
57+
58+
/// Try to perform an operation that might set an ICU `UErrorCode` pointer,
59+
/// and throw an error if an error occurs.
60+
func tryICU<T>(_ function: (_ err: UnsafeMutablePointer<UErrorCode>) -> T) throws -> T {
61+
var err = U_ZERO_ERROR
62+
let result = function(&err)
63+
if err.rawValue <= U_ZERO_ERROR.rawValue {
64+
return result
65+
} else {
66+
throw ICUError(errorCode: err)
67+
}
68+
}
69+
70+
/// Allocates a buffer with the given capacity and executes the closure,
71+
/// returning the result and deallocating the buffer.
72+
func withAllocatedBuffer<T, U>(_ capacity: Int32, _ function: (_ buffer: UnsafeMutablePointer<T>, _ capacity: Int32, _ err: UnsafeMutablePointer<UErrorCode>) -> U) throws -> U {
73+
let buffer = UnsafeMutablePointer<T>.allocate(capacity: Int(capacity))
74+
defer { buffer.deallocate() }
75+
return try tryICU { function(buffer, capacity, $0) }
76+
}
77+
78+
func withICUString(_ capacity: Int32 = 1024, _ function: (_ buffer: UnsafeMutablePointer<CChar>, _ capacity: Int32, _ err: UnsafeMutablePointer<UErrorCode>) -> Int32) throws -> String? {
79+
try withAllocatedBuffer(capacity) { buffer, capacity, err in
80+
_ = function(buffer, capacity, err)
81+
return String(validatingCString: buffer)
82+
}
83+
}
84+
85+
func withICUWideString(_ capacity: Int32 = 1024, _ function: (_ buffer: UnsafeMutablePointer<UChar>, _ capacity: Int32, _ err: UnsafeMutablePointer<UErrorCode>) -> Int32) throws -> String? {
86+
try withAllocatedBuffer(capacity) { buffer, capacity, err in
87+
let bufferSize = function(buffer, capacity, err)
88+
// TODO: handle buffer overflow with U_BUFFER_OVERFLOW_ERROR by reallocating larger buffer?
89+
let buffer2 = UnsafeMutablePointer<CChar>.allocate(capacity: Int(bufferSize * 2))
90+
defer { buffer2.deallocate() }
91+
// convert the UChar into a C String
92+
return String(validatingCString: u_austrncpy(buffer2, buffer, bufferSize))
93+
}
94+
}
95+
96+
struct ICUError : Error, CustomDebugStringConvertible {
97+
let errorCode: UErrorCode
98+
99+
var debugDescription: String {
100+
if let desc = u_errorName(errorCode).flatMap(String.init(validatingCString:)) {
101+
return "UErrorCode \(errorCode.rawValue): \(desc)"
102+
} else {
103+
return "UErrorCode \(errorCode.rawValue)"
104+
}
105+
}
106+
}

0 commit comments

Comments
 (0)