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