From 773ac2611faaeeb16cedd83406ed3b281ffcf8ea Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 23 Oct 2022 21:39:32 +0200 Subject: [PATCH 1/8] Reactivate #416 --- Sources/SQLite/Core/Blob.swift | 51 ++++++++++++++++----- Sources/SQLite/Core/Connection.swift | 2 +- Sources/SQLite/Core/Statement.swift | 2 +- Sources/SQLite/Foundation.swift | 6 +-- Tests/SQLiteTests/Core/BlobTests.swift | 10 +++- Tests/SQLiteTests/Core/StatementTests.swift | 4 +- Tests/SQLiteTests/FoundationTests.swift | 2 +- 7 files changed, 55 insertions(+), 22 deletions(-) diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift index cd31483b..e1ce5d9c 100644 --- a/Sources/SQLite/Core/Blob.swift +++ b/Sources/SQLite/Core/Blob.swift @@ -21,26 +21,55 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // +import Foundation -public struct Blob { +public final class Blob { - public let bytes: [UInt8] + public let data: NSData - public init(bytes: [UInt8]) { - self.bytes = bytes + public var bytes: UnsafeRawPointer { + data.bytes } - public init(bytes: UnsafeRawPointer, length: Int) { - let i8bufptr = UnsafeBufferPointer(start: bytes.assumingMemoryBound(to: UInt8.self), count: length) - self.init(bytes: [UInt8](i8bufptr)) + public var length: Int { + data.count } - public func toHex() -> String { - bytes.map { - ($0 < 16 ? "0" : "") + String($0, radix: 16, uppercase: false) - }.joined(separator: "") + public convenience init(bytes: [UInt8]) { + let buffer = UnsafeMutablePointer.allocate(capacity: bytes.count) + for idx in 0.. String { + let bytes = bytes.assumingMemoryBound(to: UInt8.self) + + var hex = "" + for idx in 0.. Data { - Data(dataValue.bytes) + dataValue.data as Data } public var datatypeValue: Blob { - withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Blob in - Blob(bytes: pointer.baseAddress!, length: count) - } + Blob(data: self as NSData) } } diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift index 87eb5709..463ef10a 100644 --- a/Tests/SQLiteTests/Core/BlobTests.swift +++ b/Tests/SQLiteTests/Core/BlobTests.swift @@ -11,13 +11,19 @@ class BlobTests: XCTestCase { func test_init_array() { let blob = Blob(bytes: [42, 42, 42]) - XCTAssertEqual(blob.bytes, [42, 42, 42]) + XCTAssertEqual(blob.byteArray, [42, 42, 42]) } func test_init_unsafeRawPointer() { let pointer = UnsafeMutablePointer.allocate(capacity: 3) pointer.initialize(repeating: 42, count: 3) let blob = Blob(bytes: pointer, length: 3) - XCTAssertEqual(blob.bytes, [42, 42, 42]) + XCTAssertEqual(blob.byteArray, [42, 42, 42]) + } +} + +extension Blob { + var byteArray: [UInt8] { + [UInt8](data) } } diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift index eeb513fe..28772ae1 100644 --- a/Tests/SQLiteTests/Core/StatementTests.swift +++ b/Tests/SQLiteTests/Core/StatementTests.swift @@ -22,7 +22,7 @@ class StatementTests: SQLiteTestCase { let statement = try db.prepare("SELECT email FROM users") XCTAssert(try statement.step()) let blob = statement.row[0] as Blob - XCTAssertEqual("alice@example.com", String(bytes: blob.bytes, encoding: .utf8)!) + XCTAssertEqual("alice@example.com", String(data: blob.data as Data, encoding: .utf8)!) } func test_zero_sized_blob_returns_null() throws { @@ -31,7 +31,7 @@ class StatementTests: SQLiteTestCase { try db.run(blobs.create { $0.column(blobColumn) }) try db.run(blobs.insert(blobColumn <- Blob(bytes: []))) let blobValue = try db.scalar(blobs.select(blobColumn).limit(1, offset: 0)) - XCTAssertEqual([], blobValue.bytes) + XCTAssertEqual([], blobValue.byteArray) } func test_prepareRowIterator() throws { diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift index 453febcd..cca15cc1 100644 --- a/Tests/SQLiteTests/FoundationTests.swift +++ b/Tests/SQLiteTests/FoundationTests.swift @@ -5,7 +5,7 @@ class FoundationTests: XCTestCase { func testDataFromBlob() { let data = Data([1, 2, 3]) let blob = data.datatypeValue - XCTAssertEqual([1, 2, 3], blob.bytes) + XCTAssertEqual([1, 2, 3], blob.byteArray) } func testBlobToData() { From 3b91a00c71b34cc3f4343b2c622bf400717a7c3d Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 23 Oct 2022 22:13:21 +0200 Subject: [PATCH 2/8] Fix test --- Sources/SQLite/Core/Statement.swift | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift index ec06a5bb..df4ceebf 100644 --- a/Sources/SQLite/Core/Statement.swift +++ b/Sources/SQLite/Core/Statement.swift @@ -100,21 +100,24 @@ public final class Statement { } fileprivate func bind(_ value: Binding?, atIndex idx: Int) { - if value == nil { + switch value { + case .none: sqlite3_bind_null(handle, Int32(idx)) - } else if let value = value as? Blob { + case let value as Blob where value.length == 0: + sqlite3_bind_zeroblob(handle, Int32(idx), 0) + case let value as Blob: sqlite3_bind_blob(handle, Int32(idx), value.bytes, Int32(value.length), SQLITE_TRANSIENT) - } else if let value = value as? Double { + case let value as Double: sqlite3_bind_double(handle, Int32(idx), value) - } else if let value = value as? Int64 { + case let value as Int64: sqlite3_bind_int64(handle, Int32(idx), value) - } else if let value = value as? String { + case let value as String: sqlite3_bind_text(handle, Int32(idx), value, -1, SQLITE_TRANSIENT) - } else if let value = value as? Int { + case let value as Int: self.bind(value.datatypeValue, atIndex: idx) - } else if let value = value as? Bool { + case let value as Bool: self.bind(value.datatypeValue, atIndex: idx) - } else if let value = value { + case .some(let value): fatalError("tried to bind unexpected value \(value)") } } From 0715e9512daf86fe42e592b54eb633a049adb4b7 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 23 Oct 2022 22:26:46 +0200 Subject: [PATCH 3/8] Fix blob equality --- Sources/SQLite/Core/Blob.swift | 8 +++++++- Tests/SQLiteTests/Core/BlobTests.swift | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift index e1ce5d9c..83b78c2c 100644 --- a/Sources/SQLite/Core/Blob.swift +++ b/Sources/SQLite/Core/Blob.swift @@ -36,6 +36,10 @@ public final class Blob { } public convenience init(bytes: [UInt8]) { + guard bytes.count > 0 else { + self.init(data: NSData()) + return + } let buffer = UnsafeMutablePointer.allocate(capacity: bytes.count) for idx in 0.. String { + guard length > 0 else { return "" } let bytes = bytes.assumingMemoryBound(to: UInt8.self) var hex = "" @@ -85,5 +91,5 @@ extension Blob: Equatable { } public func ==(lhs: Blob, rhs: Blob) -> Bool { - lhs.bytes == rhs.bytes + lhs.data == rhs.data } diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift index 463ef10a..9c8700cc 100644 --- a/Tests/SQLiteTests/Core/BlobTests.swift +++ b/Tests/SQLiteTests/Core/BlobTests.swift @@ -5,10 +5,14 @@ class BlobTests: XCTestCase { func test_toHex() { let blob = Blob(bytes: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 250, 255]) - XCTAssertEqual(blob.toHex(), "000a141e28323c46505a6496faff") } + func test_toHex_empty() { + let blob = Blob(bytes: []) + XCTAssertEqual(blob.toHex(), "") + } + func test_init_array() { let blob = Blob(bytes: [42, 42, 42]) XCTAssertEqual(blob.byteArray, [42, 42, 42]) @@ -20,6 +24,16 @@ class BlobTests: XCTestCase { let blob = Blob(bytes: pointer, length: 3) XCTAssertEqual(blob.byteArray, [42, 42, 42]) } + + func test_equality() { + let blob1 = Blob(bytes: [42, 42, 42]) + let blob2 = Blob(bytes: [42, 42, 42]) + let blob3 = Blob(bytes: [42, 42, 43]) + + XCTAssertEqual(Blob(bytes: []), Blob(bytes: [])) + XCTAssertEqual(blob1, blob2) + XCTAssertNotEqual(blob1, blob3) + } } extension Blob { From 0ce78d92aebea37bc68585ae4d0ead17641d86b1 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 23 Oct 2022 22:29:40 +0200 Subject: [PATCH 4/8] Remove helper --- Tests/SQLiteTests/Core/BlobTests.swift | 10 ++-------- Tests/SQLiteTests/Core/StatementTests.swift | 3 +-- Tests/SQLiteTests/FoundationTests.swift | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift index 9c8700cc..01f38197 100644 --- a/Tests/SQLiteTests/Core/BlobTests.swift +++ b/Tests/SQLiteTests/Core/BlobTests.swift @@ -15,14 +15,14 @@ class BlobTests: XCTestCase { func test_init_array() { let blob = Blob(bytes: [42, 42, 42]) - XCTAssertEqual(blob.byteArray, [42, 42, 42]) + XCTAssertEqual([UInt8](blob.data), [42, 42, 42]) } func test_init_unsafeRawPointer() { let pointer = UnsafeMutablePointer.allocate(capacity: 3) pointer.initialize(repeating: 42, count: 3) let blob = Blob(bytes: pointer, length: 3) - XCTAssertEqual(blob.byteArray, [42, 42, 42]) + XCTAssertEqual([UInt8](blob.data), [42, 42, 42]) } func test_equality() { @@ -35,9 +35,3 @@ class BlobTests: XCTestCase { XCTAssertNotEqual(blob1, blob3) } } - -extension Blob { - var byteArray: [UInt8] { - [UInt8](data) - } -} diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift index 28772ae1..2ab84c09 100644 --- a/Tests/SQLiteTests/Core/StatementTests.swift +++ b/Tests/SQLiteTests/Core/StatementTests.swift @@ -31,7 +31,7 @@ class StatementTests: SQLiteTestCase { try db.run(blobs.create { $0.column(blobColumn) }) try db.run(blobs.insert(blobColumn <- Blob(bytes: []))) let blobValue = try db.scalar(blobs.select(blobColumn).limit(1, offset: 0)) - XCTAssertEqual([], blobValue.byteArray) + XCTAssertEqual([], [UInt8](blobValue.data)) } func test_prepareRowIterator() throws { @@ -71,5 +71,4 @@ class StatementTests: SQLiteTestCase { // truncate succeeds try db.run("DROP TABLE users") } - } diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift index cca15cc1..ffd3fae1 100644 --- a/Tests/SQLiteTests/FoundationTests.swift +++ b/Tests/SQLiteTests/FoundationTests.swift @@ -5,7 +5,7 @@ class FoundationTests: XCTestCase { func testDataFromBlob() { let data = Data([1, 2, 3]) let blob = data.datatypeValue - XCTAssertEqual([1, 2, 3], blob.byteArray) + XCTAssertEqual([1, 2, 3], [UInt8](blob.data)) } func testBlobToData() { From 932d580deae26f04004bcd0aefa536ef021219c8 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 23 Oct 2022 23:08:55 +0200 Subject: [PATCH 5/8] Fix SQLCipher subspec --- Sources/SQLite/Core/Blob.swift | 5 ++--- Sources/SQLite/Extensions/Cipher.swift | 6 +++--- Tests/SQLiteTests/Core/BlobTests.swift | 4 ++++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift index 83b78c2c..fb7ab11b 100644 --- a/Sources/SQLite/Core/Blob.swift +++ b/Sources/SQLite/Core/Blob.swift @@ -27,8 +27,8 @@ public final class Blob { public let data: NSData - public var bytes: UnsafeRawPointer { - data.bytes + public var bytes: UnsafePointer { + data.bytes.assumingMemoryBound(to: UInt8.self) } public var length: Int { @@ -64,7 +64,6 @@ public final class Blob { public func toHex() -> String { guard length > 0 else { return "" } - let bytes = bytes.assumingMemoryBound(to: UInt8.self) var hex = "" for idx in 0.. Date: Sun, 23 Oct 2022 23:47:17 +0200 Subject: [PATCH 6/8] Avoid NSMutableData --- Tests/SQLiteTests/Extensions/CipherTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/SQLiteTests/Extensions/CipherTests.swift b/Tests/SQLiteTests/Extensions/CipherTests.swift index cc43272e..bd7f2042 100644 --- a/Tests/SQLiteTests/Extensions/CipherTests.swift +++ b/Tests/SQLiteTests/Extensions/CipherTests.swift @@ -19,7 +19,7 @@ class CipherTests: XCTestCase { // db2 let key2 = keyData() - try db2.key(Blob(bytes: key2.bytes, length: key2.length)) + try db2.key(Blob(data: key2)) try db2.run("CREATE TABLE foo (bar TEXT)") try db2.run("INSERT INTO foo (bar) VALUES ('world')") @@ -47,7 +47,7 @@ class CipherTests: XCTestCase { func test_data_rekey() throws { let newKey = keyData() - try db2.rekey(Blob(bytes: newKey.bytes, length: newKey.length)) + try db2.rekey(Blob(data: newKey)) XCTAssertEqual(1, try db2.scalar("SELECT count(*) FROM foo") as? Int64) } @@ -108,12 +108,12 @@ class CipherTests: XCTestCase { XCTAssertEqual(1, try conn.scalar("SELECT count(*) FROM foo") as? Int64) } - private func keyData(length: Int = 64) -> NSMutableData { + private func keyData(length: Int = 64) -> NSData { let keyData = NSMutableData(length: length)! let result = SecRandomCopyBytes(kSecRandomDefault, length, keyData.mutableBytes.assumingMemoryBound(to: UInt8.self)) XCTAssertEqual(0, result) - return keyData + return NSData(data: keyData) } } #endif From bf021c503067675295a18f69ecfc6eb3903c8195 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 23 Oct 2022 23:51:43 +0200 Subject: [PATCH 7/8] Update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a12f377..49590fbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ * Fix `insertMany([Encodable])` ([#1130][], [#1138][]) * Fix incorrect spelling of `remove_diacritics` ([#1128][]) * Fix project build order ([#1131][]) -* Performance improvements ([#1109][], [#1115][], [#1132][]) +* Blob performance improvements ([#416][], [#1167][]) +* Various performance improvements ([#1109][], [#1115][], [#1132][]) * Removed FTS3/4 tokenizer integration (`registerTokenizer`, [#1104][], [#1144][]) 0.13.3 (27-03-2022), [diff][diff-0.13.3] @@ -130,6 +131,7 @@ [#30]: https://github.com/stephencelis/SQLite.swift/issues/30 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142 [#315]: https://github.com/stephencelis/SQLite.swift/issues/315 +[#416]: https://github.com/stephencelis/SQLite.swift/pull/416 [#426]: https://github.com/stephencelis/SQLite.swift/pull/426 [#481]: https://github.com/stephencelis/SQLite.swift/pull/481 [#532]: https://github.com/stephencelis/SQLite.swift/issues/532 @@ -189,3 +191,4 @@ [#1144]: https://github.com/stephencelis/SQLite.swift/pull/1144 [#1146]: https://github.com/stephencelis/SQLite.swift/pull/1146 [#1148]: https://github.com/stephencelis/SQLite.swift/pull/1148 +[#1167]: https://github.com/stephencelis/SQLite.swift/pull/1167 From 06e55e794ffb49eda3f97e0ca627fb76ed2f98f6 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Mon, 24 Oct 2022 12:10:54 +0200 Subject: [PATCH 8/8] Simplify --- Sources/SQLite/Core/Blob.swift | 12 +----------- Tests/SQLiteTests/Core/BlobTests.swift | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift index fb7ab11b..c8bd2c15 100644 --- a/Sources/SQLite/Core/Blob.swift +++ b/Sources/SQLite/Core/Blob.swift @@ -40,17 +40,7 @@ public final class Blob { self.init(data: NSData()) return } - let buffer = UnsafeMutablePointer.allocate(capacity: bytes.count) - for idx in 0..