aboutsummaryrefslogtreecommitdiff
path: root/pw_protobuf/public/pw_protobuf/serialized_size.h
blob: 8a80a62dc101013622e7f4990e65ca4d5a341deb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once

#include <cstdint>

#include "pw_protobuf/wire_format.h"
#include "pw_varint/varint.h"

namespace pw::protobuf {

// Field types that directly map to fixed wire types:
inline constexpr size_t kMaxSizeBytesFixed32 = 4;
inline constexpr size_t kMaxSizeBytesFixed64 = 8;
inline constexpr size_t kMaxSizeBytesSfixed32 = 4;
inline constexpr size_t kMaxSizeBytesSfixed64 = 8;
inline constexpr size_t kMaxSizeBytesFloat = kMaxSizeBytesFixed32;
inline constexpr size_t kMaxSizeBytesDouble = kMaxSizeBytesFixed64;

// Field types that map to varint:
inline constexpr size_t kMaxSizeBytesUint32 = varint::kMaxVarint32SizeBytes;
inline constexpr size_t kMaxSizeBytesUint64 = varint::kMaxVarint64SizeBytes;
inline constexpr size_t kMaxSizeBytesSint32 = varint::kMaxVarint32SizeBytes;
inline constexpr size_t kMaxSizeBytesSint64 = varint::kMaxVarint64SizeBytes;
// The int32 field type does not use zigzag encoding, ergo negative values
// can result in the worst case varint size.
inline constexpr size_t kMaxSizeBytesInt32 = varint::kMaxVarint64SizeBytes;
inline constexpr size_t kMaxSizeBytesInt64 = varint::kMaxVarint64SizeBytes;
// The bool field type is backed by a varint, but has a limited value range.
inline constexpr size_t kMaxSizeBytesBool = 1;

inline constexpr size_t kMaxSizeBytesEnum = kMaxSizeBytesInt32;

inline constexpr size_t kMaxSizeOfFieldNumber = varint::kMaxVarint32SizeBytes;

inline constexpr size_t kMaxSizeOfLength = varint::kMaxVarint32SizeBytes;

// Calculate the serialized size of a proto tag (field number + wire type).
//
// Args:
//   field_number: The field number for the field.
//
// Returns:
//   The size of the field's encoded tag.
//
// Precondition: The field_number must be a ValidFieldNumber.
template <typename T>
constexpr size_t TagSizeBytes(T field_number) {
  static_assert((std::is_enum<T>() || std::is_integral<T>()) &&
                    sizeof(T) <= sizeof(uint32_t),
                "Field numbers must be 32-bit enums or integers");
  // The wiretype does not impact the serialized size, so use kVarint (0), which
  // will be optimized out by the compiler.
  return varint::EncodedSize(
      FieldKey(static_cast<uint32_t>(field_number), WireType::kVarint));
}

// Calculates the size of a varint field (uint32/64, int32/64, sint32/64, enum).
template <typename T, typename U>
constexpr size_t SizeOfVarintField(T field_number, U value) {
  return TagSizeBytes(field_number) +
         varint::EncodedSize(static_cast<uint64_t>(value));
}

// Calculates the size of a delimited field (string, bytes, nested message,
// packed repeated), excluding the data itself. This accounts for the field
// tag and length varint only. The default value for length_bytes assumes
// the length is kMaxSizeOfLength bytes long.
template <typename T>
constexpr size_t SizeOfDelimitedFieldWithoutValue(
    T field_number,
    uint32_t length_bytes = std::numeric_limits<uint32_t>::max()) {
  return TagSizeBytes(field_number) + varint::EncodedSize(length_bytes);
}

// Calculates the total size of a delimited field (string, bytes, nested
// message, packed repeated), including the data itself.
template <typename T>
constexpr size_t SizeOfDelimitedField(T field_number, uint32_t length_bytes) {
  return SizeOfDelimitedFieldWithoutValue(field_number, length_bytes) +
         length_bytes;
}

// Calculates the size of a proto field in the wire format. This is the size of
// a final serialized protobuf entry, including the key (field number + wire
// type), encoded payload size (for length-delimited types), and data.
//
// Args:
//   field_number: The field number for the field.
//   type: The wire type of the field
//   data_size: The size of the payload.
//
// Returns:
//   The size of the field.
//
// Precondition: The field_number must be a ValidFieldNumber.
// Precondition: `data_size_bytes` must be smaller than
//   std::numeric_limits<uint32_t>::max()
template <typename T>
constexpr size_t SizeOfField(T field_number,
                             WireType type,
                             size_t data_size_bytes) {
  if (type == WireType::kDelimited) {
    PW_DASSERT(data_size_bytes <= std::numeric_limits<uint32_t>::max());
    return SizeOfDelimitedField(field_number,
                                static_cast<uint32_t>(data_size_bytes));
  }
  return TagSizeBytes(field_number) + data_size_bytes;
}

// Functions for calculating the size of each type of protobuf field. Varint
// fields (int32, uint64, etc.) accept a value argument that defaults to the
// largest-to-encode value for the type.
template <typename T>
constexpr size_t SizeOfFieldFloat(T field_number) {
  return TagSizeBytes(field_number) + sizeof(float);
}
template <typename T>
constexpr size_t SizeOfFieldDouble(T field_number) {
  return TagSizeBytes(field_number) + sizeof(double);
}
template <typename T>
constexpr size_t SizeOfFieldInt32(T field_number, int32_t value = -1) {
  return SizeOfVarintField(field_number, value);
}
template <typename T>
constexpr size_t SizeOfFieldInt64(T field_number, int64_t value = -1) {
  return SizeOfVarintField(field_number, value);
}
template <typename T>
constexpr size_t SizeOfFieldSint32(
    T field_number, int32_t value = std::numeric_limits<int32_t>::min()) {
  return SizeOfVarintField(field_number, varint::ZigZagEncode(value));
}
template <typename T>
constexpr size_t SizeOfFieldSint64(
    T field_number, int64_t value = std::numeric_limits<int64_t>::min()) {
  return SizeOfVarintField(field_number, varint::ZigZagEncode(value));
}
template <typename T>
constexpr size_t SizeOfFieldUint32(
    T field_number, uint32_t value = std::numeric_limits<uint32_t>::max()) {
  return SizeOfVarintField(field_number, value);
}
template <typename T>
constexpr size_t SizeOfFieldUint64(
    T field_number, uint64_t value = std::numeric_limits<uint64_t>::max()) {
  return SizeOfVarintField(field_number, value);
}
template <typename T>
constexpr size_t SizeOfFieldFixed32(T field_number) {
  return TagSizeBytes(field_number) + sizeof(uint32_t);
}
template <typename T>
constexpr size_t SizeOfFieldFixed64(T field_number) {
  return TagSizeBytes(field_number) + sizeof(uint64_t);
}
template <typename T>
constexpr size_t SizeOfFieldSfixed32(T field_number) {
  return TagSizeBytes(field_number) + sizeof(uint32_t);
}
template <typename T>
constexpr size_t SizeOfFieldSfixed64(T field_number) {
  return TagSizeBytes(field_number) + sizeof(uint64_t);
}
template <typename T>
constexpr size_t SizeOfFieldBool(T field_number) {
  return TagSizeBytes(field_number) + 1;
}
template <typename T>
constexpr size_t SizeOfFieldString(T field_number, uint32_t length_bytes) {
  return SizeOfDelimitedField(field_number, length_bytes);
}
template <typename T>
constexpr size_t SizeOfFieldBytes(T field_number, uint32_t length_bytes) {
  return SizeOfDelimitedField(field_number, length_bytes);
}
template <typename T, typename U = int32_t>
constexpr size_t SizeOfFieldEnum(T field_number, U value = static_cast<U>(-1)) {
  static_assert((std::is_enum<U>() || std::is_integral<U>()) &&
                    sizeof(U) <= sizeof(uint32_t),
                "Enum values must be 32-bit enums or integers");
  return SizeOfFieldInt32(field_number, static_cast<int32_t>(value));
}

}  // namespace pw::protobuf