blob: 81a51f5511402c4d050efcf2f266033286e82cc8 [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
21import CoreFoundation
22
23public enum TCType: UInt8 {
24 case stop = 0x00
25 case boolean_TRUE = 0x01
26 case boolean_FALSE = 0x02
27 case i8 = 0x03
28 case i16 = 0x04
29 case i32 = 0x05
30 case i64 = 0x06
31 case double = 0x07
32 case binary = 0x08
33 case list = 0x09
34 case set = 0x0A
35 case map = 0x0B
36 case `struct` = 0x0C
37
38 public static let typeMask: UInt8 = 0xE0 // 1110 0000
39 public static let typeBits: UInt8 = 0x07 // 0000 0111
40 public static let typeShiftAmount = 5
41
42}
43
44
45public class TCompactProtocol: TProtocol {
46 public static let protocolID: UInt8 = 0x82
47 public static let version: UInt8 = 1
48 public static let versionMask: UInt8 = 0x1F // 0001 1111
49
50 public var transport: TTransport
51
52 var lastField: [UInt8] = []
53 var lastFieldId: UInt8 = 0
54
55 var boolFieldName: String?
56 var boolFieldType: TType?
57 var boolFieldId: Int32?
58 var booleanValue: Bool?
59
60 var currentMessageName: String?
61
62 public required init(on transport: TTransport) {
63 self.transport = transport
64 }
65
66
67 /// Mark: - TCompactProtocol helpers
68
69 func writebyteDirect(_ byte: UInt8) throws {
Antoine Cœur08a6eb62019-07-08 18:42:09 +080070 let byte = Data([byte])
Chris Simpsona9b6c702018-04-08 07:11:37 -040071 try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
72 try self.transport.write(data: byte)
73 }
74 }
75
76 func writeVarint32(_ val: UInt32) throws {
77 var val = val
78 var i32buf = [UInt8](repeating: 0, count: 5)
79 var idx = 0
80 while true {
81 if (val & ~0x7F) == 0 {
82 i32buf[idx] = UInt8(val)
83 idx += 1
84 break
85 } else {
86 i32buf[idx] = UInt8((val & 0x7F) | 0x80)
87 idx += 1
88 val >>= 7
89 }
90 }
91
92 try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
Antoine Cœur08a6eb62019-07-08 18:42:09 +080093 try self.transport.write(data: Data(i32buf[0..<idx]))
Chris Simpsona9b6c702018-04-08 07:11:37 -040094 }
95 }
96
97 func writeVarint64(_ val: UInt64) throws {
98 var val = val
99 var varint64out = [UInt8](repeating: 0, count: 10)
100 var idx = 0
101 while true {
102 if (val & ~0x7F) == 0{
103 varint64out[idx] = UInt8(val)
104 idx += 1
105 break
106 } else {
107 varint64out[idx] = UInt8(val & 0x7F) | 0x80
108 idx += 1
109 val >>= 7
110 }
111 }
112
113 try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
Antoine Cœur08a6eb62019-07-08 18:42:09 +0800114 try self.transport.write(data: Data(varint64out[0..<idx]))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400115 }
Chris Simpsona9b6c702018-04-08 07:11:37 -0400116 }
117
118 func writeCollectionBegin(_ elementType: TType, size: Int32) throws {
119 let ctype = compactType(elementType).rawValue
120 if size <= 14 {
121 try writebyteDirect(UInt8(size << 4) | ctype)
122 } else {
123 try writebyteDirect(0xF0 | ctype)
124 try writeVarint32(UInt32(size))
125 }
126 }
127
128 func readBinary(_ size: Int) throws -> Data {
129 var result = Data()
130 if size != 0 {
131 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
132 result = try self.transport.readAll(size: size)
133 }
134 }
135 return result
136 }
137
138 func readVarint32() throws -> UInt32 {
139 var result: UInt32 = 0
140 var shift: UInt32 = 0
141 while true {
142 let byte: UInt8 = try read()
143
144 result |= UInt32(byte & 0x7F) << shift
145 if (byte & 0x80) == 0 {
146 break
147 }
148
149 shift += 7
150 }
151
152 return result
153 }
154
155 func readVarint64() throws -> UInt64 {
156 var result: UInt64 = 0
157 var shift: UInt64 = 0
158
159 while true {
160 let byte: UInt8 = try read()
161
162 result |= UInt64(byte & 0x7F) << shift
163 if (byte & 0x80) == 0 {
164 break
165 }
166
167 shift += 7
168 }
169 return result
170 }
171
172
173 func ttype(_ compactTypeVal: UInt8) throws -> TType {
174 guard let compactType = TCType(rawValue: compactTypeVal) else {
175 throw TProtocolError(message: "Unknown TCType value: \(compactTypeVal)")
176 }
177
178 switch compactType {
179 case .stop: return .stop;
180 case .boolean_FALSE, .boolean_TRUE: return .bool;
181 case .i8: return .i8;
182 case .i16: return .i16;
183 case .i32: return .i32;
184 case .i64: return .i64;
185 case .double: return .double;
186 case .binary: return .string;
187 case .list: return .list;
188 case .set: return .set;
189 case .map: return .map;
190 case .struct: return .struct;
191 }
192 }
193
194 func compactType(_ ttype: TType) -> TCType {
195 switch ttype {
196 case .stop: return .stop
197 case .void: return .i8
198 case .bool: return .boolean_FALSE
199 case .i8: return .i8
200 case .double: return .double
201 case .i16: return .i16
202 case .i32: return .i32
203 case .i64: return .i64
204 case .string: return .binary
205 case .struct: return .struct
206 case .map: return .map
207 case .set: return .set
208 case .list: return .list
209 case .utf8: return .binary
210 case .utf16: return .binary
211 }
212 }
213
214 /// ZigZag encoding maps signed integers to unsigned integers so that
215 /// numbers with a small absolute value (for instance, -1) have
216 /// a small varint encoded value too. It does this in a way that
217 /// "zig-zags" back and forth through the positive and negative integers,
218 /// so that -1 is encoded as 1, 1 is encoded as 2, -2 is encoded as 3, and so
219 ///
220 /// - parameter n: number to zigzag
221 ///
222 /// - returns: zigzaged UInt32
223 func i32ToZigZag(_ n : Int32) -> UInt32 {
Chris Simpson2566ecd2018-08-29 14:40:44 -0400224 return UInt32(bitPattern: Int32(n << 1) ^ Int32(n >> 31))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400225 }
226
227 func i64ToZigZag(_ n : Int64) -> UInt64 {
Chris Simpson2566ecd2018-08-29 14:40:44 -0400228 return UInt64(bitPattern: Int64(n << 1) ^ Int64(n >> 63))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400229 }
230
231 func zigZagToi32(_ n: UInt32) -> Int32 {
232 return Int32(n >> 1) ^ (-Int32(n & 1))
233 }
234
235 func zigZagToi64(_ n: UInt64) -> Int64 {
236 return Int64(n >> 1) ^ (-Int64(n & 1))
237 }
238
239
240
241 /// Mark: - TProtocol
242
243 public func readMessageBegin() throws -> (String, TMessageType, Int32) {
244 let protocolId: UInt8 = try read()
245
246 if protocolId != TCompactProtocol.protocolID {
247 let expected = String(format:"%2X", TCompactProtocol.protocolID)
248 let got = String(format:"%2X", protocolId)
249 throw TProtocolError(message: "Wrong Protocol ID \(got)",
250 extendedError: .mismatchedProtocol(expected: expected, got: got))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400251 }
252
253 let versionAndType: UInt8 = try read()
254 let version: UInt8 = versionAndType & TCompactProtocol.versionMask
255 if version != TCompactProtocol.version {
256 throw TProtocolError(error: .badVersion(expected: "\(TCompactProtocol.version)",
257 got:"\(version)"))
Chris Simpsona9b6c702018-04-08 07:11:37 -0400258 }
259
260 let type = (versionAndType >> UInt8(TCType.typeShiftAmount)) & TCType.typeBits
261 guard let mtype = TMessageType(rawValue: Int32(type)) else {
262 throw TProtocolError(message: "Unknown TMessageType value: \(type)")
263 }
264 let sequenceId = try readVarint32()
265 let name: String = try read()
266
267 return (name, mtype, Int32(sequenceId))
268 }
269
270 public func readMessageEnd() throws { }
271
272 public func readStructBegin() throws -> String {
273 lastField.append(lastFieldId)
274 lastFieldId = 0
275 return ""
276 }
277
278 public func readStructEnd() throws {
279 lastFieldId = lastField.last ?? 0
280 lastField.removeLast()
281 }
282
283 public func readFieldBegin() throws -> (String, TType, Int32) {
284 let byte: UInt8 = try read()
285 guard let type = TCType(rawValue: byte & 0x0F) else {
286 throw TProtocolError(message: "Unknown TCType \(byte & 0x0F)")
287 }
288
289 // if it's a stop, then we can return immediately, as the struct is over
290 if type == .stop {
291 return ("", .stop, 0)
292 }
293
294 var fieldId: Int16 = 0
295
296 // mask off the 4MSB of the type header. it could contain a field id delta
297 let modifier = (byte & 0xF0) >> 4
298 if modifier == 0 {
299 // not a delta. look ahead for the zigzag varint field id
300 fieldId = try read()
301 } else {
302 // has a delta. add the delta to the last Read field id.
303 fieldId = Int16(lastFieldId + modifier)
304 }
305
306 let fieldType = try ttype(type.rawValue)
307
308 // if this happens to be a boolean field, the value is encoded in the type
309 if type == .boolean_TRUE || type == .boolean_FALSE {
310 // save the boolean value in a special instance variable
311 booleanValue = type == .boolean_TRUE
312 }
313
314 // push the new field onto the field stack so we can keep the deltas going
315 lastFieldId = UInt8(fieldId)
316 return ("", fieldType, Int32(fieldId))
317 }
318
319 public func readFieldEnd() throws { }
320
321 public func read() throws -> String {
322 let length = try readVarint32()
323
324 var result: String
325
326 if length != 0 {
327 let data = try readBinary(Int(length))
328 result = String(data: data, encoding: String.Encoding.utf8) ?? ""
329 } else {
330 result = ""
331 }
332
333 return result
334 }
335
336 public func read() throws -> Bool {
337 if let val = booleanValue {
338 self.booleanValue = nil
339 return val
340 } else {
341 let result = try read() as UInt8
342 return TCType(rawValue: result) == .boolean_TRUE
343 }
344 }
345
346 public func read() throws -> UInt8 {
347 var buff: UInt8 = 0
348 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
349 buff = try self.transport.readAll(size: 1)[0]
350 }
351 return buff
352 }
353
354 public func read() throws -> Int16 {
355 let v = try readVarint32()
356 return Int16(zigZagToi32(v))
357 }
358
359 public func read() throws -> Int32 {
360 let v = try readVarint32()
361 return zigZagToi32(v)
362 }
363
364 public func read() throws -> Int64 {
365 let v = try readVarint64()
366 return zigZagToi64(v)
367 }
368
369 public func read() throws -> Double {
370 var buff = Data()
371 try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
372 buff = try self.transport.readAll(size: 8)
373 }
Alexander Edgeb4711a62020-04-24 14:43:03 +0100374 let i64: UInt64 = buff.withUnsafeBytes { $0.load(as: UInt64.self) }
Chris Simpsona9b6c702018-04-08 07:11:37 -0400375 let bits = CFSwapInt64LittleToHost(i64)
Chris Simpson2566ecd2018-08-29 14:40:44 -0400376 return Double(bitPattern: bits)
Chris Simpsona9b6c702018-04-08 07:11:37 -0400377 }
378
379 public func read() throws -> Data {
380 let length = try readVarint32()
381 return try readBinary(Int(length))
382 }
383
384 public func readMapBegin() throws -> (TType, TType, Int32) {
385 var keyAndValueType: UInt8 = 8
386 let size = try readVarint32()
387 if size != 0 {
388 keyAndValueType = try read()
389 }
390
391 let keyType = try ttype(keyAndValueType >> 4)
392 let valueType = try ttype(keyAndValueType & 0xF)
393
394 return (keyType, valueType, Int32(size))
395 }
396
397 public func readMapEnd() throws { }
398
399 public func readSetBegin() throws -> (TType, Int32) {
400 return try readListBegin()
401 }
402
403 public func readSetEnd() throws { }
404
405 public func readListBegin() throws -> (TType, Int32) {
406 let sizeAndType: UInt8 = try read()
407 var size: UInt32 = UInt32(sizeAndType >> 4) & 0x0f
408 if size == 15 {
409 size = try readVarint32()
410 }
411 let elementType = try ttype(sizeAndType & 0x0F)
412
413 return (elementType, Int32(size))
414 }
415
416 public func readListEnd() throws { }
417
418 public func writeMessageBegin(name: String,
419 type messageType: TMessageType,
420 sequenceID: Int32) throws {
421 try writebyteDirect(TCompactProtocol.protocolID)
422 let nextByte: UInt8 = (TCompactProtocol.version & TCompactProtocol.versionMask) |
423 (UInt8((UInt32(messageType.rawValue) << UInt32(TCType.typeShiftAmount))) &
424 TCType.typeMask)
425 try writebyteDirect(nextByte)
426 try writeVarint32(UInt32(sequenceID))
427 try write(name)
428
429 currentMessageName = name
430 }
431
432 public func writeMessageEnd() throws {
433 currentMessageName = nil
434 }
435
436 public func writeStructBegin(name: String) throws {
437 lastField.append(lastFieldId)
438 lastFieldId = 0
439 }
440
441 public func writeStructEnd() throws {
442 lastFieldId = lastField.last ?? 0
443 lastField.removeLast()
444 }
445
446 public func writeFieldBegin(name: String,
447 type fieldType: TType,
448 fieldID: Int32) throws {
449 if fieldType == .bool {
450 boolFieldName = name
451 boolFieldType = fieldType
452 boolFieldId = fieldID
453 return
454 } else {
455 try writeFieldBeginInternal(name: name,
456 type: fieldType,
457 fieldID: fieldID,
458 typeOverride: 0xFF)
459 }
460 }
461
462 func writeFieldBeginInternal(name: String,
463 type fieldType: TType,
464 fieldID: Int32,
465 typeOverride: UInt8) throws {
466
467 let typeToWrite = typeOverride == 0xFF ? compactType(fieldType).rawValue : typeOverride
468
469 // check if we can use delta encoding for the field id
470 let diff = UInt8(fieldID) - lastFieldId
471 if (UInt8(fieldID) > lastFieldId) && (diff <= 15) {
472 // Write them together
473 try writebyteDirect((UInt8(fieldID) - lastFieldId) << 4 | typeToWrite)
474
475 } else {
476 // Write them separate
477 try writebyteDirect(typeToWrite)
478 try write(Int16(fieldID))
479 }
480
481 lastFieldId = UInt8(fieldID)
Chris Simpsona9b6c702018-04-08 07:11:37 -0400482 }
483
484 public func writeFieldStop() throws {
485 try writebyteDirect(TCType.stop.rawValue)
486 }
487
488 public func writeFieldEnd() throws { }
489
490 public func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws {
491 if size == 0 {
492 try writebyteDirect(0)
493 } else {
494 try writeVarint32(UInt32(size))
495
496 let compactedTypes = compactType(keyType).rawValue << 4 | compactType(valueType).rawValue
497 try writebyteDirect(compactedTypes)
498 }
499 }
500
501 public func writeMapEnd() throws { }
502
503 public func writeSetBegin(elementType: TType, size: Int32) throws {
504 try writeCollectionBegin(elementType, size: size)
505 }
506
507 public func writeSetEnd() throws { }
508
509 public func writeListBegin(elementType: TType, size: Int32) throws {
510 try writeCollectionBegin(elementType, size: size)
511 }
512
513 public func writeListEnd() throws { }
514
515 public func write(_ value: String) throws {
516 try write(value.data(using: String.Encoding.utf8)!)
517 }
518
519 public func write(_ value: Bool) throws {
520 if let boolFieldId = boolFieldId, let boolFieldType = boolFieldType,
521 let boolFieldName = boolFieldName {
522
523 // we haven't written the field header yet
524 let compactType: TCType = value ? .boolean_TRUE : .boolean_FALSE
525 try writeFieldBeginInternal(name: boolFieldName, type: boolFieldType, fieldID: boolFieldId,
526 typeOverride: compactType.rawValue)
527 self.boolFieldId = nil
528 self.boolFieldType = nil
529 self.boolFieldName = nil
530 } else {
531 // we're not part of a field, so just write the value.
532 try writebyteDirect(value ? TCType.boolean_TRUE.rawValue : TCType.boolean_FALSE.rawValue)
533 }
534 }
535
536 public func write(_ value: UInt8) throws {
537 try writebyteDirect(value)
538 }
539
540 public func write(_ value: Int16) throws {
541 try writeVarint32(i32ToZigZag(Int32(value)))
542 }
543
544 public func write(_ value: Int32) throws {
545 try writeVarint32(i32ToZigZag(value))
546 }
547
548 public func write(_ value: Int64) throws {
549 try writeVarint64(i64ToZigZag(value))
550 }
551
552 public func write(_ value: Double) throws {
Chris Simpson2566ecd2018-08-29 14:40:44 -0400553 var bits = CFSwapInt64HostToLittle(value.bitPattern)
Chris Simpsona9b6c702018-04-08 07:11:37 -0400554 let data = withUnsafePointer(to: &bits) {
555 return Data(bytes: UnsafePointer<UInt8>(OpaquePointer($0)), count: MemoryLayout<UInt64>.size)
556 }
557 try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
558 try self.transport.write(data: data)
559 }
560 }
561
562 public func write(_ data: Data) throws {
563 try writeVarint32(UInt32(data.count))
564 try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) {
565 try self.transport.write(data: data)
566 }
567 }
568}