forked from vapor/postgres-nio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathArray+PostgresCodable.swift
179 lines (133 loc) · 5.14 KB
/
Array+PostgresCodable.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import NIOCore
import struct Foundation.UUID
// MARK: Protocols
/// A type, of which arrays can be encoded into and decoded from a postgres binary format
public protocol PostgresArrayEncodable: PostgresEncodable {
static var psqlArrayType: PostgresDataType { get }
}
/// A type that can be decoded into a Swift Array of its own type from a Postgres array.
public protocol PostgresArrayDecodable: PostgresDecodable {}
// MARK: Element conformances
extension Bool: PostgresArrayDecodable {}
extension Bool: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .boolArray }
}
extension ByteBuffer: PostgresArrayDecodable {}
extension ByteBuffer: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .byteaArray }
}
extension UInt8: PostgresArrayDecodable {}
extension UInt8: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .charArray }
}
extension Int16: PostgresArrayDecodable {}
extension Int16: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .int2Array }
}
extension Int32: PostgresArrayDecodable {}
extension Int32: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .int4Array }
}
extension Int64: PostgresArrayDecodable {}
extension Int64: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .int8Array }
}
extension Int: PostgresArrayDecodable {}
extension Int: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType {
if MemoryLayout<Int>.size == 8 {
return .int8Array
}
return .int4Array
}
}
extension Float: PostgresArrayDecodable {}
extension Float: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .float4Array }
}
extension Double: PostgresArrayDecodable {}
extension Double: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .float8Array }
}
extension String: PostgresArrayDecodable {}
extension String: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .textArray }
}
extension UUID: PostgresArrayDecodable {}
extension UUID: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { .uuidArray }
}
// MARK: Array conformances
extension Array: PostgresEncodable where Element: PostgresArrayEncodable {
public static var psqlType: PostgresDataType {
Element.psqlArrayType
}
public static var psqlFormat: PostgresFormat {
.binary
}
@inlinable
public func encode<JSONEncoder: PostgresJSONEncoder>(
into buffer: inout ByteBuffer,
context: PostgresEncodingContext<JSONEncoder>
) throws {
// 0 if empty, 1 if not
buffer.writeInteger(self.isEmpty ? 0 : 1, as: UInt32.self)
// b
buffer.writeInteger(0, as: Int32.self)
// array element type
buffer.writeInteger(Element.psqlType.rawValue)
// continue if the array is not empty
guard !self.isEmpty else {
return
}
// length of array
buffer.writeInteger(numericCast(self.count), as: Int32.self)
// dimensions
buffer.writeInteger(1, as: Int32.self)
try self.forEach { element in
try element.encodeRaw(into: &buffer, context: context)
}
}
}
extension Array: PostgresDecodable where Element: PostgresArrayDecodable, Element == Element._DecodableType {
public init<JSONDecoder: PostgresJSONDecoder>(
from buffer: inout ByteBuffer,
type: PostgresDataType,
format: PostgresFormat,
context: PostgresDecodingContext<JSONDecoder>
) throws {
guard case .binary = format else {
// currently we only support decoding arrays in binary format.
throw PostgresDecodingError.Code.failure
}
guard let (isNotEmpty, b, element) = buffer.readMultipleIntegers(endianness: .big, as: (Int32, Int32, UInt32).self),
0 <= isNotEmpty, isNotEmpty <= 1, b == 0
else {
throw PostgresDecodingError.Code.failure
}
let elementType = PostgresDataType(element)
guard isNotEmpty == 1 else {
self = []
return
}
guard let (expectedArrayCount, dimensions) = buffer.readMultipleIntegers(endianness: .big, as: (Int32, Int32).self),
expectedArrayCount > 0,
dimensions == 1
else {
throw PostgresDecodingError.Code.failure
}
var result = Array<Element>()
result.reserveCapacity(Int(expectedArrayCount))
for _ in 0 ..< expectedArrayCount {
guard let elementLength = buffer.readInteger(as: Int32.self), elementLength >= 0 else {
throw PostgresDecodingError.Code.failure
}
guard var elementBuffer = buffer.readSlice(length: numericCast(elementLength)) else {
throw PostgresDecodingError.Code.failure
}
let element = try Element.init(from: &elementBuffer, type: elementType, format: format, context: context)
result.append(element)
}
self = result
}
}