From 764e3f7cecf1187d322e2642bf522e15ff09271e Mon Sep 17 00:00:00 2001 From: dkz2 Date: Sat, 5 Aug 2023 21:32:17 -0500 Subject: [PATCH 01/10] add + replace #25 --- .../Extensions/ByteBuffer+SwiftMemcache.swift | 10 ++- .../Extensions/UInt8+Characters.swift | 2 + .../SwiftMemcache/MemcachedConnection.swift | 75 +++++++++++++++++++ Sources/SwiftMemcache/MemcachedFlags.swift | 4 + .../MemcachedIntegrationTests.swift | 49 ++++++++++++ .../UnitTest/MemcachedFlagsTests.swift | 20 +++++ .../MemcachedRequestEncoderTests.swift | 29 ++++--- 7 files changed, 170 insertions(+), 19 deletions(-) diff --git a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift index 5df7c6d..aed4895 100644 --- a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift +++ b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift @@ -88,15 +88,17 @@ extension ByteBuffer { } if let storageMode = flags.storageMode { + self.writeInteger(UInt8.whitespace) + self.writeInteger(UInt8.M) switch storageMode { + case .add: + self.writeInteger(UInt8.E) case .append: - self.writeInteger(UInt8.whitespace) - self.writeInteger(UInt8.M) self.writeInteger(UInt8.A) case .prepend: - self.writeInteger(UInt8.whitespace) - self.writeInteger(UInt8.M) self.writeInteger(UInt8.P) + case .replace: + self.writeInteger(UInt8.R) } } } diff --git a/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift b/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift index e77f5bd..2fb0c2e 100644 --- a/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift +++ b/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift @@ -25,6 +25,8 @@ extension UInt8 { static var M: UInt8 = .init(ascii: "M") static var P: UInt8 = .init(ascii: "P") static var A: UInt8 = .init(ascii: "A") + static var E: UInt8 = .init(ascii: "E") + static var R: UInt8 = .init(ascii: "R") static var zero: UInt8 = .init(ascii: "0") static var nine: UInt8 = .init(ascii: "9") } diff --git a/Sources/SwiftMemcache/MemcachedConnection.swift b/Sources/SwiftMemcache/MemcachedConnection.swift index 0dd79c4..b7bfd6c 100644 --- a/Sources/SwiftMemcache/MemcachedConnection.swift +++ b/Sources/SwiftMemcache/MemcachedConnection.swift @@ -61,6 +61,8 @@ public actor MemcachedConnection { case unexpectedNilResponse /// Indicates that the key was not found. case keyNotFound + /// Indicates that the key already exist + case keyExist } private var state: State @@ -340,4 +342,77 @@ public actor MemcachedConnection { throw MemcachedConnectionError.connectionShutdown } } + + // MARK: - Adding a Value + + /// Adds a value to a new key in the Memcached server. + /// The operation will fail if the key already exists. + /// + /// - Parameters: + /// - key: The key to add the value to. + /// - value: The `MemcachedValue` to add. + /// - Throws: A `MemcachedConnectionError.connectionShutdown` if the connection to the Memcached server is shut down. + /// - Throws: A `MemcachedConnectionError.keyExist` if the key already exists in the Memcached server. + /// - Throws: A `MemcachedConnectionError.unexpectedNilResponse` if an unexpected response code is returned. + public func add(_ key: String, value: some MemcachedValue) async throws { + switch self.state { + case .initial(_, let bufferAllocator, _, _), + .running(let bufferAllocator, _, _, _): + + var buffer = bufferAllocator.buffer(capacity: 0) + value.writeToBuffer(&buffer) + var flags: MemcachedFlags + + flags = MemcachedFlags() + flags.storageMode = .add + + let command = MemcachedRequest.SetCommand(key: key, value: buffer, flags: flags) + let request = MemcachedRequest.set(command) + + let response = try await sendRequest(request) + + switch response.returnCode { + case .HD: + return + case .NS: + throw MemcachedConnectionError.keyExist + default: + throw MemcachedConnectionError.unexpectedNilResponse + } + + case .finished: + throw MemcachedConnectionError.connectionShutdown + } + } + + // MARK: - Replacing a Value + + /// Replace the value for an existing key in the Memcache server. + /// The operation will fail if the key does not exist. + /// + /// - Parameters: + /// - key: The key to replace the value for. + /// - value: The `MemcachedValue` to replace. + /// - Throws: A `MemcachedConnectionError` if the connection to the Memcached server is shut down. + public func replace(_ key: String, value: some MemcachedValue) async throws { + switch self.state { + case .initial(_, let bufferAllocator, _, _), + .running(let bufferAllocator, _, _, _): + + var buffer = bufferAllocator.buffer(capacity: 0) + value.writeToBuffer(&buffer) + var flags: MemcachedFlags + + flags = MemcachedFlags() + flags.storageMode = .replace + + let command = MemcachedRequest.SetCommand(key: key, value: buffer, flags: flags) + let request = MemcachedRequest.set(command) + + _ = try await self.sendRequest(request) + + case .finished: + throw MemcachedConnectionError.connectionShutdown + } + } } diff --git a/Sources/SwiftMemcache/MemcachedFlags.swift b/Sources/SwiftMemcache/MemcachedFlags.swift index b112bbd..53ec0ec 100644 --- a/Sources/SwiftMemcache/MemcachedFlags.swift +++ b/Sources/SwiftMemcache/MemcachedFlags.swift @@ -50,10 +50,14 @@ public enum TimeToLive: Equatable, Hashable { /// Enum representing the Memcached 'ms' (meta set) command modes (corresponding to the 'M' flag). public enum StorageMode: Equatable, Hashable { + /// The "add" command. If the item exists, LRU is bumped and NS is returned. + case add /// The 'append' command. If the item exists, append the new value to its data. case append /// The 'prepend' command. If the item exists, prepend the new value to its data. case prepend + /// The "replace" command. The new value is set only if the item already exists. + case replace } extension MemcachedFlags: Hashable {} diff --git a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift index 9fd3320..e738393 100644 --- a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift +++ b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift @@ -306,6 +306,55 @@ final class MemcachedIntegrationTest: XCTestCase { } } + func testAddValue() async throws { + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + XCTAssertNoThrow(try! group.syncShutdownGracefully()) + } + let memcachedConnection = MemcachedConnection(host: "memcached", port: 11211, eventLoopGroup: group) + + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { try await memcachedConnection.run() } + + // Add a value to a key + let addValue = "foo" + try await memcachedConnection.delete("adds") + try await memcachedConnection.add("adds", value: addValue) + + // Get value for the key after add operation + let addedValue: String? = try await memcachedConnection.get("adds") + XCTAssertEqual(addedValue, addValue, "Received value should be the same as the added value") + + group.cancelAll() + } + } + + func testReplaceValue() async throws { + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + XCTAssertNoThrow(try! group.syncShutdownGracefully()) + } + let memcachedConnection = MemcachedConnection(host: "memcached", port: 11211, eventLoopGroup: group) + + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { try await memcachedConnection.run() } + + // Set key and initial value + let initialValue = "foo" + try await memcachedConnection.set("greet", value: initialValue) + + // Replace value for the key + let replaceValue = "hi" + try await memcachedConnection.replace("greet", value: replaceValue) + + // Get value for the key after replace operation + let replacedValue: String? = try await memcachedConnection.get("greet") + XCTAssertEqual(replacedValue, replaceValue, "Received value should be the same as the replaceValue") + + group.cancelAll() + } + } + func testMemcachedConnectionWithUInt() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { diff --git a/Tests/SwiftMemcacheTests/UnitTest/MemcachedFlagsTests.swift b/Tests/SwiftMemcacheTests/UnitTest/MemcachedFlagsTests.swift index 5d51533..0d68881 100644 --- a/Tests/SwiftMemcacheTests/UnitTest/MemcachedFlagsTests.swift +++ b/Tests/SwiftMemcacheTests/UnitTest/MemcachedFlagsTests.swift @@ -38,6 +38,16 @@ final class MemcachedFlagsTests: XCTestCase { } } + func testStorageModeAdd() { + var flags = MemcachedFlags() + flags.storageMode = .add + if case .add? = flags.storageMode { + XCTAssertTrue(true) + } else { + XCTFail("Flag storageMode is not .add") + } + } + func testStorageModeAppend() { var flags = MemcachedFlags() flags.storageMode = .append @@ -57,4 +67,14 @@ final class MemcachedFlagsTests: XCTestCase { XCTFail("Flag storageMode is not .prepend") } } + + func testStorageModeReplace() { + var flags = MemcachedFlags() + flags.storageMode = .replace + if case .replace? = flags.storageMode { + XCTAssertTrue(true) + } else { + XCTFail("Flag storageMode is not .replace") + } + } } diff --git a/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift b/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift index 2734d76..cdcf069 100644 --- a/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift +++ b/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift @@ -48,38 +48,37 @@ final class MemcachedRequestEncoderTests: XCTestCase { XCTAssertEqual(outBuffer.getString(at: 0, length: outBuffer.readableBytes), expectedEncodedData) } - func testEncodeAppendRequest() { + func testEncodeStorageRequest(withMode mode: StorageMode, expectedEncodedData: String) { // Prepare a MemcachedRequest var buffer = ByteBufferAllocator().buffer(capacity: 2) buffer.writeString("hi") var flags = MemcachedFlags() - flags.storageMode = .append + flags.storageMode = mode let command = MemcachedRequest.SetCommand(key: "foo", value: buffer, flags: flags) let request = MemcachedRequest.set(command) // pass our request through the encoder let outBuffer = self.encodeRequest(request) - let expectedEncodedData = "ms foo 2 MA\r\nhi\r\n" + // assert the encoded request XCTAssertEqual(outBuffer.getString(at: 0, length: outBuffer.readableBytes), expectedEncodedData) } - func testEncodePrependRequest() { - // Prepare a MemcachedRequest - var buffer = ByteBufferAllocator().buffer(capacity: 2) - buffer.writeString("hi") + func testEncodeAppendRequest() { + self.testEncodeStorageRequest(withMode: .append, expectedEncodedData: "ms foo 2 MA\r\nhi\r\n") + } - var flags = MemcachedFlags() - flags.storageMode = .prepend - let command = MemcachedRequest.SetCommand(key: "foo", value: buffer, flags: flags) - let request = MemcachedRequest.set(command) + func testEncodePrependRequest() { + self.testEncodeStorageRequest(withMode: .prepend, expectedEncodedData: "ms foo 2 MP\r\nhi\r\n") + } - // pass our request through the encoder - let outBuffer = self.encodeRequest(request) + func testEncodeAddRequest() { + self.testEncodeStorageRequest(withMode: .add, expectedEncodedData: "ms foo 2 ME\r\nhi\r\n") + } - let expectedEncodedData = "ms foo 2 MP\r\nhi\r\n" - XCTAssertEqual(outBuffer.getString(at: 0, length: outBuffer.readableBytes), expectedEncodedData) + func testEncodeReplaceRequest() { + self.testEncodeStorageRequest(withMode: .replace, expectedEncodedData: "ms foo 2 MR\r\nhi\r\n") } func testEncodeTouchRequest() { From 882b5a0773d7915c31e096f7ec94e02405555dd5 Mon Sep 17 00:00:00 2001 From: dkz2 Date: Sat, 5 Aug 2023 22:22:06 -0500 Subject: [PATCH 02/10] soundness --- .../IntegrationTest/MemcachedIntegrationTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift index e738393..166e51b 100644 --- a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift +++ b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift @@ -318,6 +318,7 @@ final class MemcachedIntegrationTest: XCTestCase { // Add a value to a key let addValue = "foo" + try await memcachedConnection.delete("adds") try await memcachedConnection.add("adds", value: addValue) From f504be3a90736249c8cde0763f0a52d910165073 Mon Sep 17 00:00:00 2001 From: dkz2 Date: Sun, 6 Aug 2023 13:32:13 -0500 Subject: [PATCH 03/10] more test for add + replace --- .../SwiftMemcache/MemcachedConnection.swift | 13 +++- Sources/SwiftMemcache/MemcachedFlags.swift | 2 +- .../MemcachedIntegrationTests.swift | 61 +++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftMemcache/MemcachedConnection.swift b/Sources/SwiftMemcache/MemcachedConnection.swift index b7bfd6c..4a380e6 100644 --- a/Sources/SwiftMemcache/MemcachedConnection.swift +++ b/Sources/SwiftMemcache/MemcachedConnection.swift @@ -345,7 +345,7 @@ public actor MemcachedConnection { // MARK: - Adding a Value - /// Adds a value to a new key in the Memcached server. + /// Adds a new key-value pair in the Memcached server. /// The operation will fail if the key already exists. /// /// - Parameters: @@ -409,7 +409,16 @@ public actor MemcachedConnection { let command = MemcachedRequest.SetCommand(key: key, value: buffer, flags: flags) let request = MemcachedRequest.set(command) - _ = try await self.sendRequest(request) + let response = try await sendRequest(request) + + switch response.returnCode { + case .HD: + return + case .NS: + throw MemcachedConnectionError.keyNotFound + default: + throw MemcachedConnectionError.unexpectedNilResponse + } case .finished: throw MemcachedConnectionError.connectionShutdown diff --git a/Sources/SwiftMemcache/MemcachedFlags.swift b/Sources/SwiftMemcache/MemcachedFlags.swift index 53ec0ec..69c464d 100644 --- a/Sources/SwiftMemcache/MemcachedFlags.swift +++ b/Sources/SwiftMemcache/MemcachedFlags.swift @@ -49,7 +49,7 @@ public enum TimeToLive: Equatable, Hashable { } /// Enum representing the Memcached 'ms' (meta set) command modes (corresponding to the 'M' flag). -public enum StorageMode: Equatable, Hashable { +enum StorageMode: Equatable, Hashable { /// The "add" command. If the item exists, LRU is bumped and NS is returned. case add /// The 'append' command. If the item exists, append the new value to its data. diff --git a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift index 166e51b..49945d6 100644 --- a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift +++ b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift @@ -330,6 +330,39 @@ final class MemcachedIntegrationTest: XCTestCase { } } + func testAddValueKeyExists() async throws { + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + XCTAssertNoThrow(try! group.syncShutdownGracefully()) + } + let memcachedConnection = MemcachedConnection(host: "memcached", port: 11211, eventLoopGroup: group) + + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { try await memcachedConnection.run() } + + // Add a value to a key + let initialValue = "foo" + let newValue = "bar" + + // Ensure the key is clean and add an initial value + try await memcachedConnection.delete("adds") + try await memcachedConnection.add("adds", value: initialValue) + + do { + // Attempt to add a new value to the existing key + try await memcachedConnection.add("adds", value: newValue) + XCTFail("Expected an error indicating the key exists, but no error was thrown.") + } catch { + // Check if the error description or localized description matches the expected error + if "\(error)" != "keyExist" { + XCTFail("Unexpected error: \(error)") + } + } + + group.cancelAll() + } + } + func testReplaceValue() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { @@ -356,6 +389,34 @@ final class MemcachedIntegrationTest: XCTestCase { } } + func testReplaceNonExistentKey() async throws { + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + XCTAssertNoThrow(try! group.syncShutdownGracefully()) + } + let memcachedConnection = MemcachedConnection(host: "memcached", port: 11211, eventLoopGroup: group) + + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { try await memcachedConnection.run() } + + do { + // Ensure the key is clean + try await memcachedConnection.delete("nonExistentKey") + // Attempt to replace value for a non-existent key + let replaceValue = "testValue" + try await memcachedConnection.replace("nonExistentKey", value: replaceValue) + XCTFail("Expected an error indicating the key was not found, but no error was thrown.") + } catch { + // Check if the error description or localized description matches the expected error + if "\(error)" != "keyNotFound" { + XCTFail("Unexpected error: \(error)") + } + } + + group.cancelAll() + } + } + func testMemcachedConnectionWithUInt() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { From b12415300b9087484017204cefe1ad5be3d7002c Mon Sep 17 00:00:00 2001 From: dkz2 Date: Sun, 6 Aug 2023 13:41:19 -0500 Subject: [PATCH 04/10] build faiure fix? --- .../MemcachedIntegrationTests.swift | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift index 49945d6..628e28b 100644 --- a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift +++ b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift @@ -319,7 +319,16 @@ final class MemcachedIntegrationTest: XCTestCase { // Add a value to a key let addValue = "foo" - try await memcachedConnection.delete("adds") + // Attempt to delete the key, but ignore the error if it doesn't exist + do { + try await memcachedConnection.delete("adds") + } catch { + if "\(error)" != "keyNotFound" { + throw error + } + } + + // Proceed with adding the key-value pair try await memcachedConnection.add("adds", value: addValue) // Get value for the key after add operation @@ -344,8 +353,16 @@ final class MemcachedIntegrationTest: XCTestCase { let initialValue = "foo" let newValue = "bar" - // Ensure the key is clean and add an initial value - try await memcachedConnection.delete("adds") + // Attempt to delete the key, but ignore the error if it doesn't exist + do { + try await memcachedConnection.delete("adds") + } catch { + if "\(error)" != "keyNotFound" { + throw error + } + } + + // Set an initial value for the key try await memcachedConnection.add("adds", value: initialValue) do { From 0bb4f8b10a1cb21e7737f4c9013ff4558190f717 Mon Sep 17 00:00:00 2001 From: dkz2 Date: Tue, 8 Aug 2023 12:02:21 -0500 Subject: [PATCH 05/10] closes #26 --- .../Extensions/ByteBuffer+SwiftMemcache.swift | 16 ++++++ .../Extensions/UInt8+Characters.swift | 4 ++ .../SwiftMemcache/MemcachedConnection.swift | 54 ++++++++++++++++++ Sources/SwiftMemcache/MemcachedFlags.swift | 18 ++++++ Sources/SwiftMemcache/MemcachedRequest.swift | 6 ++ .../MemcachedRequestEncoder.swift | 16 ++++++ Sources/SwiftMemcache/MemcachedValue.swift | 5 +- .../MemcachedIntegrationTests.swift | 56 +++++++++++++++++++ .../MemcachedRequestEncoderTests.swift | 30 ++++++++++ 9 files changed, 203 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift index aed4895..48cbbf3 100644 --- a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift +++ b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift @@ -101,6 +101,22 @@ extension ByteBuffer { self.writeInteger(UInt8.R) } } + + if let arithmeticMode = flags.arithmeticMode { + self.writeInteger(UInt8.whitespace) + self.writeInteger(UInt8.M) + switch arithmeticMode { + case .decrement: + self.writeInteger(UInt8.decrement) + case .increment: + self.writeInteger(UInt8.increment) + } + if let arithmeticDelta = flags.arithmeticDelta { + self.writeInteger(UInt8.whitespace) + self.writeInteger(UInt8.D) + self.writeIntegerAsASCII(arithmeticDelta) + } + } } } diff --git a/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift b/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift index 2fb0c2e..06c671d 100644 --- a/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift +++ b/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift @@ -20,6 +20,7 @@ extension UInt8 { static var s: UInt8 = .init(ascii: "s") static var g: UInt8 = .init(ascii: "g") static var d: UInt8 = .init(ascii: "d") + static var a: UInt8 = .init(ascii: "a") static var v: UInt8 = .init(ascii: "v") static var T: UInt8 = .init(ascii: "T") static var M: UInt8 = .init(ascii: "M") @@ -27,6 +28,9 @@ extension UInt8 { static var A: UInt8 = .init(ascii: "A") static var E: UInt8 = .init(ascii: "E") static var R: UInt8 = .init(ascii: "R") + static var D: UInt8 = .init(ascii: "D") static var zero: UInt8 = .init(ascii: "0") static var nine: UInt8 = .init(ascii: "9") + static var increment: UInt8 = .init(ascii: "+") + static var decrement: UInt8 = .init(ascii: "-") } diff --git a/Sources/SwiftMemcache/MemcachedConnection.swift b/Sources/SwiftMemcache/MemcachedConnection.swift index 4a380e6..1af984b 100644 --- a/Sources/SwiftMemcache/MemcachedConnection.swift +++ b/Sources/SwiftMemcache/MemcachedConnection.swift @@ -424,4 +424,58 @@ public actor MemcachedConnection { throw MemcachedConnectionError.connectionShutdown } } + + // MARK: - Increment a Value + + /// Increment the value for an existing key in the Memcache server by a specified amount. + /// + /// - Parameters: + /// - key: The key for the value to increment. + /// - amount: The `UInt64` amount to increment the value by. + /// - Throws: A `MemcachedConnectionError` if the connection to the Memcached server is shut down. + public func increment(_ key: String, amount: UInt64) async throws { + switch self.state { + case .initial(_, _, _, _), + .running: + + var flags = MemcachedFlags() + flags.arithmeticMode = .increment + flags.arithmeticDelta = amount + + let command = MemcachedRequest.ArithmeticCommand(key: key, flags: flags) + let request = MemcachedRequest.arithmetic(command) + + _ = try await self.sendRequest(request) + + case .finished: + throw MemcachedConnectionError.connectionShutdown + } + } + + // MARK: - Decrement a Value + + /// Decrement the value for an existing key in the Memcache server by a specified amount. + /// + /// - Parameters: + /// - key: The key for the value to decrement. + /// - amount: The `UInt64` amount to decrement the value by. + /// - Throws: A `MemcachedConnectionError` if the connection to the Memcached server is shut down. + public func decrement(_ key: String, amount: UInt64) async throws { + switch self.state { + case .initial(_, _, _, _), + .running: + + var flags = MemcachedFlags() + flags.arithmeticMode = .decrement + flags.arithmeticDelta = amount + + let command = MemcachedRequest.ArithmeticCommand(key: key, flags: flags) + let request = MemcachedRequest.arithmetic(command) + + _ = try await self.sendRequest(request) + + case .finished: + throw MemcachedConnectionError.connectionShutdown + } + } } diff --git a/Sources/SwiftMemcache/MemcachedFlags.swift b/Sources/SwiftMemcache/MemcachedFlags.swift index 69c464d..192238c 100644 --- a/Sources/SwiftMemcache/MemcachedFlags.swift +++ b/Sources/SwiftMemcache/MemcachedFlags.swift @@ -37,6 +37,16 @@ struct MemcachedFlags { /// The default mode is 'set'. var storageMode: StorageMode? + /// Flag 'M' for the 'ma' (meta arithmetic) command. + /// + /// Represents the mode of the 'ma' command, which determines the behavior of the arithmetic operation. + var arithmeticMode: ArithmeticMode? + + /// Flag 'D' for the 'ma' (meta arithmetic) command. + /// + /// Represents the delta to apply to the 'ma' command. The default value is 1. + var arithmeticDelta: UInt64? + init() {} } @@ -60,4 +70,12 @@ enum StorageMode: Equatable, Hashable { case replace } +/// Enum representing the mode for the 'ma' (meta arithmetic) command in Memcached (corresponding to the 'M' flag). +enum ArithmeticMode: Equatable, Hashable { + /// 'increment' command. If applied, it increases the numerical value of the item. + case increment + /// 'decrement' command. If applied, it decreases the numerical value of the item. + case decrement +} + extension MemcachedFlags: Hashable {} diff --git a/Sources/SwiftMemcache/MemcachedRequest.swift b/Sources/SwiftMemcache/MemcachedRequest.swift index 232d0b7..5b9b03e 100644 --- a/Sources/SwiftMemcache/MemcachedRequest.swift +++ b/Sources/SwiftMemcache/MemcachedRequest.swift @@ -29,7 +29,13 @@ enum MemcachedRequest { let key: String } + struct ArithmeticCommand { + let key: String + var flags: MemcachedFlags + } + case set(SetCommand) case get(GetCommand) case delete(DeleteCommand) + case arithmetic(ArithmeticCommand) } diff --git a/Sources/SwiftMemcache/MemcachedRequestEncoder.swift b/Sources/SwiftMemcache/MemcachedRequestEncoder.swift index 1389bf0..274cdb3 100644 --- a/Sources/SwiftMemcache/MemcachedRequestEncoder.swift +++ b/Sources/SwiftMemcache/MemcachedRequestEncoder.swift @@ -73,6 +73,22 @@ struct MemcachedRequestEncoder: MessageToByteEncoder { out.writeInteger(UInt8.whitespace) out.writeBytes(command.key.utf8) + // write separator + out.writeInteger(UInt8.carriageReturn) + out.writeInteger(UInt8.newline) + + case .arithmetic(let command): + precondition(!command.key.isEmpty, "Key must not be empty") + + // write command and key + out.writeInteger(UInt8.m) + out.writeInteger(UInt8.a) + out.writeInteger(UInt8.whitespace) + out.writeBytes(command.key.utf8) + + // write flags if there are any + out.writeMemcachedFlags(flags: command.flags) + // write separator out.writeInteger(UInt8.carriageReturn) out.writeInteger(UInt8.newline) diff --git a/Sources/SwiftMemcache/MemcachedValue.swift b/Sources/SwiftMemcache/MemcachedValue.swift index f68b365..465cea7 100644 --- a/Sources/SwiftMemcache/MemcachedValue.swift +++ b/Sources/SwiftMemcache/MemcachedValue.swift @@ -33,14 +33,15 @@ extension MemcachedValue where Self: FixedWidthInteger { /// /// - Parameter buffer: The ByteBuffer to which the integer should be written. public func writeToBuffer(_ buffer: inout ByteBuffer) { - buffer.writeInteger(self) + buffer.writeString(String(self)) } /// Reads a FixedWidthInteger from a ByteBuffer. /// /// - Parameter buffer: The ByteBuffer from which the value should be read. public static func readFromBuffer(_ buffer: inout ByteBuffer) -> Self? { - return buffer.readInteger() + guard let string = buffer.readString(length: buffer.readableBytes)?.trimmingCharacters(in: .whitespacesAndNewlines) else { return nil } + return Self(string) } } diff --git a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift index 628e28b..1fc037e 100644 --- a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift +++ b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift @@ -434,6 +434,62 @@ final class MemcachedIntegrationTest: XCTestCase { } } + func testIncrementValue() async throws { + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + XCTAssertNoThrow(try! group.syncShutdownGracefully()) + } + let memcachedConnection = MemcachedConnection(host: "memcached", port: 11211, eventLoopGroup: group) + + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { try await memcachedConnection.run() } + + // Set key and initial value + let initialValue: UInt64 = 1 + try await memcachedConnection.set("increment", value: initialValue) + + // Increment value + let incrementAmount: UInt64 = 100 + try await memcachedConnection.increment("increment", amount: incrementAmount) + + // Get new value + let newValue: UInt64? = try await memcachedConnection.get("increment") + + // Check if new value is equal to initial value plus increment amount + XCTAssertEqual(newValue, initialValue + incrementAmount, "Incremented value is incorrect") + + group.cancelAll() + } + } + + func testDecrementValue() async throws { + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + defer { + XCTAssertNoThrow(try! group.syncShutdownGracefully()) + } + let memcachedConnection = MemcachedConnection(host: "memcached", port: 11211, eventLoopGroup: group) + + try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { try await memcachedConnection.run() } + + // Set key and initial value + let initialValue: UInt64 = 100 + try await memcachedConnection.set("decrement", value: initialValue) + + // Increment value + let decrementAmount: UInt64 = 10 + try await memcachedConnection.decrement("decrement", amount: decrementAmount) + + // Get new value + let newValue: UInt64? = try await memcachedConnection.get("decrement") + + // Check if new value is equal to initial value plus increment amount + XCTAssertEqual(newValue, initialValue - decrementAmount, "Incremented value is incorrect") + + group.cancelAll() + } + } + func testMemcachedConnectionWithUInt() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { diff --git a/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift b/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift index cdcf069..1f1328d 100644 --- a/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift +++ b/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift @@ -169,4 +169,34 @@ final class MemcachedRequestEncoderTests: XCTestCase { let expectedEncodedData = "md foo\r\n" XCTAssertEqual(outBuffer.getString(at: 0, length: outBuffer.readableBytes), expectedEncodedData) } + + func testEncodeIncrementRequest() { + // Prepare a MemcachedRequest + var flags = MemcachedFlags() + flags.arithmeticMode = .increment + flags.arithmeticDelta = 100 + let command = MemcachedRequest.ArithmeticCommand(key: "foo", flags: flags) + let request = MemcachedRequest.arithmetic(command) + + // pass our request through the encoder + let outBuffer = self.encodeRequest(request) + + let expectedEncodedData = "ma foo M+ D100\r\n" + XCTAssertEqual(outBuffer.getString(at: 0, length: outBuffer.readableBytes), expectedEncodedData) + } + + func testEncodeDecrementRequest() { + // Prepare a MemcachedRequest + var flags = MemcachedFlags() + flags.arithmeticMode = .decrement + flags.arithmeticDelta = 100 + let command = MemcachedRequest.ArithmeticCommand(key: "foo", flags: flags) + let request = MemcachedRequest.arithmetic(command) + + // pass our request through the encoder + let outBuffer = self.encodeRequest(request) + + let expectedEncodedData = "ma foo M- D100\r\n" + XCTAssertEqual(outBuffer.getString(at: 0, length: outBuffer.readableBytes), expectedEncodedData) + } } From 1e79a89d92ae377edf37dde4e743581b1f816624 Mon Sep 17 00:00:00 2001 From: dkz2 Date: Tue, 8 Aug 2023 12:08:41 -0500 Subject: [PATCH 06/10] mr conflict clean up --- .../SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift | 3 --- Sources/SwiftMemcache/Extensions/UInt8+Characters.swift | 3 --- Sources/SwiftMemcache/MemcachedConnection.swift | 3 --- Sources/SwiftMemcache/MemcachedFlags.swift | 3 --- .../IntegrationTest/MemcachedIntegrationTests.swift | 3 --- 5 files changed, 15 deletions(-) diff --git a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift index 00983c5..48cbbf3 100644 --- a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift +++ b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift @@ -99,7 +99,6 @@ extension ByteBuffer { self.writeInteger(UInt8.P) case .replace: self.writeInteger(UInt8.R) -<<<<<<< HEAD } } @@ -116,8 +115,6 @@ extension ByteBuffer { self.writeInteger(UInt8.whitespace) self.writeInteger(UInt8.D) self.writeIntegerAsASCII(arithmeticDelta) -======= ->>>>>>> 9ae24ae6c441eaa6db85fab9671433c65de999b4 } } } diff --git a/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift b/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift index 24280cd..06c671d 100644 --- a/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift +++ b/Sources/SwiftMemcache/Extensions/UInt8+Characters.swift @@ -28,10 +28,7 @@ extension UInt8 { static var A: UInt8 = .init(ascii: "A") static var E: UInt8 = .init(ascii: "E") static var R: UInt8 = .init(ascii: "R") -<<<<<<< HEAD static var D: UInt8 = .init(ascii: "D") -======= ->>>>>>> 9ae24ae6c441eaa6db85fab9671433c65de999b4 static var zero: UInt8 = .init(ascii: "0") static var nine: UInt8 = .init(ascii: "9") static var increment: UInt8 = .init(ascii: "+") diff --git a/Sources/SwiftMemcache/MemcachedConnection.swift b/Sources/SwiftMemcache/MemcachedConnection.swift index 54989e1..1af984b 100644 --- a/Sources/SwiftMemcache/MemcachedConnection.swift +++ b/Sources/SwiftMemcache/MemcachedConnection.swift @@ -424,7 +424,6 @@ public actor MemcachedConnection { throw MemcachedConnectionError.connectionShutdown } } -<<<<<<< HEAD // MARK: - Increment a Value @@ -479,6 +478,4 @@ public actor MemcachedConnection { throw MemcachedConnectionError.connectionShutdown } } -======= ->>>>>>> 9ae24ae6c441eaa6db85fab9671433c65de999b4 } diff --git a/Sources/SwiftMemcache/MemcachedFlags.swift b/Sources/SwiftMemcache/MemcachedFlags.swift index a8af6e6..192238c 100644 --- a/Sources/SwiftMemcache/MemcachedFlags.swift +++ b/Sources/SwiftMemcache/MemcachedFlags.swift @@ -68,7 +68,6 @@ enum StorageMode: Equatable, Hashable { case prepend /// The "replace" command. The new value is set only if the item already exists. case replace -<<<<<<< HEAD } /// Enum representing the mode for the 'ma' (meta arithmetic) command in Memcached (corresponding to the 'M' flag). @@ -77,8 +76,6 @@ enum ArithmeticMode: Equatable, Hashable { case increment /// 'decrement' command. If applied, it decreases the numerical value of the item. case decrement -======= ->>>>>>> 9ae24ae6c441eaa6db85fab9671433c65de999b4 } extension MemcachedFlags: Hashable {} diff --git a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift index 516b543..1fc037e 100644 --- a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift +++ b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift @@ -434,7 +434,6 @@ final class MemcachedIntegrationTest: XCTestCase { } } -<<<<<<< HEAD func testIncrementValue() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { @@ -491,8 +490,6 @@ final class MemcachedIntegrationTest: XCTestCase { } } -======= ->>>>>>> 9ae24ae6c441eaa6db85fab9671433c65de999b4 func testMemcachedConnectionWithUInt() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { From c486acc9f1b654e44ec21c461121904ae28961c5 Mon Sep 17 00:00:00 2001 From: dkz2 Date: Tue, 8 Aug 2023 23:14:54 -0500 Subject: [PATCH 07/10] detla is now associated value --- .../Extensions/ByteBuffer+SwiftMemcache.swift | 11 +++++----- .../SwiftMemcache/MemcachedConnection.swift | 20 +++++++++++-------- Sources/SwiftMemcache/MemcachedFlags.swift | 9 ++------- .../MemcachedIntegrationTests.swift | 12 +++++------ .../MemcachedRequestEncoderTests.swift | 6 ++---- 5 files changed, 28 insertions(+), 30 deletions(-) diff --git a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift index 48cbbf3..ceedb40 100644 --- a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift +++ b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift @@ -106,15 +106,16 @@ extension ByteBuffer { self.writeInteger(UInt8.whitespace) self.writeInteger(UInt8.M) switch arithmeticMode { - case .decrement: + case .decrement(let delta): self.writeInteger(UInt8.decrement) - case .increment: + self.writeInteger(UInt8.whitespace) + self.writeInteger(UInt8.D) + self.writeIntegerAsASCII(delta) + case .increment(let delta): self.writeInteger(UInt8.increment) - } - if let arithmeticDelta = flags.arithmeticDelta { self.writeInteger(UInt8.whitespace) self.writeInteger(UInt8.D) - self.writeIntegerAsASCII(arithmeticDelta) + self.writeIntegerAsASCII(delta) } } } diff --git a/Sources/SwiftMemcache/MemcachedConnection.swift b/Sources/SwiftMemcache/MemcachedConnection.swift index 1af984b..3af1e4d 100644 --- a/Sources/SwiftMemcache/MemcachedConnection.swift +++ b/Sources/SwiftMemcache/MemcachedConnection.swift @@ -431,16 +431,18 @@ public actor MemcachedConnection { /// /// - Parameters: /// - key: The key for the value to increment. - /// - amount: The `UInt64` amount to increment the value by. + /// - amount: The `Int` amount to increment the value by. Must be larger than 0. /// - Throws: A `MemcachedConnectionError` if the connection to the Memcached server is shut down. - public func increment(_ key: String, amount: UInt64) async throws { + public func increment(_ key: String, amount: Int) async throws { + // Ensure the amount is greater than 0 + precondition(amount > 0, "Amount to increment should be larger than 0") + switch self.state { case .initial(_, _, _, _), .running: var flags = MemcachedFlags() - flags.arithmeticMode = .increment - flags.arithmeticDelta = amount + flags.arithmeticMode = .increment(amount) let command = MemcachedRequest.ArithmeticCommand(key: key, flags: flags) let request = MemcachedRequest.arithmetic(command) @@ -458,16 +460,18 @@ public actor MemcachedConnection { /// /// - Parameters: /// - key: The key for the value to decrement. - /// - amount: The `UInt64` amount to decrement the value by. + /// - amount: The `Int` amount to decrement the value by. Must be larger than 0. /// - Throws: A `MemcachedConnectionError` if the connection to the Memcached server is shut down. - public func decrement(_ key: String, amount: UInt64) async throws { + public func decrement(_ key: String, amount: Int) async throws { + // Ensure the amount is greater than 0 + precondition(amount > 0, "Amount to decrement should be larger than 0") + switch self.state { case .initial(_, _, _, _), .running: var flags = MemcachedFlags() - flags.arithmeticMode = .decrement - flags.arithmeticDelta = amount + flags.arithmeticMode = .decrement(amount) let command = MemcachedRequest.ArithmeticCommand(key: key, flags: flags) let request = MemcachedRequest.arithmetic(command) diff --git a/Sources/SwiftMemcache/MemcachedFlags.swift b/Sources/SwiftMemcache/MemcachedFlags.swift index 192238c..e098b6f 100644 --- a/Sources/SwiftMemcache/MemcachedFlags.swift +++ b/Sources/SwiftMemcache/MemcachedFlags.swift @@ -42,11 +42,6 @@ struct MemcachedFlags { /// Represents the mode of the 'ma' command, which determines the behavior of the arithmetic operation. var arithmeticMode: ArithmeticMode? - /// Flag 'D' for the 'ma' (meta arithmetic) command. - /// - /// Represents the delta to apply to the 'ma' command. The default value is 1. - var arithmeticDelta: UInt64? - init() {} } @@ -73,9 +68,9 @@ enum StorageMode: Equatable, Hashable { /// Enum representing the mode for the 'ma' (meta arithmetic) command in Memcached (corresponding to the 'M' flag). enum ArithmeticMode: Equatable, Hashable { /// 'increment' command. If applied, it increases the numerical value of the item. - case increment + case increment(Int) /// 'decrement' command. If applied, it decreases the numerical value of the item. - case decrement + case decrement(Int) } extension MemcachedFlags: Hashable {} diff --git a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift index 1fc037e..685aca6 100644 --- a/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift +++ b/Tests/SwiftMemcacheTests/IntegrationTest/MemcachedIntegrationTests.swift @@ -445,15 +445,15 @@ final class MemcachedIntegrationTest: XCTestCase { group.addTask { try await memcachedConnection.run() } // Set key and initial value - let initialValue: UInt64 = 1 + let initialValue = 1 try await memcachedConnection.set("increment", value: initialValue) // Increment value - let incrementAmount: UInt64 = 100 + let incrementAmount = 100 try await memcachedConnection.increment("increment", amount: incrementAmount) // Get new value - let newValue: UInt64? = try await memcachedConnection.get("increment") + let newValue: Int? = try await memcachedConnection.get("increment") // Check if new value is equal to initial value plus increment amount XCTAssertEqual(newValue, initialValue + incrementAmount, "Incremented value is incorrect") @@ -473,15 +473,15 @@ final class MemcachedIntegrationTest: XCTestCase { group.addTask { try await memcachedConnection.run() } // Set key and initial value - let initialValue: UInt64 = 100 + let initialValue = 100 try await memcachedConnection.set("decrement", value: initialValue) // Increment value - let decrementAmount: UInt64 = 10 + let decrementAmount = 10 try await memcachedConnection.decrement("decrement", amount: decrementAmount) // Get new value - let newValue: UInt64? = try await memcachedConnection.get("decrement") + let newValue: Int? = try await memcachedConnection.get("decrement") // Check if new value is equal to initial value plus increment amount XCTAssertEqual(newValue, initialValue - decrementAmount, "Incremented value is incorrect") diff --git a/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift b/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift index 1f1328d..92de139 100644 --- a/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift +++ b/Tests/SwiftMemcacheTests/UnitTest/MemcachedRequestEncoderTests.swift @@ -173,8 +173,7 @@ final class MemcachedRequestEncoderTests: XCTestCase { func testEncodeIncrementRequest() { // Prepare a MemcachedRequest var flags = MemcachedFlags() - flags.arithmeticMode = .increment - flags.arithmeticDelta = 100 + flags.arithmeticMode = .increment(100) let command = MemcachedRequest.ArithmeticCommand(key: "foo", flags: flags) let request = MemcachedRequest.arithmetic(command) @@ -188,8 +187,7 @@ final class MemcachedRequestEncoderTests: XCTestCase { func testEncodeDecrementRequest() { // Prepare a MemcachedRequest var flags = MemcachedFlags() - flags.arithmeticMode = .decrement - flags.arithmeticDelta = 100 + flags.arithmeticMode = .decrement(100) let command = MemcachedRequest.ArithmeticCommand(key: "foo", flags: flags) let request = MemcachedRequest.arithmetic(command) From f863db27458c66638772ddf4e26ee2445becc118 Mon Sep 17 00:00:00 2001 From: dkz2 Date: Tue, 8 Aug 2023 23:17:27 -0500 Subject: [PATCH 08/10] precondition that both storage and arth mode are not set --- .../SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift index ceedb40..dc4044f 100644 --- a/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift +++ b/Sources/SwiftMemcache/Extensions/ByteBuffer+SwiftMemcache.swift @@ -53,6 +53,9 @@ extension ByteBuffer { /// - parameters: /// - flags: An instance of MemcachedFlags that holds the flags intended to be serialized and written to the ByteBuffer. mutating func writeMemcachedFlags(flags: MemcachedFlags) { + // Ensure that both storageMode and arithmeticMode aren't set at the same time. + precondition(!(flags.storageMode != nil && flags.arithmeticMode != nil), "Cannot specify both a storage and arithmetic mode.") + if let shouldReturnValue = flags.shouldReturnValue, shouldReturnValue { self.writeInteger(UInt8.whitespace) self.writeInteger(UInt8.v) From 153b018bc88e30c2e0fbf209771ee44f53759528 Mon Sep 17 00:00:00 2001 From: dkz2 Date: Thu, 10 Aug 2023 12:56:43 -0500 Subject: [PATCH 09/10] make sure of writeIntAsASCII --- Sources/SwiftMemcache/MemcachedValue.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftMemcache/MemcachedValue.swift b/Sources/SwiftMemcache/MemcachedValue.swift index 465cea7..3ab27e9 100644 --- a/Sources/SwiftMemcache/MemcachedValue.swift +++ b/Sources/SwiftMemcache/MemcachedValue.swift @@ -33,7 +33,7 @@ extension MemcachedValue where Self: FixedWidthInteger { /// /// - Parameter buffer: The ByteBuffer to which the integer should be written. public func writeToBuffer(_ buffer: inout ByteBuffer) { - buffer.writeString(String(self)) + buffer.writeIntegerAsASCII(self) } /// Reads a FixedWidthInteger from a ByteBuffer. From 3ab280fe01e5e89a616c5fceb77ca3dcae101b30 Mon Sep 17 00:00:00 2001 From: dkz2 Date: Fri, 11 Aug 2023 13:10:19 -0500 Subject: [PATCH 10/10] readIntegerFromASCII --- Sources/SwiftMemcache/MemcachedValue.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/SwiftMemcache/MemcachedValue.swift b/Sources/SwiftMemcache/MemcachedValue.swift index 3ab27e9..abd8417 100644 --- a/Sources/SwiftMemcache/MemcachedValue.swift +++ b/Sources/SwiftMemcache/MemcachedValue.swift @@ -40,8 +40,7 @@ extension MemcachedValue where Self: FixedWidthInteger { /// /// - Parameter buffer: The ByteBuffer from which the value should be read. public static func readFromBuffer(_ buffer: inout ByteBuffer) -> Self? { - guard let string = buffer.readString(length: buffer.readableBytes)?.trimmingCharacters(in: .whitespacesAndNewlines) else { return nil } - return Self(string) + return buffer.readIntegerFromASCII() } }