ProtobufRawMessage.swift 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // ProtobufRuntime/Sources/Protobuf/ProtobufRawMessage.swift - Raw message decoding
  2. //
  3. // This source file is part of the Swift.org open source project
  4. //
  5. // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
  6. // Licensed under Apache License v2.0 with Runtime Library Exception
  7. //
  8. // See http://swift.org/LICENSE.txt for license information
  9. // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
  10. //
  11. // -----------------------------------------------------------------------------
  12. ///
  13. /// A "RawMessage" is a tool for parsing proto binary messages without
  14. /// using a schema of any sort. This is slow, inconvenient, and unsafe.
  15. /// Despite these drawbacks, it is occasionally quite useful...
  16. ///
  17. // -----------------------------------------------------------------------------
  18. import Swift
  19. // TODO: This is a tentative sketch; needs tests and plenty of more stuff filled in.
  20. public struct ProtobufRawMessage {
  21. public private(set) var fieldWireType = [Int: Int]()
  22. public private(set) var fieldData = [Int: Any]()
  23. public init(protobuf: [UInt8]) throws {
  24. try protobuf.withUnsafeBufferPointer { (bp) throws in
  25. var protobufDecoder = ProtobufBinaryDecoder(protobufPointer: bp)
  26. while let tagType = try protobufDecoder.getTagType() {
  27. let protoFieldNumber = tagType / 8
  28. let wireType = tagType % 8
  29. fieldWireType[protoFieldNumber] = wireType
  30. switch wireType {
  31. case 0:
  32. if let v = try protobufDecoder.decodeUInt64() {
  33. fieldData[protoFieldNumber] = v
  34. } else {
  35. throw ProtobufDecodingError.malformedProtobuf
  36. }
  37. case 1:
  38. if let v = try protobufDecoder.decodeFixed64() {
  39. fieldData[protoFieldNumber] = v
  40. } else {
  41. throw ProtobufDecodingError.malformedProtobuf
  42. }
  43. case 2:
  44. if let v = try protobufDecoder.decodeBytes() {
  45. fieldData[protoFieldNumber] = v
  46. } else {
  47. throw ProtobufDecodingError.malformedProtobuf
  48. }
  49. case 3:
  50. // TODO: Find a useful way to deal with groups
  51. try protobufDecoder.skip()
  52. case 5:
  53. if let v = try protobufDecoder.decodeFixed32() {
  54. fieldData[protoFieldNumber] = v
  55. } else {
  56. throw ProtobufDecodingError.malformedProtobuf
  57. }
  58. default:
  59. throw ProtobufDecodingError.malformedProtobuf
  60. }
  61. }
  62. }
  63. }
  64. // TODO: serializeProtobuf()
  65. /// Get the contents of this field as a UInt64
  66. /// Returns nil if the field doesn't exist or it's contents cannot be expressed as a UInt64
  67. public func getUInt64(protoFieldNumber: Int) -> UInt64? {
  68. return fieldData[protoFieldNumber] as? UInt64
  69. }
  70. public func getString(protoFieldNumber: Int) -> String? {
  71. if let bytes = fieldData[protoFieldNumber] as? [UInt8] {
  72. return bytes.withUnsafeBufferPointer {(buffer) -> String? in
  73. buffer.baseAddress?.withMemoryRebound(to: CChar.self, capacity: buffer.count) { (cp) -> String? in
  74. // cp is not null-terminated!
  75. var chars = [CChar](UnsafeBufferPointer<CChar>(start: cp, count: buffer.count))
  76. chars.append(0)
  77. return String(validatingUTF8: chars)
  78. }
  79. }
  80. } else {
  81. return nil
  82. }
  83. }
  84. // TODO: More getters...
  85. // TODO: Setters...
  86. }