[
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n  pull_request:\n\njobs:\n  Xcode:\n    strategy:\n      matrix:\n        env:\n          - platform: macOS\n            destination: \"platform=macOS\"\n            action: test\n          - platform: iOS\n            destination: \"platform=iOS Simulator,name=iPhone 8\"\n            action: test\n          - platform: tvOS\n            destination: \"platform=tvOS Simulator,name=Apple TV 4K\"\n            action: test\n          # - platform: watchOS\n          #   destination: \"platform=watchOS Simulator,name=Apple Watch Series 4 - 44mm\"\n          #   action: build\n    runs-on: macOS-latest\n    env:\n      ACTION: ${{ matrix.env.action }}\n      DESTINATION: ${{ matrix.env.destination }}\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          submodules: true\n      - name: Xcode Version\n        run: xcodebuild -version\n      - name: Build and Test\n        run: |\n          set -o pipefail\n          xcodebuild \"$ACTION\" \\\n            -scheme SwiftyOpenCC \\\n            -destination \"$DESTINATION\" | xcpretty\n\n  # Mac:\n  #   runs-on: macOS-latest\n  #   steps:\n  #     - uses: actions/checkout@v2\n  #       with:\n  #         submodules: true\n  #     - name: Swift Version\n  #       run: swift -version\n  #     - name: Build and Test\n  #       run: swift test\n\n  # Linux:\n  #   strategy:\n  #     matrix:\n  #       tag: ['5.4']\n  #   runs-on: ubuntu-latest\n  #   container:\n  #     image: swift:${{ matrix.tag }}\n  #   steps:\n  #     - uses: actions/checkout@v1\n  #     - run: git submodule update --init\n  #     - name: Swift Version\n  #       run: swift -version\n  #     - name: Build and Test\n  #       run: swift test --enable-test-discovery\n"
  },
  {
    "path": ".github/workflows/jazzy.yml",
    "content": "name: Jazzy\n\non:\n  push:\n    tags:\n      - v*\n\njobs:\n  build:\n    runs-on: macOS-latest\n    steps:\n      - name: Get Version\n        id: get_version\n        run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}\n      - name: Checkout\n        uses: actions/checkout@v1\n      - name: Install Jazzy\n        run: gem install jazzy --user-install\n      - name: Run Jazzy\n        run: |\n          jazzy \\\n            --clean \\\n            --author ddddxxx \\\n            --github_url https://github.com/$GITHUB_REPOSITORY \\\n            --module-version $VERSION \\\n            --module OpenCC \\\n            --output docs\n        env:\n          VERSION: ${{ steps.get_version.outputs.VERSION }}\n      - name: Deploy\n        run: |\n          cd docs\n          git init\n          git config user.name  \"CI\"\n          git config user.email \"jazzy-ci@github.com\"\n          git remote add secure-origin https://${{ secrets.ACCESS_TOKEN }}@github.com/$GITHUB_REPOSITORY.git\n          git checkout -b gh-pages\n          git add .\n          git commit -m \"Updated docs\"\n          git push --force secure-origin gh-pages\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"OpenCC\"]\n\tpath = OpenCC\n\turl = https://github.com/BYVoid/OpenCC\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 DengXiang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version:5.3\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"SwiftyOpenCC\",\n    products: [\n        .library(\n            name: \"OpenCC\",\n            targets: [\"OpenCC\"]),\n    ],\n    targets: [\n        .target(\n            name: \"OpenCC\",\n            dependencies: [\"copencc\"],\n            resources: [\n                .copy(\"Dictionary\")\n            ]),\n        .testTarget(\n            name: \"OpenCCTests\",\n            dependencies: [\"OpenCC\"],\n            resources: [\n                .copy(\"benchmark\"),\n                .copy(\"testcases\"),\n            ]),\n        .target(\n            name: \"copencc\",\n            exclude: [\n                \"src/benchmark\",\n                \"src/tools\",\n                \"src/BinaryDictTest.cpp\",\n                \"src/Config.cpp\",\n                \"src/ConfigTest.cpp\",\n                \"src/ConversionChainTest.cpp\",\n                \"src/ConversionTest.cpp\",\n                \"src/DartsDictTest.cpp\",\n                \"src/DictGroupTest.cpp\",\n                \"src/MarisaDictTest.cpp\",\n                \"src/MaxMatchSegmentationTest.cpp\",\n                \"src/PhraseExtractTest.cpp\",\n                \"src/SerializedValuesTest.cpp\",\n                \"src/SimpleConverter.cpp\",\n                \"src/SimpleConverterTest.cpp\",\n                \"src/TextDictTest.cpp\",\n                \"src/UTF8StringSliceTest.cpp\",\n                \"src/UTF8UtilTest.cpp\",\n                \"deps/google-benchmark\",\n                \"deps/gtest-1.11.0\",\n                \"deps/pybind11-2.5.0\",\n                \"deps/rapidjson-1.1.0\",\n                \"deps/tclap-1.2.2\",\n                \n                \"src/CmdLineOutput.hpp\",\n                \"src/Config.hpp\",\n                \"src/ConfigTestBase.hpp\",\n                \"src/DictGroupTestBase.hpp\",\n                \"src/SimpleConverter.hpp\",\n                \"src/TestUtils.hpp\",\n                \"src/TestUtilsUTF8.hpp\",\n                \"src/TextDictTestBase.hpp\",\n                \"src/py_opencc.cpp\",\n                \n                // ???\n                \"src/README.md\",\n                \"src/CMakeLists.txt\",\n                \"deps/marisa-0.2.6/AUTHORS\",\n                \"deps/marisa-0.2.6/CMakeLists.txt\",\n                \"deps/marisa-0.2.6/COPYING.md\",\n                \"deps/marisa-0.2.6/README.md\",\n            ],\n            sources: [\n                \"source.cpp\",\n                \"src\",\n                \"deps/marisa-0.2.6\",\n            ],\n            cxxSettings: [\n                .headerSearchPath(\"src\"),\n                .headerSearchPath(\"deps/darts-clone\"),\n                .headerSearchPath(\"deps/marisa-0.2.6/include\"),\n                .headerSearchPath(\"deps/marisa-0.2.6/lib\"),\n                .define(\"ENABLE_DARTS\"),\n            ]),\n    ],\n    cxxLanguageStandard: .cxx14\n)\n"
  },
  {
    "path": "README.md",
    "content": "# Swifty Open Chinese Convert\n\n[![Github CI Status](https://github.com/ddddxxx/SwiftyOpenCC/workflows/CI/badge.svg)](https://github.com/ddddxxx/SwiftyOpenCC/actions)\n![platforms](https://img.shields.io/badge/platforms-Linux%20%7C%20macOS%20%7C%20iOS%20%7C%20tvOS%20%7C%20watchOS-lightgrey.svg)\n[![codebeat badge](https://codebeat.co/badges/39f17620-4f1c-4a46-b3f9-8f5b248ac28f)](https://codebeat.co/projects/github-com-ddddxxx-swiftyopencc-master)\n\nSwift port of [Open Chinese Convert](https://github.com/BYVoid/OpenCC)\n\n## Requirements\n\n- macOS 10.10+ / iOS 8.0+ / tvOS 9.0+ / watchOS 2.0+\n- Swift 5.0\n\n## Usage\n\n### Quick Start\n\n```swift\nimport OpenCC\n\nlet str = \"鼠标里面的硅二极管坏了，导致光标分辨率降低。\"\nlet converter = try! ChineseConverter(option: [.traditionalize, .twStandard, .twIdiom])\nconverter.convert(str)\n// 滑鼠裡面的矽二極體壞了，導致游標解析度降低。\n```\n\n## Documentation\n\n[Github Pages](http://ddddxxx.github.io/SwiftyOpenCC) (100% Documented)\n\n## License\n\nSwiftyOpenCC is available under the MIT license. See the [LICENSE file](LICENSE).\n"
  },
  {
    "path": "Sources/OpenCC/ChineseConverter.swift",
    "content": "//\n//  ChineseConverter.swift\n//  OpenCC\n//\n//  Created by ddddxxx on 2017/3/9.\n//\n\nimport Foundation\nimport copencc\n\n/// The `ChineseConverter` class is used to represent and apply conversion\n/// between Traditional Chinese and Simplified Chinese to Unicode strings.\n/// An instance of this class is an immutable representation of a compiled\n/// conversion pattern.\n///\n/// The `ChineseConverter` supporting character-level conversion, phrase-level\n/// conversion, variant conversion and regional idioms among Mainland China,\n/// Taiwan and HongKong\n///\n/// `ChineseConverter` is designed to be immutable and threadsafe, so that\n/// a single instance can be used in conversion on multiple threads at once.\n/// However, the string on which it is operating should not be mutated\n/// during the course of a conversion.\npublic class ChineseConverter {\n    \n    /// These constants define the ChineseConverter options.\n    public struct Options: OptionSet {\n        \n        public let rawValue: Int\n        \n        public init(rawValue: Int) {\n            self.rawValue = rawValue\n        }\n        \n        /// Convert to Traditional Chinese. (default)\n        public static let traditionalize = Options(rawValue: 1 << 0)\n        \n        /// Convert to Simplified Chinese.\n        public static let simplify = Options(rawValue: 1 << 1)\n        \n        /// Use Taiwan standard.\n        public static let twStandard = Options(rawValue: 1 << 5)\n        \n        /// Use HongKong standard.\n        public static let hkStandard = Options(rawValue: 1 << 6)\n        \n        /// Taiwanese idiom conversion.\n        public static let twIdiom = Options(rawValue: 1 << 10)\n    }\n    \n    private let seg: ConversionDictionary\n    private let chain: [ConversionDictionary]\n    \n    private let converter: CCConverterRef\n    \n    private init(loader: DictionaryLoader, options: Options) throws {\n        seg = try loader.segmentation(options: options)\n        chain = try loader.conversionChain(options: options)\n        var rawChain = chain.map { $0.dict }\n        converter = CCConverterCreate(\"SwiftyOpenCC\", seg.dict, &rawChain, rawChain.count)\n    }\n    \n    /// Returns an initialized `ChineseConverter` instance with the specified\n    /// conversion options.\n    ///\n    /// - Parameter options: The convert’s options.\n    /// - Throws: Throws `ConversionError` if failed.\n    public convenience init(options: Options) throws {\n        let loader = DictionaryLoader(bundle: .module)\n        try self.init(loader: loader, options: options)\n    }\n    \n    /// Return a converted string using the convert’s current option.\n    ///\n    /// - Parameter text: The string to convert.\n    /// - Returns: A converted string using the convert’s current option.\n    public func convert(_ text: String) -> String {\n        let stlStr = CCConverterCreateConvertedStringFromString(converter, text)!\n        defer { STLStringDestroy(stlStr) }\n        return String(utf8String: STLStringGetUTF8String(stlStr))!\n    }\n    \n}\n"
  },
  {
    "path": "Sources/OpenCC/ConversionDictionary.swift",
    "content": "//\n//  ConversionDictionary.swift\n//  OpenCC\n//\n//  Created by ddddxxx on 2020/1/3.\n//\n\nimport Foundation\nimport copencc\n\nclass ConversionDictionary {\n    \n    let group: [ConversionDictionary]\n    \n    let dict: CCDictRef\n    \n    init(path: String) throws {\n        guard let dict = CCDictCreateMarisaWithPath(path) else {\n            throw ConversionError(ccErrorno)\n        }\n        self.group = []\n        self.dict = dict\n    }\n    \n    init(group: [ConversionDictionary]) {\n        var rawGroup = group.map { $0.dict }\n        self.group = group\n        self.dict = CCDictCreateWithGroup(&rawGroup, rawGroup.count)\n    }\n}\n"
  },
  {
    "path": "Sources/OpenCC/ConversionError.swift",
    "content": "//\n//  ConversionError.swift\n//  OpenCC\n//\n//  Created by ddddxxx on 2020/1/3.\n//\n\nimport Foundation\nimport copencc\n\npublic enum ConversionError: Error {\n    \n    case fileNotFound\n    \n    case invalidFormat\n    \n    case invalidTextDictionary\n    \n    case invalidUTF8\n    \n    case unknown\n    \n    init(_ code: CCErrorCode) {\n        switch code {\n        case .fileNotFound:\n            self = .fileNotFound\n        case .invalidFormat:\n            self = .invalidFormat\n        case .invalidTextDictionary:\n            self = .invalidTextDictionary\n        case .invalidUTF8:\n            self = .invalidUTF8\n        case .unknown, _:\n            self = .unknown\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/OpenCC/DictionaryLoader.swift",
    "content": "//\n//  DictionaryLoader.swift\n//  OpenCC\n//\n//  Created by ddddxxx on 2018/5/5.\n//\n\nimport Foundation\nimport copencc\n\nextension ChineseConverter {\n    \n    struct DictionaryLoader {\n        \n        private static let subdirectory = \"Dictionary\"\n        private static let dictCache = WeakValueCache<String, ConversionDictionary>()\n        \n        private let bundle: Bundle\n        \n        init(bundle: Bundle) {\n            self.bundle = bundle\n        }\n        \n        func dict(_ name: ChineseConverter.DictionaryName) throws -> ConversionDictionary {\n            guard let path = bundle.path(forResource: name.description, ofType: \"ocd2\", inDirectory: DictionaryLoader.subdirectory) else {\n                throw ConversionError.fileNotFound\n            }\n            return try DictionaryLoader.dictCache.value(for: path) {\n                return try ConversionDictionary(path: path)\n            }\n        }\n    }\n}\n\nextension ChineseConverter.DictionaryLoader {\n    \n    func segmentation(options: ChineseConverter.Options) throws -> ConversionDictionary {\n        let dictName = options.segmentationDictName\n        return try dict(dictName)\n    }\n    \n    func conversionChain(options: ChineseConverter.Options) throws -> [ConversionDictionary] {\n        return try options.conversionChain.compactMap { names in\n            switch names.count {\n            case 0:\n                return nil\n            case 1:\n                return try dict(names.first!)\n            case _:\n                let dicts = try names.map(dict)\n                return ConversionDictionary(group: dicts)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/OpenCC/DictionaryName.swift",
    "content": "//\n//  DictionaryName.swift\n//  OpenCC\n//\n//  Created by ddddxxx on 2019/9/16.\n//\n\nimport Foundation\n\nextension ChineseConverter {\n    \n    enum DictionaryName: CustomStringConvertible {\n        \n        case hkVariants\n        case hkVariantsRev\n        case hkVariantsRevPhrases\n        case jpVariants\n        case stCharacters\n        case stPhrases\n        case tsCharacters\n        case tsPhrases\n        case twPhrases\n        case twPhrasesRev\n        case twVariants\n        case twVariantsRev\n        case twVariantsRevPhrases\n        \n        var description: String {\n            switch self {\n            case .hkVariants: return \"HKVariants\"\n            case .hkVariantsRev: return \"HKVariantsRev\"\n            case .hkVariantsRevPhrases: return \"HKVariantsRevPhrases\"\n            case .jpVariants: return \"JPVariants\"\n            case .stCharacters: return \"STCharacters\"\n            case .stPhrases: return \"STPhrases\"\n            case .tsCharacters: return \"TSCharacters\"\n            case .tsPhrases: return \"TSPhrases\"\n            case .twPhrases: return \"TWPhrases\"\n            case .twPhrasesRev: return \"TWPhrasesRev\"\n            case .twVariants: return \"TWVariants\"\n            case .twVariantsRev: return \"TWVariantsRev\"\n            case .twVariantsRevPhrases: return \"TWVariantsRevPhrases\"\n            }\n        }\n    }\n}\n\nextension ChineseConverter.Options {\n    \n    var segmentationDictName: ChineseConverter.DictionaryName {\n        if contains(.traditionalize) {\n            return .stPhrases\n        } else if contains(.simplify) {\n            return .tsPhrases\n        } else if contains(.hkStandard) {\n            return .hkVariants\n        } else if contains(.twStandard) {\n            return .twVariants\n        } else {\n            return .stPhrases\n        }\n    }\n    \n    var conversionChain: [[ChineseConverter.DictionaryName]] {\n        var result: [[ChineseConverter.DictionaryName]] = []\n        if contains(.traditionalize) {\n            result.append([.stPhrases, .stCharacters])\n            if contains(.twIdiom) {\n                result.append([.twPhrases])\n            }\n            if contains(.hkStandard) {\n                result.append([.hkVariants])\n            } else if contains(.twStandard) {\n                result.append([.twVariants])\n            }\n        } else if contains(.simplify) {\n            if contains(.hkStandard) {\n                result.append([.hkVariantsRevPhrases, .hkVariantsRev])\n            } else if contains(.twStandard) {\n                result.append([.twVariantsRevPhrases, .twVariantsRev])\n            }\n            if contains(.twIdiom) {\n                result.append([.twPhrasesRev])\n            }\n            result.append([.tsPhrases, .tsCharacters])\n        } else {\n            if contains(.hkStandard) {\n                result.append([.hkVariants])\n            } else if contains(.twStandard) {\n                result.append([.twVariants])\n            }\n        }\n        if result.isEmpty {\n            return [[.stPhrases, .stCharacters]]\n        }\n        return result\n    }\n}\n"
  },
  {
    "path": "Sources/OpenCC/WeakValueCache.swift",
    "content": "//\n//  WeakValueCache.swift\n//  OpenCC\n//\n//  Created by ddddxxx on 2020/1/3.\n//\n\nimport Foundation\n\nclass WeakBox<Value: AnyObject> {\n    \n    private(set) weak var value: Value?\n    \n    init(_ value: Value) {\n        self.value = value\n    }\n}\n\nclass WeakValueCache<Key: Hashable, Value: AnyObject> {\n    \n    private var storage: [Key: WeakBox<Value>] = [:]\n    \n    private var lock = NSLock()\n    \n    func value(for key: Key) -> Value? {\n        return storage[key]?.value\n    }\n    \n    func value(for key: Key, make: () throws -> Value) rethrows -> Value {\n        if let value = storage[key]?.value {\n            return value\n        }\n        lock.lock()\n        defer { lock.unlock() }\n        if let value = storage[key]?.value {\n            return value\n        }\n        let value = try make()\n        storage[key] = WeakBox(value)\n        return value\n    }\n}\n"
  },
  {
    "path": "Sources/copencc/include/header.h",
    "content": "#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\"\n{\n#endif\n\n// MARK: Error\n\nenum CCErrorCode {\n    CCErrorCodeFileNotFound = 1,\n    CCErrorCodeInvalidFormat,\n    CCErrorCodeInvalidTextDictionary,\n    CCErrorCodeInvalidUTF8,\n    CCErrorCodeUnknown,\n} __attribute__((enum_extensibility(open)));\n\ntypedef enum CCErrorCode CCErrorCode;\n\nCCErrorCode ccErrorno;\n\n// MARK: CCDict\n\ntypedef void* CCDictRef;\n\nCCDictRef _Nullable CCDictCreateDartsWithPath(const char * _Nonnull path);\n\nCCDictRef _Nullable CCDictCreateMarisaWithPath(const char * _Nonnull path);\n\nCCDictRef _Nonnull CCDictCreateWithGroup(CCDictRef _Nonnull * const _Nonnull dictGroup, intptr_t count);\n\nvoid CCDictDestroy(CCDictRef _Nonnull dict);\n\n// MARK: CCConverter\n\ntypedef void* CCConverterRef;\n\nCCConverterRef _Nonnull CCConverterCreate(const char * _Nonnull name, CCDictRef _Nonnull segmentation, CCDictRef _Nonnull * const _Nonnull conversionChain, intptr_t chainCount);\n\nvoid CCConverterDestroy(CCConverterRef _Nonnull dict);\n\ntypedef void* STLString;\n\nSTLString _Nullable CCConverterCreateConvertedStringFromString(CCConverterRef _Nonnull converter, const char * _Nonnull str);\n\nconst char* _Nonnull STLStringGetUTF8String(STLString _Nonnull str);\n\nvoid STLStringDestroy(STLString _Nonnull str);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "Sources/copencc/include/module.modulemap",
    "content": "module copencc {\n    header \"header.h\"\n    export *\n}\n"
  },
  {
    "path": "Sources/copencc/source.cpp",
    "content": "#include \"DartsDict.hpp\"\n#include \"DictGroup.hpp\"\n#include \"Converter.hpp\"\n#include \"MarisaDict.hpp\"\n#include \"MaxMatchSegmentation.hpp\"\n#include \"Conversion.hpp\"\n#include \"ConversionChain.hpp\"\n\n#include \"header.h\"\n\n// MARK: Error\n\nvoid* catchOpenCCException(void* (^block)()) {\n    try {\n        return block();\n    } catch (opencc::FileNotFound& ex) {\n        ccErrorno = CCErrorCodeFileNotFound;\n        return NULL;\n    } catch (opencc::InvalidFormat& ex) {\n        ccErrorno = CCErrorCodeInvalidFormat;\n        return NULL;\n    } catch (opencc::InvalidTextDictionary& ex) {\n        ccErrorno = CCErrorCodeInvalidTextDictionary;\n        return NULL;\n    } catch (opencc::InvalidUTF8& ex) {\n        ccErrorno = CCErrorCodeInvalidUTF8;\n        return NULL;\n    } catch (opencc::Exception& ex) {\n        ccErrorno = CCErrorCodeUnknown;\n        return NULL;\n    }\n}\n\n// MARK: CCDict\n\nCCDictRef _Nullable CCDictCreateDartsWithPath(const char * _Nonnull path) {\n    return catchOpenCCException(^{\n        auto dict = opencc::SerializableDict::NewFromFile<opencc::DartsDict>(std::string(path));\n        auto dictPtr = new opencc::DictPtr(dict);\n        return static_cast<void*>(dictPtr);\n    });\n}\n\nCCDictRef _Nullable CCDictCreateMarisaWithPath(const char * _Nonnull path) {\n    return catchOpenCCException(^{\n        auto dict = opencc::SerializableDict::NewFromFile<opencc::MarisaDict>(std::string(path));\n        auto dictPtr = new opencc::DictPtr(dict);\n        return static_cast<void*>(dictPtr);\n    });\n}\n\nCCDictRef _Nonnull CCDictCreateWithGroup(CCDictRef _Nonnull * const _Nonnull dictGroup, intptr_t count) {\n    std::list<opencc::DictPtr> list;\n    for (int i=0; i<count; i++) {\n        auto *dictPtr = static_cast<opencc::DictPtr*>(dictGroup[i]);\n        list.push_back(*dictPtr);\n    }\n    auto dict = new opencc::DictGroupPtr(new opencc::DictGroup(list));\n    return static_cast<void*>(dict);\n}\n\nvoid CCDictDestroy(CCDictRef _Nonnull dict) {\n    auto *dictPtr = static_cast<opencc::DictPtr*>(dict);\n    dictPtr->reset();\n}\n\n// MARK: CCConverter\n\nCCConverterRef _Nonnull CCConverterCreate(const char * _Nonnull name, CCDictRef _Nonnull segmentation, CCDictRef _Nonnull * const _Nonnull conversionChain, intptr_t chainCount) {\n    auto *segmentationPtr = static_cast<opencc::DictPtr*>(segmentation);\n    std::list<opencc::ConversionPtr> conversions;\n    for (int i=0; i<chainCount; i++) {\n        auto *dictPtr = static_cast<opencc::DictPtr*>(conversionChain[i]);\n        auto conversion = opencc::ConversionPtr(new opencc::Conversion(*dictPtr));\n        conversions.push_back(conversion);\n    }\n    auto covName = std::string(name);\n    auto covSeg = opencc::SegmentationPtr(new opencc::MaxMatchSegmentation(*segmentationPtr));\n    auto covChain = opencc::ConversionChainPtr(new opencc::ConversionChain(conversions));\n    auto converter = new opencc::Converter(covName, covSeg, covChain);\n    return static_cast<void*>(converter);\n}\n\nvoid CCConverterDestroy(CCConverterRef _Nonnull dict) {\n    auto converter = static_cast<opencc::Converter*>(dict);\n    delete converter;\n}\n\nSTLString _Nullable CCConverterCreateConvertedStringFromString(CCConverterRef _Nonnull converter, const char * _Nonnull str) {\n    return catchOpenCCException(^{\n        auto converterPtr = static_cast<opencc::Converter*>(converter);\n        auto string = new std::string(converterPtr->Convert(str));\n        return static_cast<void*>(string);\n    });\n}\n\nconst char* _Nonnull STLStringGetUTF8String(STLString _Nonnull str) {\n    auto string = static_cast<std::string*>(str);\n    return string->c_str();\n}\n\nvoid STLStringDestroy(STLString _Nonnull str) {\n    auto string = static_cast<std::string*>(str);\n    delete string;\n}\n"
  },
  {
    "path": "Tests/LinuxMain.swift",
    "content": "fatalError(\"Run the tests with `swift test --enable-test-discovery`.\")\n"
  },
  {
    "path": "Tests/OpenCCTests/OpenCCTests.swift",
    "content": "import XCTest\n@testable import OpenCC\n\nlet testCases: [(String, ChineseConverter.Options)] = [\n    (\"s2t\", [.traditionalize]),\n    (\"t2s\", [.simplify]),\n    (\"s2hk\", [.traditionalize, .hkStandard]),\n    (\"hk2s\", [.simplify, .hkStandard]),\n    (\"s2tw\", [.traditionalize, .twStandard]),\n    (\"tw2s\", [.simplify, .twStandard]),\n    (\"s2twp\", [.traditionalize, .twStandard, .twIdiom]),\n    (\"tw2sp\", [.simplify, .twStandard, .twIdiom]),\n]\n\nclass OpenCCTests: XCTestCase {\n    \n    func converter(option: ChineseConverter.Options) throws -> ChineseConverter {\n        return try ChineseConverter(options: option)\n    }\n    \n    func testConversion() throws {\n        func testCase(name: String, ext: String) -> String {\n            let url = Bundle.module.url(forResource: name, withExtension: ext, subdirectory: \"testcases\")!\n            return try! String(contentsOf: url)\n        }\n        for (name, opt) in testCases {\n            let coverter = try ChineseConverter(options: opt)\n            let input = testCase(name: name, ext: \"in\")\n            let converted = coverter.convert(input)\n            let output = testCase(name: name, ext: \"ans\")\n            XCTAssertEqual(converted, output, \"Conversion \\(name) fails\")\n        }\n    }\n    \n    func testConverterCreationPerformance() {\n        let options: ChineseConverter.Options = [.traditionalize, .twStandard, .twIdiom]\n        measure {\n            for _ in 0..<10 {\n                _ = try! ChineseConverter(options: options)\n            }\n        }\n    }\n    \n    func testDictionaryCache() {\n        let options: ChineseConverter.Options = [.traditionalize, .twStandard, .twIdiom]\n        let holder = try! ChineseConverter(options: options)\n        measure {\n            for _ in 0..<1_000 {\n                _ = try! ChineseConverter(options: options)\n            }\n        }\n        _ = holder.convert(\"foo\")\n    }\n    \n    func testConversionPerformance() throws {\n        let cov = try converter(option: [.traditionalize, .twStandard, .twIdiom])\n        let url = Bundle.module.url(forResource: \"zuozhuan\", withExtension: \"txt\", subdirectory: \"benchmark\")!\n        // 1.9 MB, 624k word\n        let str = try String(contentsOf: url)\n        measure {\n            _ = cov.convert(str)\n        }\n    }\n}\n"
  }
]