Skip to content

Commit 81ca909

Browse files
authored
Faster decoding, thanks to fewer bound checks. (#203)
1 parent 2c49bee commit 81ca909

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+178
-218
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ let package = Package(
1313
.library(name: "PostgresNIO", targets: ["PostgresNIO"]),
1414
],
1515
dependencies: [
16-
.package(url: "https://github.com/apple/swift-nio.git", from: "2.33.0"),
16+
.package(url: "https://github.com/apple/swift-nio.git", from: "2.35.0"),
1717
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.14.1"),
1818
.package(url: "https://github.com/apple/swift-crypto.git", "1.0.0" ..< "3.0.0"),
1919
.package(url: "https://github.com/apple/swift-metrics.git", from: "2.0.0"),

Sources/PostgresNIO/Message/PostgresMessage+Authentication.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ extension PostgresMessage {
2323
case 10:
2424
var mechanisms: [String] = []
2525
while buffer.readableBytes > 0 {
26-
guard let nextString = buffer.psqlReadNullTerminatedString() else {
26+
guard let nextString = buffer.readNullTerminatedString() else {
2727
throw PostgresError.protocol("Could not parse SASL mechanisms from authentication message")
2828
}
2929
if nextString.isEmpty {
@@ -68,7 +68,7 @@ extension PostgresMessage {
6868
case .saslMechanisms(let mechanisms):
6969
buffer.writeInteger(10, as: Int32.self)
7070
mechanisms.forEach {
71-
buffer.psqlWriteNullTerminatedString($0)
71+
buffer.writeNullTerminatedString($0)
7272
}
7373
case .saslContinue(let challenge):
7474
buffer.writeInteger(11, as: Int32.self)

Sources/PostgresNIO/Message/PostgresMessage+Bind.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ extension PostgresMessage {
3939

4040
/// Serializes this message into a byte buffer.
4141
public func serialize(into buffer: inout ByteBuffer) {
42-
buffer.psqlWriteNullTerminatedString(self.portalName)
43-
buffer.psqlWriteNullTerminatedString(self.statementName)
42+
buffer.writeNullTerminatedString(self.portalName)
43+
buffer.writeNullTerminatedString(self.statementName)
4444

4545
buffer.write(array: self.parameterFormatCodes)
4646
buffer.write(array: self.parameters) {

Sources/PostgresNIO/Message/PostgresMessage+Close.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ extension PostgresMessage {
3333
/// Serializes this message into a byte buffer.
3434
public func serialize(into buffer: inout ByteBuffer) throws {
3535
buffer.writeInteger(target.rawValue)
36-
buffer.psqlWriteNullTerminatedString(name)
36+
buffer.writeNullTerminatedString(name)
3737
}
3838
}
3939
}

Sources/PostgresNIO/Message/PostgresMessage+CommandComplete.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ extension PostgresMessage {
55
public struct CommandComplete: PostgresMessageType {
66
/// Parses an instance of this message type from a byte buffer.
77
public static func parse(from buffer: inout ByteBuffer) throws -> CommandComplete {
8-
guard let string = buffer.psqlReadNullTerminatedString() else {
8+
guard let string = buffer.readNullTerminatedString() else {
99
throw PostgresError.protocol("Could not parse close response message")
1010
}
1111
return .init(tag: string)

Sources/PostgresNIO/Message/PostgresMessage+Describe.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ extension PostgresMessage {
3131
/// Serializes this message into a byte buffer.
3232
public func serialize(into buffer: inout ByteBuffer) {
3333
buffer.writeInteger(command.rawValue)
34-
buffer.psqlWriteNullTerminatedString(name)
34+
buffer.writeNullTerminatedString(name)
3535
}
3636
}
3737
}

Sources/PostgresNIO/Message/PostgresMessage+Error.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extension PostgresMessage {
1111
public static func parse(from buffer: inout ByteBuffer) throws -> Error {
1212
var fields: [Field: String] = [:]
1313
while let field = buffer.readInteger(as: Field.self) {
14-
guard let string = buffer.psqlReadNullTerminatedString() else {
14+
guard let string = buffer.readNullTerminatedString() else {
1515
throw PostgresError.protocol("Could not read error response string.")
1616
}
1717
fields[field] = string

Sources/PostgresNIO/Message/PostgresMessage+Execute.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extension PostgresMessage {
2020

2121
/// Serializes this message into a byte buffer.
2222
public func serialize(into buffer: inout ByteBuffer) {
23-
buffer.psqlWriteNullTerminatedString(portalName)
23+
buffer.writeNullTerminatedString(portalName)
2424
buffer.writeInteger(self.maxRows)
2525
}
2626
}

Sources/PostgresNIO/Message/PostgresMessage+NotificationResponse.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ extension PostgresMessage {
1010
guard let backendPID: Int32 = buffer.readInteger() else {
1111
throw PostgresError.protocol("Invalid NotificationResponse message: unable to read backend PID")
1212
}
13-
guard let channel = buffer.psqlReadNullTerminatedString() else {
13+
guard let channel = buffer.readNullTerminatedString() else {
1414
throw PostgresError.protocol("Invalid NotificationResponse message: unable to read channel")
1515
}
16-
guard let payload = buffer.psqlReadNullTerminatedString() else {
16+
guard let payload = buffer.readNullTerminatedString() else {
1717
throw PostgresError.protocol("Invalid NotificationResponse message: unable to read payload")
1818
}
1919
return .init(backendPID: backendPID, channel: channel, payload: payload)

Sources/PostgresNIO/Message/PostgresMessage+ParameterStatus.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ extension PostgresMessage {
44
public struct ParameterStatus: PostgresMessageType, CustomStringConvertible {
55
/// Parses an instance of this message type from a byte buffer.
66
public static func parse(from buffer: inout ByteBuffer) throws -> ParameterStatus {
7-
guard let parameter = buffer.psqlReadNullTerminatedString() else {
7+
guard let parameter = buffer.readNullTerminatedString() else {
88
throw PostgresError.protocol("Could not read parameter from parameter status message")
99
}
10-
guard let value = buffer.psqlReadNullTerminatedString() else {
10+
guard let value = buffer.readNullTerminatedString() else {
1111
throw PostgresError.protocol("Could not read value from parameter status message")
1212
}
1313
return .init(parameter: parameter, value: value)

Sources/PostgresNIO/Message/PostgresMessage+RowDescription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extension PostgresMessage {
1111
/// Describes a single field returns in a `RowDescription` message.
1212
public struct Field: CustomStringConvertible {
1313
static func parse(from buffer: inout ByteBuffer) throws -> Field {
14-
guard let name = buffer.psqlReadNullTerminatedString() else {
14+
guard let name = buffer.readNullTerminatedString() else {
1515
throw PostgresError.protocol("Could not read row description field name")
1616
}
1717
guard let tableOID = buffer.readInteger(as: UInt32.self) else {

Sources/PostgresNIO/Message/PostgresMessage+SASLResponse.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ extension PostgresMessage {
3838
public let initialData: [UInt8]
3939

4040
public static func parse(from buffer: inout ByteBuffer) throws -> PostgresMessage.SASLInitialResponse {
41-
guard let mechanism = buffer.psqlReadNullTerminatedString() else {
41+
guard let mechanism = buffer.readNullTerminatedString() else {
4242
throw PostgresError.protocol("Could not parse SASL mechanism from initial response message")
4343
}
4444
guard let dataLength = buffer.readInteger(as: Int32.self) else {
@@ -57,7 +57,7 @@ extension PostgresMessage {
5757
}
5858

5959
public func serialize(into buffer: inout ByteBuffer) throws {
60-
buffer.psqlWriteNullTerminatedString(mechanism)
60+
buffer.writeNullTerminatedString(mechanism)
6161
if initialData.count > 0 {
6262
buffer.writeInteger(Int32(initialData.count), as: Int32.self) // write(array:) writes Int16, which is incorrect here
6363
buffer.writeBytes(initialData)

Sources/PostgresNIO/New/Data/Array+PSQLCodable.swift

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,30 +108,25 @@ extension Array: PSQLDecodable where Element: PSQLArrayElement {
108108
throw PSQLCastingError.failure(targetType: Self.self, type: type, postgresData: buffer, context: context)
109109
}
110110

111-
guard let isNotEmpty = buffer.readInteger(as: Int32.self), (0...1).contains(isNotEmpty) else {
111+
guard let (isNotEmpty, b, element) = buffer.readMultipleIntegers(endianness: .big, as: (Int32, Int32, Int32).self),
112+
0 <= isNotEmpty, isNotEmpty <= 1, b == 0
113+
else {
112114
throw PSQLCastingError.failure(targetType: Self.self, type: type, postgresData: buffer, context: context)
113115
}
114116

115-
guard let b = buffer.readInteger(as: Int32.self), b == 0 else {
116-
throw PSQLCastingError.failure(targetType: Self.self, type: type, postgresData: buffer, context: context)
117-
}
118-
119-
guard let elementType = buffer.readInteger(as: PSQLDataType.self) else {
120-
throw PSQLCastingError.failure(targetType: Self.self, type: type, postgresData: buffer, context: context)
121-
}
117+
let elementType = PSQLDataType(rawValue: element)
122118

123119
guard isNotEmpty == 1 else {
124120
return []
125121
}
126122

127-
guard let expectedArrayCount = buffer.readInteger(as: Int32.self), expectedArrayCount > 0 else {
123+
guard let (expectedArrayCount, dimensions) = buffer.readMultipleIntegers(endianness: .big, as: (Int32, Int32).self),
124+
expectedArrayCount > 0,
125+
dimensions == 1
126+
else {
128127
throw PSQLCastingError.failure(targetType: Self.self, type: type, postgresData: buffer, context: context)
129128
}
130-
131-
guard let dimensions = buffer.readInteger(as: Int32.self), dimensions == 1 else {
132-
throw PSQLCastingError.failure(targetType: Self.self, type: type, postgresData: buffer, context: context)
133-
}
134-
129+
135130
var result = Array<Element>()
136131
result.reserveCapacity(Int(expectedArrayCount))
137132

Sources/PostgresNIO/New/Extensions/ByteBuffer+PSQL.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
11
import NIOCore
22

33
internal extension ByteBuffer {
4-
mutating func psqlWriteNullTerminatedString(_ string: String) {
5-
self.writeString(string)
6-
self.writeInteger(0, as: UInt8.self)
7-
}
8-
9-
mutating func psqlReadNullTerminatedString() -> String? {
10-
guard let nullIndex = readableBytesView.firstIndex(of: 0) else {
11-
return nil
12-
}
13-
14-
defer { moveReaderIndex(forwardBy: 1) }
15-
return readString(length: nullIndex - readerIndex)
16-
}
174

185
mutating func psqlWriteBackendMessageID(_ messageID: PSQLBackendMessage.ID) {
196
self.writeInteger(messageID.rawValue)

Sources/PostgresNIO/New/Messages/Authentication.swift

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ extension PSQLBackendMessage {
1616
case saslFinal(data: ByteBuffer)
1717

1818
static func decode(from buffer: inout ByteBuffer) throws -> Self {
19-
try buffer.psqlEnsureAtLeastNBytesRemaining(2)
20-
21-
// we have at least two bytes remaining, therefore we can force unwrap this read.
22-
let authID = buffer.readInteger(as: Int32.self)!
19+
let authID = try buffer.throwingReadInteger(as: Int32.self)
2320

2421
switch authID {
2522
case 0:
@@ -29,12 +26,10 @@ extension PSQLBackendMessage {
2926
case 3:
3027
return .plaintext
3128
case 5:
32-
try buffer.psqlEnsureExactNBytesRemaining(4)
33-
let salt1 = buffer.readInteger(as: UInt8.self)!
34-
let salt2 = buffer.readInteger(as: UInt8.self)!
35-
let salt3 = buffer.readInteger(as: UInt8.self)!
36-
let salt4 = buffer.readInteger(as: UInt8.self)!
37-
return .md5(salt: (salt1, salt2, salt3, salt4))
29+
guard let salt = buffer.readMultipleIntegers(endianness: .big, as: (UInt8, UInt8, UInt8, UInt8).self) else {
30+
throw PSQLPartialDecodingError.expectedAtLeastNRemainingBytes(4, actual: buffer.readableBytes)
31+
}
32+
return .md5(salt: salt)
3833
case 6:
3934
return .scmCredential
4035
case 7:
@@ -47,7 +42,7 @@ extension PSQLBackendMessage {
4742
case 10:
4843
var names = [String]()
4944
let endIndex = buffer.readerIndex + buffer.readableBytes
50-
while buffer.readerIndex < endIndex, let next = buffer.psqlReadNullTerminatedString() {
45+
while buffer.readerIndex < endIndex, let next = buffer.readNullTerminatedString() {
5146
names.append(next)
5247
}
5348

Sources/PostgresNIO/New/Messages/BackendKeyData.swift

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,10 @@ extension PSQLBackendMessage {
77
let secretKey: Int32
88

99
static func decode(from buffer: inout ByteBuffer) throws -> Self {
10-
try buffer.psqlEnsureExactNBytesRemaining(8)
11-
12-
// We have verified the correct length before, this means we have exactly eight bytes
13-
// to read. If we have enough readable bytes, a read of Int32 should always succeed.
14-
// Therefore we can force unwrap here.
15-
let processID = buffer.readInteger(as: Int32.self)!
16-
let secretKey = buffer.readInteger(as: Int32.self)!
17-
10+
guard let (processID, secretKey) = buffer.readMultipleIntegers(endianness: .big, as: (Int32, Int32).self) else {
11+
throw PSQLPartialDecodingError.expectedAtLeastNRemainingBytes(8, actual: buffer.readableBytes)
12+
}
13+
1814
return .init(processID: processID, secretKey: secretKey)
1915
}
2016
}

Sources/PostgresNIO/New/Messages/Bind.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ extension PSQLFrontendMessage {
1313
var parameters: [PSQLEncodable]
1414

1515
func encode(into buffer: inout ByteBuffer, using jsonEncoder: PSQLJSONEncoder) throws {
16-
buffer.psqlWriteNullTerminatedString(self.portalName)
17-
buffer.psqlWriteNullTerminatedString(self.preparedStatementName)
16+
buffer.writeNullTerminatedString(self.portalName)
17+
buffer.writeNullTerminatedString(self.preparedStatementName)
1818

1919
// The number of parameter format codes that follow (denoted C below). This can be
2020
// zero to indicate that there are no parameters or that the parameters all use the

Sources/PostgresNIO/New/Messages/Cancel.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ extension PSQLFrontendMessage {
1515
let secretKey: Int32
1616

1717
func encode(into buffer: inout ByteBuffer) {
18-
buffer.writeInteger(self.cancelRequestCode)
19-
buffer.writeInteger(self.processID)
20-
buffer.writeInteger(self.secretKey)
18+
buffer.writeMultipleIntegers(self.cancelRequestCode, self.processID, self.secretKey)
2119
}
2220
}
2321
}

Sources/PostgresNIO/New/Messages/Close.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ extension PSQLFrontendMessage {
1010
switch self {
1111
case .preparedStatement(let name):
1212
buffer.writeInteger(UInt8(ascii: "S"))
13-
buffer.psqlWriteNullTerminatedString(name)
13+
buffer.writeNullTerminatedString(name)
1414
case .portal(let name):
1515
buffer.writeInteger(UInt8(ascii: "P"))
16-
buffer.psqlWriteNullTerminatedString(name)
16+
buffer.writeNullTerminatedString(name)
1717
}
1818
}
1919
}

Sources/PostgresNIO/New/Messages/DataRow.swift

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,19 @@ struct DataRow: PSQLBackendMessage.PayloadDecodable, Equatable {
1515
var bytes: ByteBuffer
1616

1717
static func decode(from buffer: inout ByteBuffer) throws -> Self {
18-
try buffer.psqlEnsureAtLeastNBytesRemaining(2)
19-
let columnCount = buffer.readInteger(as: Int16.self)!
18+
let columnCount = try buffer.throwingReadInteger(as: Int16.self)
2019
let firstColumnIndex = buffer.readerIndex
2120

2221
for _ in 0..<columnCount {
23-
try buffer.psqlEnsureAtLeastNBytesRemaining(2)
24-
let bufferLength = Int(buffer.readInteger(as: Int32.self)!)
25-
22+
let bufferLength = try buffer.throwingReadInteger(as: Int32.self)
2623
guard bufferLength >= 0 else {
2724
// if buffer length is negative, this means that the value is null
2825
continue
2926
}
30-
31-
try buffer.psqlEnsureAtLeastNBytesRemaining(bufferLength)
32-
buffer.moveReaderIndex(forwardBy: bufferLength)
27+
28+
try buffer.throwingMoveReaderIndex(forwardBy: Int(bufferLength))
3329
}
3430

35-
try buffer.psqlEnsureExactNBytesRemaining(0)
36-
3731
buffer.moveReaderIndex(to: firstColumnIndex)
3832
let columnSlice = buffer.readSlice(length: buffer.readableBytes)!
3933
return DataRow(columnCount: columnCount, bytes: columnSlice)

Sources/PostgresNIO/New/Messages/Describe.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ extension PSQLFrontendMessage {
1111
switch self {
1212
case .preparedStatement(let name):
1313
buffer.writeInteger(UInt8(ascii: "S"))
14-
buffer.psqlWriteNullTerminatedString(name)
14+
buffer.writeNullTerminatedString(name)
1515
case .portal(let name):
1616
buffer.writeInteger(UInt8(ascii: "P"))
17-
buffer.psqlWriteNullTerminatedString(name)
17+
buffer.writeNullTerminatedString(name)
1818
}
1919
}
2020
}

Sources/PostgresNIO/New/Messages/ErrorResponse.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ extension PSQLBackendMessage.PayloadDecodable where Self: PSQLMessageNotice {
117117
asType: PSQLBackendMessage.Field.self)
118118
}
119119

120-
guard let string = buffer.psqlReadNullTerminatedString() else {
120+
guard let string = buffer.readNullTerminatedString() else {
121121
throw PSQLPartialDecodingError.fieldNotDecodable(type: String.self)
122122
}
123123
fields[field] = string

Sources/PostgresNIO/New/Messages/Execute.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ extension PSQLFrontendMessage {
1515
}
1616

1717
func encode(into buffer: inout ByteBuffer) {
18-
buffer.psqlWriteNullTerminatedString(self.portalName)
18+
buffer.writeNullTerminatedString(self.portalName)
1919
buffer.writeInteger(self.maxNumberOfRows)
2020
}
2121
}

Sources/PostgresNIO/New/Messages/NotificationResponse.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@ extension PSQLBackendMessage {
88
let payload: String
99

1010
static func decode(from buffer: inout ByteBuffer) throws -> PSQLBackendMessage.NotificationResponse {
11-
try buffer.psqlEnsureAtLeastNBytesRemaining(6)
12-
let backendPID = buffer.readInteger(as: Int32.self)!
11+
let backendPID = try buffer.throwingReadInteger(as: Int32.self)
1312

14-
guard let channel = buffer.psqlReadNullTerminatedString() else {
13+
guard let channel = buffer.readNullTerminatedString() else {
1514
throw PSQLPartialDecodingError.fieldNotDecodable(type: String.self)
1615
}
17-
guard let payload = buffer.psqlReadNullTerminatedString() else {
16+
guard let payload = buffer.readNullTerminatedString() else {
1817
throw PSQLPartialDecodingError.fieldNotDecodable(type: String.self)
1918
}
2019

Sources/PostgresNIO/New/Messages/ParameterDescription.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,16 @@ extension PSQLBackendMessage {
77
var dataTypes: [PSQLDataType]
88

99
static func decode(from buffer: inout ByteBuffer) throws -> Self {
10-
try buffer.psqlEnsureAtLeastNBytesRemaining(2)
11-
12-
let parameterCount = buffer.readInteger(as: Int16.self)!
10+
let parameterCount = try buffer.throwingReadInteger(as: Int16.self)
1311
guard parameterCount >= 0 else {
1412
throw PSQLPartialDecodingError.integerMustBePositiveOrNull(parameterCount)
1513
}
1614

17-
try buffer.psqlEnsureExactNBytesRemaining(Int(parameterCount) * 4)
18-
1915
var result = [PSQLDataType]()
2016
result.reserveCapacity(Int(parameterCount))
2117

2218
for _ in 0..<parameterCount {
23-
let rawValue = buffer.readInteger(as: Int32.self)!
19+
let rawValue = try buffer.throwingReadInteger(as: Int32.self)
2420
result.append(PSQLDataType(rawValue))
2521
}
2622

0 commit comments

Comments
 (0)