| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- // Sources/SwiftProtobuf/TextFormatEncoder.swift - Text format encoding support
- //
- // Copyright (c) 2014 - 2019 Apple Inc. and the project authors
- // Licensed under Apache License v2.0 with Runtime Library Exception
- //
- // See LICENSE.txt for license information:
- // https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
- //
- // -----------------------------------------------------------------------------
- ///
- /// Text format serialization engine.
- ///
- // -----------------------------------------------------------------------------
- import Foundation
- private let asciiSpace = UInt8(ascii: " ")
- private let asciiColon = UInt8(ascii: ":")
- private let asciiComma = UInt8(ascii: ",")
- private let asciiMinus = UInt8(ascii: "-")
- private let asciiBackslash = UInt8(ascii: "\\")
- private let asciiDoubleQuote = UInt8(ascii: "\"")
- private let asciiZero = UInt8(ascii: "0")
- private let asciiOpenCurlyBracket = UInt8(ascii: "{")
- private let asciiCloseCurlyBracket = UInt8(ascii: "}")
- private let asciiOpenSquareBracket = UInt8(ascii: "[")
- private let asciiCloseSquareBracket = UInt8(ascii: "]")
- private let asciiNewline = UInt8(ascii: "\n")
- private let asciiUpperA = UInt8(ascii: "A")
- private let tabSize = 2
- private let tab = [UInt8](repeating: asciiSpace, count: tabSize)
- /// TextFormatEncoder has no public members.
- internal struct TextFormatEncoder {
- private var data = [UInt8]()
- private var indentString: [UInt8] = []
- var stringResult: String {
- get {
- String(decoding: data, as: UTF8.self)
- }
- }
- internal mutating func append(staticText: StaticString) {
- let buff = UnsafeBufferPointer(start: staticText.utf8Start, count: staticText.utf8CodeUnitCount)
- data.append(contentsOf: buff)
- }
- internal mutating func append(name: _NameMap.Name) {
- data.append(contentsOf: name.utf8Buffer)
- }
- internal mutating func append(bytes: [UInt8]) {
- data.append(contentsOf: bytes)
- }
- private mutating func append(text: String) {
- data.append(contentsOf: text.utf8)
- }
- init() {}
- internal mutating func indent() {
- data.append(contentsOf: indentString)
- }
- mutating func emitFieldName(name: UnsafeRawBufferPointer) {
- indent()
- data.append(contentsOf: name)
- }
- mutating func emitFieldName(name: StaticString) {
- let buff = UnsafeRawBufferPointer(start: name.utf8Start, count: name.utf8CodeUnitCount)
- emitFieldName(name: buff)
- }
- mutating func emitFieldName(name: [UInt8]) {
- indent()
- data.append(contentsOf: name)
- }
- mutating func emitExtensionFieldName(name: String) {
- indent()
- data.append(asciiOpenSquareBracket)
- append(text: name)
- data.append(asciiCloseSquareBracket)
- }
- mutating func emitFieldNumber(number: Int) {
- indent()
- appendUInt(value: UInt64(number))
- }
- mutating func startRegularField() {
- append(staticText: ": ")
- }
- mutating func endRegularField() {
- data.append(asciiNewline)
- }
- // In Text format, a message-valued field writes the name
- // without a trailing colon:
- // name_of_field {key: value key2: value2}
- mutating func startMessageField() {
- append(staticText: " {\n")
- indentString.append(contentsOf: tab)
- }
- mutating func endMessageField() {
- indentString.removeLast(tabSize)
- indent()
- append(staticText: "}\n")
- }
- mutating func startArray() {
- data.append(asciiOpenSquareBracket)
- }
- mutating func arraySeparator() {
- append(staticText: ", ")
- }
- mutating func endArray() {
- data.append(asciiCloseSquareBracket)
- }
- mutating func putEnumValue<E: Enum>(value: E) {
- if let name = value.name {
- data.append(contentsOf: name.utf8Buffer)
- } else {
- appendInt(value: Int64(value.rawValue))
- }
- }
- mutating func putFloatValue(value: Float) {
- if value.isNaN {
- append(staticText: "nan")
- } else if !value.isFinite {
- if value < 0 {
- append(staticText: "-inf")
- } else {
- append(staticText: "inf")
- }
- } else {
- data.append(contentsOf: value.debugDescription.utf8)
- }
- }
- mutating func putDoubleValue(value: Double) {
- if value.isNaN {
- append(staticText: "nan")
- } else if !value.isFinite {
- if value < 0 {
- append(staticText: "-inf")
- } else {
- append(staticText: "inf")
- }
- } else {
- data.append(contentsOf: value.debugDescription.utf8)
- }
- }
- private mutating func appendUInt(value: UInt64) {
- if value >= 1000 {
- appendUInt(value: value / 1000)
- }
- if value >= 100 {
- data.append(asciiZero + UInt8((value / 100) % 10))
- }
- if value >= 10 {
- data.append(asciiZero + UInt8((value / 10) % 10))
- }
- data.append(asciiZero + UInt8(value % 10))
- }
- private mutating func appendInt(value: Int64) {
- if value < 0 {
- data.append(asciiMinus)
- // This is the twos-complement negation of value,
- // computed in a way that won't overflow a 64-bit
- // signed integer.
- appendUInt(value: 1 + ~UInt64(bitPattern: value))
- } else {
- appendUInt(value: UInt64(bitPattern: value))
- }
- }
- mutating func putInt64(value: Int64) {
- appendInt(value: value)
- }
- mutating func putUInt64(value: UInt64) {
- appendUInt(value: value)
- }
- mutating func appendUIntHex(value: UInt64, digits: Int) {
- if digits == 0 {
- append(staticText: "0x")
- } else {
- appendUIntHex(value: value >> 4, digits: digits - 1)
- let d = UInt8(truncatingIfNeeded: value % 16)
- data.append(d < 10 ? asciiZero + d : asciiUpperA + d - 10)
- }
- }
- mutating func putUInt64Hex(value: UInt64, digits: Int) {
- appendUIntHex(value: value, digits: digits)
- }
- mutating func putBoolValue(value: Bool) {
- append(staticText: value ? "true" : "false")
- }
- mutating func putStringValue(value: String) {
- data.append(asciiDoubleQuote)
- for c in value.unicodeScalars {
- switch c.value {
- // Special two-byte escapes
- case 8:
- append(staticText: "\\b")
- case 9:
- append(staticText: "\\t")
- case 10:
- append(staticText: "\\n")
- case 11:
- append(staticText: "\\v")
- case 12:
- append(staticText: "\\f")
- case 13:
- append(staticText: "\\r")
- case 34:
- append(staticText: "\\\"")
- case 92:
- append(staticText: "\\\\")
- case 0...31, 127: // Octal form for C0 control chars
- data.append(asciiBackslash)
- data.append(asciiZero + UInt8(c.value / 64))
- data.append(asciiZero + UInt8(c.value / 8 % 8))
- data.append(asciiZero + UInt8(c.value % 8))
- case 0...127: // ASCII
- data.append(UInt8(truncatingIfNeeded: c.value))
- case 0x80...0x7ff:
- data.append(0xc0 + UInt8(c.value / 64))
- data.append(0x80 + UInt8(c.value % 64))
- case 0x800...0xffff:
- data.append(0xe0 + UInt8(truncatingIfNeeded: c.value >> 12))
- data.append(0x80 + UInt8(truncatingIfNeeded: (c.value >> 6) & 0x3f))
- data.append(0x80 + UInt8(truncatingIfNeeded: c.value & 0x3f))
- default:
- data.append(0xf0 + UInt8(truncatingIfNeeded: c.value >> 18))
- data.append(0x80 + UInt8(truncatingIfNeeded: (c.value >> 12) & 0x3f))
- data.append(0x80 + UInt8(truncatingIfNeeded: (c.value >> 6) & 0x3f))
- data.append(0x80 + UInt8(truncatingIfNeeded: c.value & 0x3f))
- }
- }
- data.append(asciiDoubleQuote)
- }
- mutating func putBytesValue(value: Data) {
- data.append(asciiDoubleQuote)
- value.withUnsafeBytes { (body: UnsafeRawBufferPointer) in
- if let p = body.baseAddress, body.count > 0 {
- for i in 0..<body.count {
- let c = p[i]
- switch c {
- // Special two-byte escapes
- case 8:
- append(staticText: "\\b")
- case 9:
- append(staticText: "\\t")
- case 10:
- append(staticText: "\\n")
- case 11:
- append(staticText: "\\v")
- case 12:
- append(staticText: "\\f")
- case 13:
- append(staticText: "\\r")
- case 34:
- append(staticText: "\\\"")
- case 92:
- append(staticText: "\\\\")
- case 32...126: // printable ASCII
- data.append(c)
- default: // Octal form for non-printable chars
- data.append(asciiBackslash)
- data.append(asciiZero + UInt8(c / 64))
- data.append(asciiZero + UInt8(c / 8 % 8))
- data.append(asciiZero + UInt8(c % 8))
- }
- }
- }
- }
- data.append(asciiDoubleQuote)
- }
- }
|