| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- // Sources/SwiftProtobuf/BinaryEncodingSizeVisitor.swift - Binary size calculation support
- //
- // Copyright (c) 2014 - 2016 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
- //
- // -----------------------------------------------------------------------------
- ///
- /// Visitor used during binary encoding that precalcuates the size of a
- /// serialized message.
- ///
- // -----------------------------------------------------------------------------
- import Foundation
- /// Visitor that calculates the binary-encoded size of a message so that a
- /// properly sized `Data` or `UInt8` array can be pre-allocated before
- /// serialization.
- internal struct BinaryEncodingSizeVisitor: Visitor {
- /// Accumulates the required size of the message during traversal.
- var serializedSize: Int = 0
- init() {}
- mutating func visitUnknown(bytes: Data) throws {
- serializedSize += bytes.count
- }
- mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
- serializedSize += tagSize + MemoryLayout<Float>.size
- }
- mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
- serializedSize += tagSize + MemoryLayout<Double>.size
- }
- mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws {
- try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber)
- }
- mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- serializedSize += tagSize + Varint.encodedSize(of: value)
- }
- mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws {
- try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber)
- }
- mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- serializedSize += tagSize + Varint.encodedSize(of: value)
- }
- mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value))
- }
- mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value))
- }
- mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
- serializedSize += tagSize + MemoryLayout<UInt32>.size
- }
- mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
- serializedSize += tagSize + MemoryLayout<UInt64>.size
- }
- mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
- serializedSize += tagSize + MemoryLayout<Int32>.size
- }
- mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
- serializedSize += tagSize + MemoryLayout<Int64>.size
- }
- mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- serializedSize += tagSize + 1
- }
- mutating func visitSingularStringField(value: String, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let count = value.utf8.count
- serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count
- }
- mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws {
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let count = value.count
- serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count
- }
- // The default impls for visitRepeated*Field would work, but by implementing
- // these directly, the calculation for the tag overhead can be optimized and
- // the fixed width fields can be simple multiplication.
- mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
- serializedSize += tagSize * value.count + MemoryLayout<Float>.size * value.count
- }
- mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
- serializedSize += tagSize * value.count + MemoryLayout<Double>.size * value.count
- }
- mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
- serializedSize += tagSize * value.count + dataSize
- }
- mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
- serializedSize += tagSize * value.count + dataSize
- }
- mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
- serializedSize += tagSize * value.count + dataSize
- }
- mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
- serializedSize += tagSize * value.count + dataSize
- }
- mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
- serializedSize += tagSize * value.count + dataSize
- }
- mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
- serializedSize += tagSize * value.count + dataSize
- }
- mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
- serializedSize += tagSize * value.count + MemoryLayout<UInt32>.size * value.count
- }
- mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
- serializedSize += tagSize * value.count + MemoryLayout<UInt64>.size * value.count
- }
- mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize
- serializedSize += tagSize * value.count + MemoryLayout<Int32>.size * value.count
- }
- mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize
- serializedSize += tagSize * value.count + MemoryLayout<Int64>.size * value.count
- }
- mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize
- serializedSize += tagSize * value.count + 1 * value.count
- }
- mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.reduce(0) {
- let count = $1.utf8.count
- return $0 + Varint.encodedSize(of: Int64(count)) + count
- }
- serializedSize += tagSize * value.count + dataSize
- }
- mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.reduce(0) {
- let count = $1.count
- return $0 + Varint.encodedSize(of: Int64(count)) + count
- }
- serializedSize += tagSize * value.count + dataSize
- }
- // Packed field handling.
- mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.count * MemoryLayout<Float>.size
- serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.count * MemoryLayout<Double>.size
- serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
- serializedSize +=
- tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
- serializedSize +=
- tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
- serializedSize +=
- tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) }
- serializedSize +=
- tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
- serializedSize +=
- tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) }
- serializedSize +=
- tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.count * MemoryLayout<UInt32>.size
- serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.count * MemoryLayout<UInt64>.size
- serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.count * MemoryLayout<Int32>.size
- serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.count * MemoryLayout<Int64>.size
- serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize
- let dataSize = value.count
- serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitSingularEnumField<E: Enum>(
- value: E,
- fieldNumber: Int
- ) throws {
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .varint
- ).encodedSize
- serializedSize += tagSize
- let dataSize = Varint.encodedSize(of: Int32(truncatingIfNeeded: value.rawValue))
- serializedSize += dataSize
- }
- mutating func visitRepeatedEnumField<E: Enum>(
- value: [E],
- fieldNumber: Int
- ) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .varint
- ).encodedSize
- serializedSize += value.count * tagSize
- let dataSize = value.reduce(0) {
- $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
- }
- serializedSize += dataSize
- }
- mutating func visitPackedEnumField<E: Enum>(
- value: [E],
- fieldNumber: Int
- ) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .varint
- ).encodedSize
- serializedSize += tagSize
- let dataSize = value.reduce(0) {
- $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue))
- }
- serializedSize += Varint.encodedSize(of: Int64(dataSize)) + dataSize
- }
- mutating func visitSingularMessageField<M: Message>(
- value: M,
- fieldNumber: Int
- ) throws {
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .lengthDelimited
- ).encodedSize
- let messageSize = try value.serializedDataSize()
- serializedSize +=
- tagSize + Varint.encodedSize(of: UInt64(messageSize)) + messageSize
- }
- mutating func visitRepeatedMessageField<M: Message>(
- value: [M],
- fieldNumber: Int
- ) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .lengthDelimited
- ).encodedSize
- serializedSize += value.count * tagSize
- let dataSize = try value.reduce(0) {
- let messageSize = try $1.serializedDataSize()
- return $0 + Varint.encodedSize(of: UInt64(messageSize)) + messageSize
- }
- serializedSize += dataSize
- }
- mutating func visitSingularGroupField<G: Message>(value: G, fieldNumber: Int) throws {
- // The wire format doesn't matter here because the encoded size of the
- // integer won't change based on the low three bits.
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .startGroup
- ).encodedSize
- serializedSize += 2 * tagSize
- try value.traverse(visitor: &self)
- }
- mutating func visitRepeatedGroupField<G: Message>(
- value: [G],
- fieldNumber: Int
- ) throws {
- assert(!value.isEmpty)
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .startGroup
- ).encodedSize
- serializedSize += 2 * value.count * tagSize
- for v in value {
- try v.traverse(visitor: &self)
- }
- }
- mutating func visitMapField<KeyType, ValueType: MapValueType>(
- fieldType: _ProtobufMap<KeyType, ValueType>.Type,
- value: _ProtobufMap<KeyType, ValueType>.BaseType,
- fieldNumber: Int
- ) throws {
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .lengthDelimited
- ).encodedSize
- for (k, v) in value {
- var sizer = BinaryEncodingSizeVisitor()
- try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
- try ValueType.visitSingular(value: v, fieldNumber: 2, with: &sizer)
- let entrySize = sizer.serializedSize
- serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
- }
- serializedSize += value.count * tagSize
- }
- mutating func visitMapField<KeyType, ValueType>(
- fieldType: _ProtobufEnumMap<KeyType, ValueType>.Type,
- value: _ProtobufEnumMap<KeyType, ValueType>.BaseType,
- fieldNumber: Int
- ) throws where ValueType.RawValue == Int {
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .lengthDelimited
- ).encodedSize
- for (k, v) in value {
- var sizer = BinaryEncodingSizeVisitor()
- try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
- try sizer.visitSingularEnumField(value: v, fieldNumber: 2)
- let entrySize = sizer.serializedSize
- serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
- }
- serializedSize += value.count * tagSize
- }
- mutating func visitMapField<KeyType, ValueType>(
- fieldType: _ProtobufMessageMap<KeyType, ValueType>.Type,
- value: _ProtobufMessageMap<KeyType, ValueType>.BaseType,
- fieldNumber: Int
- ) throws {
- let tagSize = FieldTag(
- fieldNumber: fieldNumber,
- wireFormat: .lengthDelimited
- ).encodedSize
- for (k, v) in value {
- var sizer = BinaryEncodingSizeVisitor()
- try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer)
- try sizer.visitSingularMessageField(value: v, fieldNumber: 2)
- let entrySize = sizer.serializedSize
- serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize
- }
- serializedSize += value.count * tagSize
- }
- mutating func visitExtensionFieldsAsMessageSet(
- fields: ExtensionFieldValueSet,
- start: Int,
- end: Int
- ) throws {
- var sizer = BinaryEncodingMessageSetSizeVisitor()
- try fields.traverse(visitor: &sizer, start: start, end: end)
- serializedSize += sizer.serializedSize
- }
- }
- extension BinaryEncodingSizeVisitor {
- // Helper Visitor to compute the sizes when writing out the extensions as MessageSets.
- internal struct BinaryEncodingMessageSetSizeVisitor: SelectiveVisitor {
- var serializedSize: Int = 0
- init() {}
- mutating func visitSingularMessageField<M: Message>(value: M, fieldNumber: Int) throws {
- var groupSize = WireFormat.MessageSet.itemTagsEncodedSize
- groupSize += Varint.encodedSize(of: Int32(fieldNumber))
- let messageSize = try value.serializedDataSize()
- groupSize += Varint.encodedSize(of: UInt64(messageSize)) + messageSize
- serializedSize += groupSize
- }
- // SelectiveVisitor handles the rest.
- }
- }
|