Skip to content

Commit 8ae02ac

Browse files
committed
data refactor
1 parent 1bdeb3f commit 8ae02ac

File tree

2 files changed

+301
-197
lines changed

2 files changed

+301
-197
lines changed

Sources/PostgreSQL/Data/PartialPostgreSQLData.swift

+176-93
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,176 @@
1+
import COperatingSystem
2+
import Foundation
3+
14
/// Reference wrapper for `PostgreSQLData` being mutated
25
/// by the PostgreSQL data coders.
36
final class PartialPostgreSQLData {
47
/// The partial data.
5-
var data: PostgreSQLData
8+
var data: [String: PostgreSQLData]
69

710
/// Creates a new `PartialPostgreSQLData`.
8-
init(data: PostgreSQLData) {
11+
init(data: [String: PostgreSQLData]) {
912
self.data = data
1013
}
1114

1215
/// Sets the `PostgreSQLData` at supplied coding path.
1316
func set(_ data: PostgreSQLData, at path: [CodingKey]) {
14-
set(&self.data, to: data, at: path)
17+
guard path.count == 1 else {
18+
fatalError()
19+
}
20+
self.data[path[0].stringValue] = data
21+
// set(&self.data, to: data, at: path)
1522
}
1623

1724
/// Returns the value, if one at from the given path.
1825
func get(at path: [CodingKey]) -> PostgreSQLData? {
19-
var child = data
20-
for seg in path {
21-
switch child {
22-
case .array(let arr):
23-
guard let index = seg.intValue, arr.count > index else {
24-
return nil
25-
}
26-
child = arr[index]
27-
case .dictionary(let dict):
28-
guard let value = dict[seg.stringValue] else {
29-
return nil
30-
}
31-
child = value
32-
default:
33-
return nil
34-
}
35-
}
36-
return child
37-
}
38-
39-
/// Sets the mutable `PostgreSQLData` to supplied data at coding path.
40-
private func set(_ context: inout PostgreSQLData, to value: PostgreSQLData, at path: [CodingKey]) {
41-
guard path.count >= 1 else {
42-
context = value
43-
return
44-
}
45-
46-
let end = path[0]
47-
48-
var child: PostgreSQLData?
49-
switch path.count {
50-
case 1:
51-
child = value
52-
case 2...:
53-
if let index = end.intValue {
54-
let array = context.array ?? []
55-
if array.count > index {
56-
child = array[index]
57-
} else {
58-
child = PostgreSQLData.array([])
59-
}
60-
set(&child!, to: value, at: Array(path[1...]))
61-
} else {
62-
child = context.dictionary?[end.stringValue] ?? PostgreSQLData.dictionary([:])
63-
set(&child!, to: value, at: Array(path[1...]))
64-
}
65-
default: break
66-
}
67-
68-
if let index = end.intValue {
69-
if case .array(var arr) = context {
70-
if arr.count > index {
71-
arr[index] = child ?? .null
72-
} else {
73-
arr.append(child ?? .null)
74-
}
75-
context = .array(arr)
76-
} else if let child = child {
77-
context = .array([child])
78-
}
79-
} else {
80-
if case .dictionary(var dict) = context {
81-
dict[end.stringValue] = child
82-
context = .dictionary(dict)
83-
} else if let child = child {
84-
context = .dictionary([
85-
end.stringValue: child
86-
])
87-
}
26+
guard path.count == 1 else {
27+
fatalError()
8828
}
29+
return self.data[path[0].stringValue]
30+
// var child = data
31+
// for seg in path {
32+
// switch child {
33+
// case .array(let arr):
34+
// guard let index = seg.intValue, arr.count > index else {
35+
// return nil
36+
// }
37+
// child = arr[index]
38+
// case .dictionary(let dict):
39+
// guard let value = dict[seg.stringValue] else {
40+
// return nil
41+
// }
42+
// child = value
43+
// default:
44+
// return nil
45+
// }
46+
// }
47+
// return child
8948
}
49+
50+
// /// Sets the mutable `PostgreSQLData` to supplied data at coding path.
51+
// private func set(_ context: inout PostgreSQLData, to value: PostgreSQLData, at path: [CodingKey]) {
52+
// guard path.count >= 1 else {
53+
// context = value
54+
// return
55+
// }
56+
//
57+
// let end = path[0]
58+
//
59+
// var child: PostgreSQLData?
60+
// switch path.count {
61+
// case 1:
62+
// child = value
63+
// case 2...:
64+
// if let index = end.intValue {
65+
// let array = context.array ?? []
66+
// if array.count > index {
67+
// child = array[index]
68+
// } else {
69+
// child = PostgreSQLData.array([])
70+
// }
71+
// set(&child!, to: value, at: Array(path[1...]))
72+
// } else {
73+
// child = context.dictionary?[end.stringValue] ?? PostgreSQLData.dictionary([:])
74+
// set(&child!, to: value, at: Array(path[1...]))
75+
// }
76+
// default: break
77+
// }
78+
//
79+
// if let index = end.intValue {
80+
// if case .array(var arr) = context {
81+
// if arr.count > index {
82+
// arr[index] = child ?? .null
83+
// } else {
84+
// arr.append(child ?? .null)
85+
// }
86+
// context = .array(arr)
87+
// } else if let child = child {
88+
// context = .array([child])
89+
// }
90+
// } else {
91+
// if case .dictionary(var dict) = context {
92+
// dict[end.stringValue] = child
93+
// context = .dictionary(dict)
94+
// } else if let child = child {
95+
// context = .dictionary([
96+
// end.stringValue: child
97+
// ])
98+
// }
99+
// }
100+
// }
90101
}
91102

92103

93104
/// MARK: Encoding Convenience
94105

106+
extension FixedWidthInteger {
107+
/// Big-endian bytes for this integer.
108+
fileprivate var data: Data {
109+
var bytes = [UInt8](repeating: 0, count: Self.bitWidth / 8)
110+
var intNetwork = bigEndian
111+
memcpy(&bytes, &intNetwork, bytes.count)
112+
return Data(bytes)
113+
}
114+
}
115+
116+
extension FloatingPoint {
117+
/// Big-endian bytes for this floating-point number.
118+
fileprivate var data: Data {
119+
var bytes = [UInt8](repeating: 0, count: MemoryLayout<Self>.size)
120+
var copy = self
121+
memcpy(&bytes, &copy, bytes.count)
122+
return Data(bytes.reversed())
123+
}
124+
}
125+
126+
extension Data {
127+
/// Converts this data to a floating-point number.
128+
fileprivate func makeFloatingPoint<F>(_ type: F.Type = F.self) -> F where F: FloatingPoint {
129+
return Data(reversed()).unsafeCast()
130+
}
131+
132+
/// Converts this data to a fixed-width integer.
133+
fileprivate func makeFixedWidthInteger<I>(_ type: I.Type = I.self) -> I where I: FixedWidthInteger {
134+
return unsafeCast(to: I.self).bigEndian
135+
}
136+
137+
fileprivate func unsafeCast<T>(to type: T.Type = T.self) -> T {
138+
return withUnsafeBytes { (pointer: UnsafePointer<T>) -> T in
139+
return pointer.pointee
140+
}
141+
}
142+
143+
/// Convert the row's data into a string, throwing if invalid encoding.
144+
fileprivate func makeString(encoding: String.Encoding = .utf8) throws -> String {
145+
guard let string = String(data: self, encoding: encoding) else {
146+
throw PostgreSQLError(identifier: "utf8String", reason: "Unexpected non-UTF8 string.")
147+
}
148+
149+
return string
150+
}
151+
}
152+
95153
extension PartialPostgreSQLData {
96154
/// Sets a generic fixed width integer to the supplied path.
97155
func setFixedWidthInteger<U>(_ value: U, at path: [CodingKey]) throws
98156
where U: FixedWidthInteger
99157
{
158+
// switch U.bitWidth {
159+
// case 8: try set(.int8(safeCast(value, at: path)), at: path)
160+
// case 16: try set(.int16(safeCast(value, at: path)), at: path)
161+
// case 32: try set(.int32(safeCast(value, at: path)), at: path)
162+
// case 64: try set(.int64(safeCast(value, at: path)), at: path)
163+
// default: throw DecodingError.typeMismatch(U.self, .init(codingPath: path, debugDescription: "Integer bit width not supported: \(U.bitWidth)"))
164+
// }
165+
let type: PostgreSQLDataType
100166
switch U.bitWidth {
101-
case 8: try set(.int8(safeCast(value, at: path)), at: path)
102-
case 16: try set(.int16(safeCast(value, at: path)), at: path)
103-
case 32: try set(.int32(safeCast(value, at: path)), at: path)
104-
case 64: try set(.int64(safeCast(value, at: path)), at: path)
167+
case 8: type = .char
168+
case 16: type = .int2
169+
case 32: type = .int4
170+
case 64: type = .int8
105171
default: throw DecodingError.typeMismatch(U.self, .init(codingPath: path, debugDescription: "Integer bit width not supported: \(U.bitWidth)"))
106172
}
173+
set(.init(type: type, data: value.data), at: path)
107174
}
108175

109176
/// Sets an encodable value at the supplied path.
@@ -142,14 +209,18 @@ extension PartialPostgreSQLData {
142209
}
143210

144211
/// Gets a `Float` from the supplied path or throws a decoding error.
145-
func requireFixedWidthItenger<I>(_ type: I.Type = I.self, at path: [CodingKey]) throws -> I
212+
func requireFixedWidthInteger<I>(_ type: I.Type = I.self, at path: [CodingKey]) throws -> I
146213
where I: FixedWidthInteger
147214
{
148-
switch try requireGet(I.self, at: path) {
149-
case .int8(let value): return try safeCast(value, at: path)
150-
case .int16(let value): return try safeCast(value, at: path)
151-
case .int32(let value): return try safeCast(value, at: path)
152-
case .int64(let value): return try safeCast(value, at: path)
215+
let data = try requireGet(I.self, at: path)
216+
guard let value = data.data else {
217+
fatalError()
218+
}
219+
switch data.type {
220+
case .char: return try safeCast(value.makeFixedWidthInteger(Int8.self), at: path)
221+
case .int2: return try safeCast(value.makeFixedWidthInteger(Int16.self), at: path)
222+
case .int4: return try safeCast(value.makeFixedWidthInteger(Int32.self), at: path)
223+
case .int8: return try safeCast(value.makeFixedWidthInteger(Int64.self), at: path)
153224
default: throw DecodingError.typeMismatch(type, .init(codingPath: path, debugDescription: ""))
154225
}
155226
}
@@ -176,29 +247,41 @@ extension PartialPostgreSQLData {
176247
func requireFloatingPoint<F>(_ type: F.Type = F.self, at path: [CodingKey]) throws -> F
177248
where F: BinaryFloatingPoint
178249
{
179-
switch try requireGet(F.self, at: path) {
180-
case .int8(let value): return F(value)
181-
case .int16(let value): return F(value)
182-
case .int32(let value): return F(value)
183-
case .int64(let value): return F(value)
184-
case .float(let value): return F(value)
185-
case .double(let value): return F(value)
250+
let data = try requireGet(F.self, at: path)
251+
guard let value = data.data else {
252+
fatalError()
253+
}
254+
switch data.type {
255+
case .char: return F(value.makeFixedWidthInteger(Int8.self))
256+
case .int2: return F(value.makeFixedWidthInteger(Int16.self))
257+
case .int4: return F(value.makeFixedWidthInteger(Int32.self))
258+
case .int8: return F(value.makeFixedWidthInteger(Int64.self))
259+
case .float4: return F(value.makeFloatingPoint(Float.self))
260+
case .float8: return F(value.makeFloatingPoint(Double.self))
186261
default: throw DecodingError.typeMismatch(F.self, .init(codingPath: path, debugDescription: ""))
187262
}
188263
}
189264

190265
/// Gets a `String` from the supplied path or throws a decoding error.
191266
func requireString(at path: [CodingKey]) throws -> String {
192-
switch try requireGet(String.self, at: path) {
193-
case .string(let value): return value
267+
let data = try requireGet(String.self, at: path)
268+
guard let value = data.data else {
269+
fatalError()
270+
}
271+
switch data.type {
272+
case .text: return String(data: value, encoding: .utf8) !! "Non-utf8"
194273
default: throw DecodingError.typeMismatch(String.self, .init(codingPath: path, debugDescription: ""))
195274
}
196275
}
197276

198277
/// Gets a `Bool` from the supplied path or throws a decoding error.
199278
func requireBool(at path: [CodingKey]) throws -> Bool {
200-
switch try requireGet(Bool.self, at: path) {
201-
case .bool(let value): return value
279+
let data = try requireGet(String.self, at: path)
280+
guard let value = data.data else {
281+
fatalError()
282+
}
283+
switch data.type {
284+
case .char: return value.makeFixedWidthInteger(Int8.self) == 1
202285
default: throw DecodingError.typeMismatch(Bool.self, .init(codingPath: path, debugDescription: ""))
203286
}
204287
}

0 commit comments

Comments
 (0)