diff --git a/Sources/Destiny/Socket.swift b/Sources/Destiny/Socket.swift index d27e796..76c3a12 100644 --- a/Sources/Destiny/Socket.swift +++ b/Sources/Destiny/Socket.swift @@ -62,10 +62,10 @@ public extension Socket { /// Reads `scalarCount` characters and loads them into the target SIMD. @inlinable - func readLineSIMD2() throws -> (T, Int) where T.Scalar == UInt8 { // read just the method, path & http version + func readLineSIMD2(length: Int) throws -> (T, Int) where T.Scalar == UInt8 { // read just the method, path & http version var string:T = T() let read:Int = try withUnsafeMutableBytes(of: &string) { p in - return try readSIMDBuffer(into: p.baseAddress!, length: T.scalarCount) + return try readSIMDBuffer(into: p.baseAddress!, length: length) } return (string, read) } @@ -76,7 +76,7 @@ public extension Socket { var head_count:Int = 0 var token:DestinyRoutePathType = .init() while true { - let (line, read):(SIMD64, Int) = try readLineSIMD2() + let (line, read):(SIMD64, Int) = try readLineSIMD2(length: 64) if head_count == 0 { token = line.lowHalf } @@ -160,15 +160,12 @@ public extension Socket { /// Reads multiple bytes and writes them into a buffer @inlinable func readSIMDBuffer(into baseAddress: UnsafeMutableRawPointer, length: Int) throws -> Int { - var bytes_read:Int = 0 if Task.isCancelled { return 0 } - let to_read:Int = min(Self.bufferLength, length - bytes_read) - let read_bytes:Int = recv(fileDescriptor, baseAddress + bytes_read, to_read, 0) + let read_bytes:Int = recv(fileDescriptor, baseAddress, length, 0) if read_bytes < 0 { // error throw SocketError.readBufferFailed() } - bytes_read += read_bytes - return bytes_read + return read_bytes } } diff --git a/Sources/DestinyUtilities/StackStrings.swift b/Sources/DestinyUtilities/StackStrings.swift index b86fac0..bc8bee7 100644 --- a/Sources/DestinyUtilities/StackStrings.swift +++ b/Sources/DestinyUtilities/StackStrings.swift @@ -589,4 +589,294 @@ public extension SIMD64 where Scalar : BinaryInteger { public extension SIMD2 where Scalar : BinaryInteger { @inlinable func hasSuffix(_ simd: SIMD2) -> Bool { } -}*/ \ No newline at end of file +}*/ + +// MARK: drop O(1) +// implementation should never change +public extension SIMD2 where Scalar : BinaryInteger { + /// Sets the trailing scalars to zero. + /// + /// - Parameters: + /// - length: The number of trailing scalars to set to zero. + /// - Complexity: O(1) + @inlinable + mutating func drop(_ length: Int) { + switch length { + case _ where length <= 0: + break + case 1: + self[1] = 0 + break + default: + self = .init() + break + } + } +} +public extension SIMD4 where Scalar : BinaryInteger { + /// Sets the trailing scalars to zero. + /// + /// - Parameters: + /// - length: The number of trailing scalars to set to zero. + /// - Complexity: O(1) + @inlinable + mutating func drop(_ length: Int) { + switch length { + case _ where length <= 0: + break + case 1: + highHalf[1] = 0 + break + case 2: + highHalf = .init() + break + case 3: + highHalf = .init() + lowHalf[1] = 0 + break + default: + self = .init() + break + } + } +} +public extension SIMD8 where Scalar : BinaryInteger { + /// Sets the trailing scalars to zero. + /// + /// - Parameters: + /// - length: The number of trailing scalars to set to zero. + /// - Complexity: O(1) + @inlinable + mutating func drop(_ length: Int) { + switch length { + case _ where length <= 0: + break + case 1...4: + highHalf.drop(length) + break + case 5...7: + highHalf = .init() + lowHalf.drop(length - 4) + break + default: + self = .init() + break + } + } +} +public extension SIMD16 where Scalar : BinaryInteger { + /// Sets the trailing scalars to zero. + /// + /// - Parameters: + /// - length: The number of trailing scalars to set to zero. + /// - Complexity: O(1) + mutating func drop(_ length: Int) { + switch length { + case _ where length <= 0: + break + case 1...8: + highHalf.drop(length) + break + case 9...15: + highHalf = .init() + lowHalf.drop(length - 8) + break + default: + self = .init() + break + } + } +} +public extension SIMD32 where Scalar : BinaryInteger { + /// Sets the trailing scalars to zero. + /// + /// - Parameters: + /// - length: The number of trailing scalars to set to zero. + /// - Complexity: O(1) + mutating func drop(_ length: Int) { + switch length { + case _ where length <= 0: + break + case 1...16: + highHalf.drop(length) + break + case 17...31: + highHalf = .init() + lowHalf.drop(length - 16) + break + default: + self = .init() + break + } + } +} +public extension SIMD64 where Scalar : BinaryInteger { + /// Sets the trailing scalars to zero. + /// + /// - Parameters: + /// - length: The number of trailing scalars to set to zero. + /// - Complexity: O(1) + mutating func drop(_ length: Int) { + switch length { + case _ where length <= 0: + break + case 1...32: + highHalf.drop(length) + break + case 33...63: + highHalf = .init() + lowHalf.drop(length - 32) + break + default: + self = .init() + break + } + } +} + +// MARK: keep O(1) +// implementation should never change +public extension SIMD2 where Scalar : BinaryInteger { + /// Keeps the leading scalar values and sets everything else to zero. + /// + /// - Complexity: O(1) + /// - Parameters: + /// - length: The number of leading scalars to keep. + @inlinable + mutating func keep(_ length: Int) { + switch length { + case _ where length <= 0: + self = .init() + break + case 1: + y = 0 + break + default: + break + } + } +} +public extension SIMD4 where Scalar : BinaryInteger { + /// Keeps the leading scalar values and sets everything else to zero. + /// + /// - Complexity: O(1) + /// - Parameters: + /// - length: The number of leading scalars to keep. + @inlinable + mutating func keep(_ length: Int) { + switch length { + case _ where length <= 0: + self = .init() + break + case 1: + y = 0 + z = 0 + w = 0 + break + case 2: + highHalf = .init() + break + case 3: + w = 0 + break + default: + break + } + } +} +public extension SIMD8 where Scalar : BinaryInteger { + /// Keeps the leading scalar values and sets everything else to zero. + /// + /// - Complexity: O(1) + /// - Parameters: + /// - length: The number of leading scalars to keep. + @inlinable + mutating func keep(_ length: Int) { + switch length { + case _ where length <= 0: + self = .init() + break + case 1...4: + lowHalf.keep(length) + highHalf = .init() + break + case 5...7: + highHalf.keep(length - 4) + break + default: + break + } + } +} +public extension SIMD16 where Scalar : BinaryInteger { + /// Keeps the leading scalar values and sets everything else to zero. + /// + /// - Complexity: O(1) + /// - Parameters: + /// - length: The number of leading scalars to keep. + @inlinable + mutating func keep(_ length: Int) { + switch length { + case _ where length <= 0: + self = .init() + break + case 1...8: + lowHalf.keep(length) + highHalf = .init() + break + case 9...15: + highHalf.keep(length - 8) + break + default: + break + } + } +} +public extension SIMD32 where Scalar : BinaryInteger { + /// Keeps the leading scalar values and sets everything else to zero. + /// + /// - Complexity: O(1) + /// - Parameters: + /// - length: The number of leading scalars to keep. + @inlinable + mutating func keep(_ length: Int) { + switch length { + case _ where length <= 0: + self = .init() + break + case 1...16: + lowHalf.keep(length) + highHalf = .init() + break + case 17...31: + highHalf.keep(length - 16) + break + default: + break + } + } +} +public extension SIMD64 where Scalar : BinaryInteger { + /// Keeps the leading scalar values and sets everything else to zero. + /// + /// - Complexity: O(1) + /// - Parameters: + /// - length: The number of leading scalars to keep. + @inlinable + mutating func keep(_ length: Int) { + switch length { + case _ where length <= 0: + self = .init() + break + case 1...32: + lowHalf.keep(length) + highHalf = .init() + break + case 33...63: + highHalf.keep(length - 32) + break + default: + break + } + } +} \ No newline at end of file diff --git a/Tests/DestinyTests/DestinyTests.swift b/Tests/DestinyTests/DestinyTests.swift index e503ccd..7585c46 100644 --- a/Tests/DestinyTests/DestinyTests.swift +++ b/Tests/DestinyTests/DestinyTests.swift @@ -11,121 +11,6 @@ import HTTPTypes import Testing struct DestinyTests { - @Test func simd_leadingNonzeroByteCount() { - var string:String = "siuerbnieprsbgsrgnpeirfnpae" - var ss32:StackString32 = StackString32(&string) - #expect(ss32.leadingNonzeroByteCount == 27) - - string = "ouerb\0gouwrgoruegbrotugbrotgenrotgurteg" - ss32 = StackString32(&string) - #expect(ss32.leadingNonzeroByteCount == 5) - - string = "" - var ss2:StackString2 = StackString2(&string) - #expect(ss2.leadingNonzeroByteCount == 0) - - string = "a" - ss2 = StackString2(&string) - #expect(ss2.leadingNonzeroByteCount == 1) - } - @Test func simd_trailingNonzeroByteCount() { - var string:String = "siuerbnieprsbgsrgnpeirfnpae" - var ss32:StackString32 = StackString32(&string) - #expect(ss32.trailingNonzeroByteCount == 0) - - string = "ouerbgouwrgoruegbrotugbtg\0enrotg" - ss32 = StackString32(&string) - #expect(ss32.trailingNonzeroByteCount == 6) - - string = "" - var ss2:StackString2 = StackString2(&string) - #expect(ss2.trailingNonzeroByteCount == 0) - - string = "a" - ss2 = StackString2(&string) - #expect(ss2.trailingNonzeroByteCount == 0) - } - @Test func simd_trailingZeroByteCount() { - var string:String = "siuerbnieprsbgsrgnpeirfnpae" - var ss32:StackString32 = StackString32(&string) - #expect(ss32.trailingZeroByteCount == 5) - - string = "ouerbgouwrgoruegbrotugbtg\0enrotg" - ss32 = StackString32(&string) - #expect(ss32.trailingZeroByteCount == 0) - - string = "" - var ss2:StackString2 = StackString2(&string) - #expect(ss2.trailingZeroByteCount == 2, Comment(rawValue: ss2.string())) - - string = "a" - ss2 = StackString2(&string) - #expect(ss2.trailingZeroByteCount == 1, Comment(rawValue: ss2.string())) - } - @Test func simd_hasPrefix() { - var string:String = "testing brother!?!" - let test:StackString32 = StackString32(&string) - #expect(test.hasPrefix(SIMD2(x: Character("t").asciiValue!, y: Character("e").asciiValue!))) - #expect(test.hasPrefix(SIMD4(Character("t").asciiValue!, Character("e").asciiValue!, Character("s").asciiValue!, Character("t").asciiValue!))) - #expect(test.hasPrefix(SIMD8(Character("t").asciiValue!, Character("e").asciiValue!, Character("s").asciiValue!, Character("t").asciiValue!, Character("i").asciiValue!, Character("n").asciiValue!, Character("g").asciiValue!, Character(" ").asciiValue!))) - #expect(test.hasPrefix(SIMD16( - Character("t").asciiValue!, - Character("e").asciiValue!, - Character("s").asciiValue!, - Character("t").asciiValue!, - Character("i").asciiValue!, - Character("n").asciiValue!, - Character("g").asciiValue!, - Character(" ").asciiValue!, - Character("b").asciiValue!, - Character("r").asciiValue!, - Character("o").asciiValue!, - Character("t").asciiValue!, - Character("h").asciiValue!, - Character("e").asciiValue!, - Character("r").asciiValue!, - Character("!").asciiValue! - ))) - } - @Test func simd_split() throws { - var string:String = "GET /dynamic/text HTTP/1.1" - var ss:StackString32 = StackString32(&string) - var values:[StackString32] = ss.splitSIMD(separator: 32) // space - try #require(values.count == 3) - #expect(values[0].string() == "GET") - #expect(values[1].string() == "/dynamic/text") - #expect(values[2].string() == "HTTP/1.1") - } - @Test func simd_split4() throws { - let ss:StackString4 = StackString4(31, 32, 33, 34) - var values:[StackString4] = ss.split(separator: 32) - - try #require(values.count == 2, Comment(rawValue: "\(values)")) - #expect(values[0] == SIMD4(31, 0, 0, 0)) - #expect(values[1] == SIMD4(33, 34, 0, 0)) - - values = ss.split(separator: 35) - try #require(values.count == 1) - #expect(values[0] == ss) - - values = ss.split(separator: 33) - try #require(values.count == 2, Comment(rawValue: "\(values)")) - #expect(values[0] == SIMD4(31, 32, 0 ,0)) - #expect(values[1] == SIMD4(34, 0, 0, 0)) - - values = ss.split(separator: 34) - try #require(values.count == 1, Comment(rawValue: "\(values)")) - #expect(values[0] == SIMD4(31, 32, 33, 0)) - - values = ss.split(separator: 31) - try #require(values.count == 1, Comment(rawValue: "\(values)")) - #expect(values[0] == SIMD4(32, 33, 34, 0)) - } - @Test func simd_string() throws { - var string:String = "brooooooooo !" - let ss:StackString64 = StackString64(&string) - #expect(ss.string() == "brooooooooo !") - } @Test func example() { let _:Router = #router( version: "HTTP/2.0", diff --git a/Tests/DestinyTests/SIMDTests.swift b/Tests/DestinyTests/SIMDTests.swift new file mode 100644 index 0000000..d254427 --- /dev/null +++ b/Tests/DestinyTests/SIMDTests.swift @@ -0,0 +1,160 @@ +// +// SIMDTests.swift +// +// +// Created by Evan Anderson on 11/8/24. +// + +import DestinyUtilities +import Testing + +struct SIMDTests { + + @Test func simd_leadingNonzeroByteCount() { + var string:String = "siuerbnieprsbgsrgnpeirfnpae" + var ss32:StackString32 = StackString32(&string) + #expect(ss32.leadingNonzeroByteCount == 27) + + string = "ouerb\0gouwrgoruegbrotugbrotgenrotgurteg" + ss32 = StackString32(&string) + #expect(ss32.leadingNonzeroByteCount == 5) + + string = "" + var ss2:StackString2 = StackString2(&string) + #expect(ss2.leadingNonzeroByteCount == 0) + + string = "a" + ss2 = StackString2(&string) + #expect(ss2.leadingNonzeroByteCount == 1) + } + + @Test func simd_trailingNonzeroByteCount() { + var string:String = "siuerbnieprsbgsrgnpeirfnpae" + var ss32:StackString32 = StackString32(&string) + #expect(ss32.trailingNonzeroByteCount == 0) + + string = "ouerbgouwrgoruegbrotugbtg\0enrotg" + ss32 = StackString32(&string) + #expect(ss32.trailingNonzeroByteCount == 6) + + string = "" + var ss2:StackString2 = StackString2(&string) + #expect(ss2.trailingNonzeroByteCount == 0) + + string = "a" + ss2 = StackString2(&string) + #expect(ss2.trailingNonzeroByteCount == 0) + } + + @Test func simd_trailingZeroByteCount() { + var string:String = "siuerbnieprsbgsrgnpeirfnpae" + var ss32:StackString32 = StackString32(&string) + #expect(ss32.trailingZeroByteCount == 5) + + string = "ouerbgouwrgoruegbrotugbtg\0enrotg" + ss32 = StackString32(&string) + #expect(ss32.trailingZeroByteCount == 0) + + string = "" + var ss2:StackString2 = StackString2(&string) + #expect(ss2.trailingZeroByteCount == 2, Comment(rawValue: ss2.string())) + + string = "a" + ss2 = StackString2(&string) + #expect(ss2.trailingZeroByteCount == 1, Comment(rawValue: ss2.string())) + } + + @Test func simd_hasPrefix() { + var string:String = "testing brother!?!" + let test:StackString32 = StackString32(&string) + #expect(test.hasPrefix(SIMD2(x: Character("t").asciiValue!, y: Character("e").asciiValue!))) + #expect(test.hasPrefix(SIMD4(Character("t").asciiValue!, Character("e").asciiValue!, Character("s").asciiValue!, Character("t").asciiValue!))) + #expect(test.hasPrefix(SIMD8(Character("t").asciiValue!, Character("e").asciiValue!, Character("s").asciiValue!, Character("t").asciiValue!, Character("i").asciiValue!, Character("n").asciiValue!, Character("g").asciiValue!, Character(" ").asciiValue!))) + #expect(test.hasPrefix(SIMD16( + Character("t").asciiValue!, + Character("e").asciiValue!, + Character("s").asciiValue!, + Character("t").asciiValue!, + Character("i").asciiValue!, + Character("n").asciiValue!, + Character("g").asciiValue!, + Character(" ").asciiValue!, + Character("b").asciiValue!, + Character("r").asciiValue!, + Character("o").asciiValue!, + Character("t").asciiValue!, + Character("h").asciiValue!, + Character("e").asciiValue!, + Character("r").asciiValue!, + Character("!").asciiValue! + ))) + } + + @Test func simd_split() throws { + var string:String = "GET /dynamic/text HTTP/1.1" + var ss:StackString32 = StackString32(&string) + var values:[StackString32] = ss.splitSIMD(separator: 32) // space + try #require(values.count == 3) + #expect(values[0].string() == "GET") + #expect(values[1].string() == "/dynamic/text") + #expect(values[2].string() == "HTTP/1.1") + } + + @Test func simd_split4() throws { + let ss:StackString4 = StackString4(31, 32, 33, 34) + var values:[StackString4] = ss.split(separator: 32) + + try #require(values.count == 2, Comment(rawValue: "\(values)")) + #expect(values[0] == SIMD4(31, 0, 0, 0)) + #expect(values[1] == SIMD4(33, 34, 0, 0)) + + values = ss.split(separator: 35) + try #require(values.count == 1) + #expect(values[0] == ss) + + values = ss.split(separator: 33) + try #require(values.count == 2, Comment(rawValue: "\(values)")) + #expect(values[0] == SIMD4(31, 32, 0 ,0)) + #expect(values[1] == SIMD4(34, 0, 0, 0)) + + values = ss.split(separator: 34) + try #require(values.count == 1, Comment(rawValue: "\(values)")) + #expect(values[0] == SIMD4(31, 32, 33, 0)) + + values = ss.split(separator: 31) + try #require(values.count == 1, Comment(rawValue: "\(values)")) + #expect(values[0] == SIMD4(32, 33, 34, 0)) + } + + @Test func simd_string() { + var string:String = "brooooooooo !" + let ss:StackString64 = StackString64(&string) + #expect(ss.string() == "brooooooooo !") + } + + @Test func simd_drop() { + var string:String = "iuebrgow eg347h0t34h t30834r034rgg3q 632 q 0928j3 m939n3 4580tw" + var ss64:StackString64 = StackString64(&string) + ss64.drop(1) + #expect(ss64.string() == "iuebrgow eg347h0t34h t30834r034rgg3q 632 q 0928j3 m939n3 4580t") + + ss64.drop(5) + #expect(ss64.string() == "iuebrgow eg347h0t34h t30834r034rgg3q 632 q 0928j3 m939n3 4") + + ss64.drop(32) + #expect(ss64.string() == "iuebrgow eg347h0t34h t30834r034r") + } + + @Test func simd_keep() { + var string:String = "iuebrgow eg347h0t34h t30834r034rgg3q 632 q 0928j3 m939n3 4580tw" + var ss64:StackString64 = StackString64(&string) + ss64.keep(35) + #expect(ss64.string() == "iuebrgow eg347h0t34h t30834r034rgg3") + + ss64.keep(14) + #expect(ss64.string() == "iuebrgow eg347") + + ss64.keep(8) + #expect(ss64.string() == "iuebrgow") + } +} \ No newline at end of file