Hotenka // Patch a memory leak.
This commit is contained in:
parent
af8b15e170
commit
a66879f7bf
|
@ -1,4 +1,5 @@
|
||||||
// swift-tools-version:5.3
|
// swift-tools-version:5.3
|
||||||
|
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
|
|
|
@ -66,13 +66,10 @@ public class HotenkaChineseConverter {
|
||||||
private(set) var dict: [String: [String: String]]
|
private(set) var dict: [String: [String: String]]
|
||||||
private var dictFiles: [String: [String]]
|
private var dictFiles: [String: [String]]
|
||||||
var ptrSQL: OpaquePointer?
|
var ptrSQL: OpaquePointer?
|
||||||
var ptrStatement: OpaquePointer?
|
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
sqlite3_finalize(ptrStatement)
|
|
||||||
sqlite3_close_v2(ptrSQL)
|
sqlite3_close_v2(ptrSQL)
|
||||||
ptrSQL = nil
|
ptrSQL = nil
|
||||||
ptrStatement = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(sqliteDir dbPath: String) {
|
public init(sqliteDir dbPath: String) {
|
||||||
|
@ -180,8 +177,13 @@ public class HotenkaChineseConverter {
|
||||||
|
|
||||||
public func query(dict dictType: DictType, key searchKey: String) -> String? {
|
public func query(dict dictType: DictType, key searchKey: String) -> String? {
|
||||||
guard ptrSQL != nil else { return dict[dictType.rawKeyString]?[searchKey] }
|
guard ptrSQL != nil else { return dict[dictType.rawKeyString]?[searchKey] }
|
||||||
|
var ptrStatement: OpaquePointer?
|
||||||
let sqlQuery = "SELECT * FROM DATA_HOTENKA WHERE dict=\(dictType.rawValue) AND theKey='\(searchKey)';"
|
let sqlQuery = "SELECT * FROM DATA_HOTENKA WHERE dict=\(dictType.rawValue) AND theKey='\(searchKey)';"
|
||||||
sqlite3_prepare_v2(ptrSQL, sqlQuery, -1, &ptrStatement, nil)
|
sqlite3_prepare_v2(ptrSQL, sqlQuery, -1, &ptrStatement, nil)
|
||||||
|
defer {
|
||||||
|
sqlite3_finalize(ptrStatement)
|
||||||
|
ptrStatement = nil
|
||||||
|
}
|
||||||
// 此處只需要用到第一筆結果。
|
// 此處只需要用到第一筆結果。
|
||||||
while sqlite3_step(ptrStatement) == SQLITE_ROW {
|
while sqlite3_step(ptrStatement) == SQLITE_ROW {
|
||||||
guard let rawValue = sqlite3_column_text(ptrStatement, 2) else { continue }
|
guard let rawValue = sqlite3_column_text(ptrStatement, 2) else { continue }
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
// Swiftified by (c) 2022 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// Rebranded from (c) Nick Chen's Obj-C library "NCChineseConverter" (MIT License).
|
||||||
|
/*
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
1. The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
2. No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
requirements above.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SQLite3
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import Hotenka
|
||||||
|
|
||||||
|
private let packageRootPath = URL(fileURLWithPath: #file).pathComponents.prefix(while: { $0 != "Tests" }).joined(
|
||||||
|
separator: "/"
|
||||||
|
).dropFirst()
|
||||||
|
|
||||||
|
private let testDataPath: String = packageRootPath + "/Tests/TestDictData/"
|
||||||
|
|
||||||
|
extension HotenkaTests {
|
||||||
|
func testGeneratingSQLiteDB() throws {
|
||||||
|
NSLog("// Start loading from: \(packageRootPath)")
|
||||||
|
let testInstance: HotenkaChineseConverter = .init(dictDir: testDataPath)
|
||||||
|
NSLog("// Loading complete. Generating SQLite database.")
|
||||||
|
var ptrSQL: OpaquePointer?
|
||||||
|
let dbPath = testDataPath + "convdict.sqlite"
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
sqlite3_open(dbPath, &ptrSQL) == SQLITE_OK,
|
||||||
|
"HOTENKA: SQLite Database Initialization Error."
|
||||||
|
)
|
||||||
|
XCTAssertTrue(
|
||||||
|
sqlite3_exec(ptrSQL, "PRAGMA synchronous = OFF;", nil, nil, nil) == SQLITE_OK,
|
||||||
|
"HOTENKA: SQLite synchronous OFF failed."
|
||||||
|
)
|
||||||
|
|
||||||
|
let sqlMakeTableHotenka = """
|
||||||
|
DROP TABLE IF EXISTS DATA_HOTENKA;
|
||||||
|
CREATE TABLE IF NOT EXISTS DATA_HOTENKA (
|
||||||
|
dict INTEGER,
|
||||||
|
theKey TEXT,
|
||||||
|
theValue TEXT,
|
||||||
|
PRIMARY KEY (dict, theKey)
|
||||||
|
) WITHOUT ROWID;
|
||||||
|
"""
|
||||||
|
|
||||||
|
XCTAssertTrue(
|
||||||
|
sqlite3_exec(ptrSQL, sqlMakeTableHotenka, nil, nil, nil) == SQLITE_OK,
|
||||||
|
"HOTENKA: SQLite Table Creation Failed."
|
||||||
|
)
|
||||||
|
|
||||||
|
assert(sqlite3_exec(ptrSQL, "begin;", nil, nil, nil) == SQLITE_OK)
|
||||||
|
|
||||||
|
testInstance.dict.forEach { dictName, subDict in
|
||||||
|
guard let dictID = DictType.match(rawKeyString: dictName)?.rawValue else { return }
|
||||||
|
subDict.forEach { key, value in
|
||||||
|
var ptrStatement: OpaquePointer?
|
||||||
|
let sqlInsertion = "INSERT INTO DATA_HOTENKA (dict, theKey, theValue) VALUES (\(dictID), '\(key)', '\(value)')"
|
||||||
|
assert(
|
||||||
|
sqlite3_prepare_v2(
|
||||||
|
ptrSQL, sqlInsertion, -1, &ptrStatement, nil
|
||||||
|
) == SQLITE_OK,
|
||||||
|
"HOTENKA: Failed from preparing: \(sqlInsertion)"
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
sqlite3_step(ptrStatement) == SQLITE_DONE,
|
||||||
|
"HOTENKA: Failed from stepping: \(sqlInsertion)"
|
||||||
|
)
|
||||||
|
sqlite3_finalize(ptrStatement)
|
||||||
|
ptrStatement = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(sqlite3_exec(ptrSQL, "commit;", nil, nil, nil) == SQLITE_OK)
|
||||||
|
sqlite3_close_v2(ptrSQL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSampleWithSQLiteDB() throws {
|
||||||
|
NSLog("// Start loading plist from: \(packageRootPath)")
|
||||||
|
let testInstance2: HotenkaChineseConverter = .init(sqliteDir: testDataPath + "convdict.sqlite")
|
||||||
|
NSLog("// Successfully loading sql dictionary.")
|
||||||
|
|
||||||
|
let oriString = "为中华崛起而读书"
|
||||||
|
let result1 = testInstance2.convert(oriString, to: .zhHantTW)
|
||||||
|
let result2 = testInstance2.convert(result1, to: .zhHantKX)
|
||||||
|
let result3 = testInstance2.convert(result2, to: .zhHansJP)
|
||||||
|
NSLog("// Results: \(result1) \(result2) \(result3)")
|
||||||
|
XCTAssertEqual(result1, "為中華崛起而讀書")
|
||||||
|
XCTAssertEqual(result2, "爲中華崛起而讀書")
|
||||||
|
XCTAssertEqual(result3, "為中華崛起而読書")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue