Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 1 | /* |
| 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 | |
| 20 | import Foundation |
| 21 | import CoreFoundation |
| 22 | |
| 23 | public 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 | |
| 45 | public 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œur | 08a6eb6 | 2019-07-08 18:42:09 +0800 | [diff] [blame] | 70 | let byte = Data([byte]) |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 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")) { |
Antoine Cœur | 08a6eb6 | 2019-07-08 18:42:09 +0800 | [diff] [blame] | 93 | try self.transport.write(data: Data(i32buf[0..<idx])) |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 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")) { |
Antoine Cœur | 08a6eb6 | 2019-07-08 18:42:09 +0800 | [diff] [blame] | 114 | try self.transport.write(data: Data(varint64out[0..<idx])) |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 115 | } |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 116 | } |
| 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 Simpson | 2566ecd | 2018-08-29 14:40:44 -0400 | [diff] [blame] | 224 | return UInt32(bitPattern: Int32(n << 1) ^ Int32(n >> 31)) |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 225 | } |
| 226 | |
| 227 | func i64ToZigZag(_ n : Int64) -> UInt64 { |
Chris Simpson | 2566ecd | 2018-08-29 14:40:44 -0400 | [diff] [blame] | 228 | return UInt64(bitPattern: Int64(n << 1) ^ Int64(n >> 63)) |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 229 | } |
| 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 Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 251 | } |
| 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 Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 258 | } |
| 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 Edge | b4711a6 | 2020-04-24 14:43:03 +0100 | [diff] [blame^] | 374 | let i64: UInt64 = buff.withUnsafeBytes { $0.load(as: UInt64.self) } |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 375 | let bits = CFSwapInt64LittleToHost(i64) |
Chris Simpson | 2566ecd | 2018-08-29 14:40:44 -0400 | [diff] [blame] | 376 | return Double(bitPattern: bits) |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 377 | } |
| 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 Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 482 | } |
| 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 Simpson | 2566ecd | 2018-08-29 14:40:44 -0400 | [diff] [blame] | 553 | var bits = CFSwapInt64HostToLittle(value.bitPattern) |
Chris Simpson | a9b6c70 | 2018-04-08 07:11:37 -0400 | [diff] [blame] | 554 | 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 | } |