blob: 766027e729a7a663ca3c9846939631147577fc0b [file] [log] [blame]
Chris Simpsona9b6c702018-04-08 07:11:37 -04001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20import Foundation
21
22public struct TBinaryProtocolVersion {
23 static let version1 = Int32(bitPattern: 0x80010000)
24 static let versionMask = Int32(bitPattern: 0xffff0000)
25}
26
27public class TBinaryProtocol: TProtocol {
28 public var messageSizeLimit: UInt32 = 0
29
30 public var transport: TTransport
31
32 // class level properties for setting global config (useful for server in lieu of Factory design)
33 public static var strictRead: Bool = false
34 public static var strictWrite: Bool = true
35
36 private var strictRead: Bool
37 private var strictWrite: Bool
38
39 var currentMessageName: String?
40 var currentFieldName: String?
41
42
43 public convenience init(transport: TTransport, strictRead: Bool, strictWrite: Bool) {
44 self.init(on: transport)
45 self.strictRead = strictRead
46 self.strictWrite = strictWrite
47 }
48
49 public required init(on transport: TTransport) {
50 self.transport = transport
51 self.strictWrite = TBinaryProtocol.strictWrite
52 self.strictRead = TBinaryProtocol.strictRead
53 }
54
55 func readStringBody(_ size: Int) throws -> String {
56
57 var data = Data()
58 try ProtocolTransportTry(error: TProtocolError(message: "Transport read failed")) {
59 data = try self.transport.readAll(size: size)
60 }
61
62 return String(data: data, encoding: String.Encoding.utf8) ?? ""
63 }
64
65 /// Mark: - TProtocol
66
67 public func readMessageBegin() throws -> (String, TMessageType, Int32) {
68 let size: Int32 = try read()
69 var messageName = ""
70 var type = TMessageType.exception
71
72 if size < 0 {
73 let version = size & TBinaryProtocolVersion.versionMask
74 if version != TBinaryProtocolVersion.version1 {
75 throw TProtocolError(error: .badVersion(expected: "\(TBinaryProtocolVersion.version1)",
76 got: "\(version)"))
77 }
78 type = TMessageType(rawValue: Int32(size) & 0x00FF) ?? type
79 messageName = try read()
80 } else {
81 if strictRead {
Chris Simpson2566ecd2018-08-29 14:40:44 -040082 let errorMessage = "Missing message version, old client? Message Name: \(currentMessageName ?? "")"
Chris Simpsona9b6c702018-04-08 07:11:37 -040083 throw TProtocolError(error: .invalidData,
84 message: errorMessage)
85 }
86 if messageSizeLimit > 0 && size > Int32(messageSizeLimit) {
87 throw TProtocolError(error: .sizeLimit(limit: Int(messageSizeLimit), got: Int(size)))
88 }
89
90 messageName = try readStringBody(Int(size))
91 type = TMessageType(rawValue: Int32(try read() as UInt8)) ?? type
92 }
93
94 let seqID: Int32 = try read()
95 return (messageName, type, seqID)
96 }
97
98 public func readMessageEnd() throws {
99 return
100 }
101
102 public func readStructBegin() throws -> String {
103 return ""
104 }
105
106 public func readStructEnd() throws {
107 return
108 }
109
110 public func readFieldBegin() throws -> (String, TType, Int32) {
111
112 let fieldType = TType(rawValue: Int32(try read() as UInt8)) ?? TType.stop
113 var fieldID: Int32 = 0
114
115 if fieldType != .stop {
116 fieldID = Int32(try read() as Int16)
117 }
118
119 return ("", fieldType, fieldID)
120 }
121
122 public func readFieldEnd() throws {
123 return
124 }
125
126 public func readMapBegin() throws -> (TType, TType, Int32) {
127 var raw = Int32(try read() as UInt8)
128 guard let keyType = TType(rawValue: raw) else {
129 throw TProtocolError(message: "Unknown value for keyType TType: \(raw)")
130 }
131
132 raw = Int32(try read() as UInt8)
133 guard let valueType = TType(rawValue: raw) else {
134 throw TProtocolError(message: "Unknown value for valueType TType: \(raw)")
135 }
136 let size: Int32 = try read()
137
138 return (keyType, valueType, size)
139 }
140
141 public func readMapEnd() throws {
142 return
143 }
144
145 public func readSetBegin() throws -> (TType, Int32) {
146 let raw = Int32(try read() as UInt8)
147 guard let elementType = TType(rawValue: raw) else {
148 throw TProtocolError(message: "Unknown value for elementType TType: \(raw)")
149 }
150
151 let size: Int32 = try read()
152
153 return (elementType, size)
154 }
155
156 public func readSetEnd() throws {
157 return
158 }
159
160 public func readListBegin() throws -> (TType, Int32) {
161 let raw = Int32(try read() as UInt8)
162 guard let elementType = TType(rawValue: raw) else {
163 throw TProtocolError(message: "Unknown value for elementType TType: \(raw)")
164 }
165 let size: Int32 = try read()
166
167 return (elementType, size)
168 }
169
170 public func readListEnd() throws {
171 return
172 }
173
174 public func read() throws -> String {
175 let data: Data = try read()
176 guard let str = String.init(data: data, encoding: .utf8) else {
177 throw TProtocolError(error: .invalidData, message: "Couldn't encode UTF-8 from data read")
178 }
179 return str
180 }
181
182 public func read() throws -> Bool {
183 return (try read() as UInt8) == 1
184 }
185
186 public func read() throws -> UInt8 {
187 var buff = Data()
188 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
189 buff = try self.transport.readAll(size: 1)
190 }
191 return buff[0]
192 }
193
Kino Roya9da9eb2022-10-07 23:13:01 -0700194 public func read() throws -> Int8 {
195 var buff = Data()
196 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
197 buff = try self.transport.readAll(size: 1)
198 }
199 return buff.withUnsafeBytes { pntr in
200 return pntr.load(as: Int8.self)
201 }
202 }
203
Chris Simpsona9b6c702018-04-08 07:11:37 -0400204 public func read() throws -> Int16 {
205 var buff = Data()
206 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
207 buff = try self.transport.readAll(size: 2)
208 }
209 var ret = Int16(buff[0] & 0xff) << 8
210 ret |= Int16(buff[1] & 0xff)
211 return ret
212 }
213
214 public func read() throws -> Int32 {
215 var buff = Data()
216 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
217 buff = try self.transport.readAll(size: 4)
218 }
219 var ret = Int32(buff[0] & 0xff) << 24
220 ret |= Int32(buff[1] & 0xff) << 16
221 ret |= Int32(buff[2] & 0xff) << 8
222 ret |= Int32(buff[3] & 0xff)
223
224 return ret
225 }
226
227 public func read() throws -> Int64 {
228 var buff = Data()
229 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
230 buff = try self.transport.readAll(size: 8)
231 }
232 var ret = Int64(buff[0] & 0xff) << 56
233 ret |= Int64(buff[1] & 0xff) << 48
234 ret |= Int64(buff[2] & 0xff) << 40
235 ret |= Int64(buff[3] & 0xff) << 32
236 ret |= Int64(buff[4] & 0xff) << 24
237 ret |= Int64(buff[5] & 0xff) << 16
238 ret |= Int64(buff[6] & 0xff) << 8
239 ret |= Int64(buff[7] & 0xff)
240
241 return ret
242 }
243
244 public func read() throws -> Double {
245 let val = try read() as Int64
Chris Simpson2566ecd2018-08-29 14:40:44 -0400246 return Double(bitPattern: UInt64(bitPattern: val))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400247 }
248
249 public func read() throws -> Data {
250 let size = Int(try read() as Int32)
251 var data = Data()
252 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
253 data = try self.transport.readAll(size: size)
254 }
255
256 return data
257 }
258
Kino Roya9da9eb2022-10-07 23:13:01 -0700259 public func read() throws -> UUID {
260 let data = try self.transport.readAll(size: 16)
261 let lsb = data[0..<8]
262 let msb = data[8..<16]
263
264 var id = UUID().uuid
265 withUnsafeMutableBytes(of: &id) { pntr in
266 var copyData = msb
267 copyData.append(lsb)
268 copyData.copyBytes(to: pntr)
269 }
270 return UUID(uuid: id)
271 }
272
Chris Simpsona9b6c702018-04-08 07:11:37 -0400273 // Write methods
274
275 public func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws {
276 if strictWrite {
277 let version = TBinaryProtocolVersion.version1 | Int32(messageType.rawValue)
278 try write(version)
279 try write(name)
280 try write(sequenceID)
281 } else {
282 try write(name)
283 try write(UInt8(messageType.rawValue))
284 try write(sequenceID)
285 }
286 currentMessageName = name
287 }
288
289 public func writeMessageEnd() throws {
290 currentMessageName = nil
291 }
292
293 public func writeStructBegin(name: String) throws {
294 return
295 }
296
297 public func writeStructEnd() throws {
298 return
299 }
300
301 public func writeFieldBegin(name: String, type fieldType: TType, fieldID: Int32) throws {
302 try write(UInt8(fieldType.rawValue))
303 try write(Int16(fieldID))
304 }
305
306 public func writeFieldStop() throws {
307 try write(UInt8(TType.stop.rawValue))
308 }
309
310 public func writeFieldEnd() throws {
311 return
312 }
313
314 public func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws {
315 try write(UInt8(keyType.rawValue))
316 try write(UInt8(valueType.rawValue))
317 try write(size)
318 }
319
320 public func writeMapEnd() throws {
321 return
322 }
323
324 public func writeSetBegin(elementType: TType, size: Int32) throws {
325 try write(UInt8(elementType.rawValue))
326 try write(size)
327 }
328
329 public func writeSetEnd() throws {
330 return
331 }
332
333 public func writeListBegin(elementType: TType, size: Int32) throws {
334 try write(UInt8(elementType.rawValue))
335 try write(size)
336 }
337
338 public func writeListEnd() throws {
339 return
340 }
341
342 public func write(_ value: String) throws {
343 try write(value.data(using: .utf8)!)
344 }
345
346 public func write(_ value: Bool) throws {
347 let byteVal: UInt8 = value ? 1 : 0
348 try write(byteVal)
349 }
350
351 public func write(_ value: UInt8) throws {
Antoine Cœur08a6eb62019-07-08 18:42:09 +0800352 let buff = Data([value])
Chris Simpsona9b6c702018-04-08 07:11:37 -0400353
354 try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
355 try self.transport.write(data: buff)
356 }
357 }
358
Kino Roya9da9eb2022-10-07 23:13:01 -0700359 public func write(_ value: Int8) throws {
360 var value = value
361 let buff = Data(bytes: &value, count: MemoryLayout<Int8>.size(ofValue: value))
362 try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
363 try self.transport.write(data: buff)
364 }
365 }
366
Chris Simpsona9b6c702018-04-08 07:11:37 -0400367 public func write(_ value: Int16) throws {
368 var buff = Data()
Antoine Cœur08a6eb62019-07-08 18:42:09 +0800369 buff.append(Data([UInt8(0xff & (value >> 8))]))
370 buff.append(Data([UInt8(0xff & (value))]))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400371 try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
372 try self.transport.write(data: buff)
373 }
374 }
375
376 public func write(_ value: Int32) throws {
377 var buff = Data()
Antoine Cœur08a6eb62019-07-08 18:42:09 +0800378 buff.append(Data([UInt8(0xff & (value >> 24))]))
379 buff.append(Data([UInt8(0xff & (value >> 16))]))
380 buff.append(Data([UInt8(0xff & (value >> 8))]))
381 buff.append(Data([UInt8(0xff & (value))]))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400382
383 try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
384 try self.transport.write(data: buff)
385 }
386 }
387
388 public func write(_ value: Int64) throws {
389 var buff = Data()
Antoine Cœur08a6eb62019-07-08 18:42:09 +0800390 buff.append(Data([UInt8(0xff & (value >> 56))]))
391 buff.append(Data([UInt8(0xff & (value >> 48))]))
392 buff.append(Data([UInt8(0xff & (value >> 40))]))
393 buff.append(Data([UInt8(0xff & (value >> 32))]))
394 buff.append(Data([UInt8(0xff & (value >> 24))]))
395 buff.append(Data([UInt8(0xff & (value >> 16))]))
396 buff.append(Data([UInt8(0xff & (value >> 8))]))
397 buff.append(Data([UInt8(0xff & (value))]))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400398
399 try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
400 try self.transport.write(data: buff)
401 }
402 }
403
404 public func write(_ value: Double) throws {
405 // Notably unsafe, since Double and Int64 are the same size, this should work fine
Chris Simpson2566ecd2018-08-29 14:40:44 -0400406 try self.write(Int64(bitPattern: value.bitPattern))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400407 }
408
409 public func write(_ data: Data) throws {
410 try write(Int32(data.count))
411
412 try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
413 try self.transport.write(data: data)
414 }
415 }
Kino Roya9da9eb2022-10-07 23:13:01 -0700416
417 public func write(_ value: UUID) throws {
418 let data = withUnsafePointer(to: value.uuid) {
419 Data(bytes: $0, count: MemoryLayout.size(ofValue: value.uuid))
420 }
421 let msb = data[0..<8]
422 let lsb = data[8..<16]
423
424 var buff = Data()
425 buff.append(lsb)
426 buff.append(msb)
427
428 try self.transport.write(data: buff)
429 }
Chris Simpsona9b6c702018-04-08 07:11:37 -0400430}