1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 #include <cstdint>
17 #include <limits>
18
19 #include "pw_assert/assert.h"
20
21 namespace pw::protobuf {
22
23 // Per the protobuf specification, valid field numbers range between 1 and
24 // 2**29 - 1, inclusive. The numbers 19000-19999 are reserved for internal
25 // use.
26 constexpr static uint32_t kMaxFieldNumber = (1u << 29) - 1;
27 constexpr static uint32_t kFirstReservedNumber = 19000;
28 constexpr static uint32_t kLastReservedNumber = 19999;
29
ValidFieldNumber(uint32_t field_number)30 constexpr bool ValidFieldNumber(uint32_t field_number) {
31 return field_number != 0 && field_number <= kMaxFieldNumber &&
32 !(field_number >= kFirstReservedNumber &&
33 field_number <= kLastReservedNumber);
34 }
35
ValidFieldNumber(uint64_t field_number)36 constexpr bool ValidFieldNumber(uint64_t field_number) {
37 if (field_number > std::numeric_limits<uint32_t>::max()) {
38 return false;
39 }
40 return ValidFieldNumber(static_cast<uint32_t>(field_number));
41 }
42
43 enum class WireType {
44 kVarint = 0,
45 kFixed64 = 1,
46 kDelimited = 2,
47 // Wire types 3 and 4 are deprecated per the protobuf specification.
48 kFixed32 = 5,
49 };
50
51 // Represents a protobuf field key, storing a field number and wire type.
52 class FieldKey {
53 public:
54 // Checks if the given encoded protobuf key is valid. Must be called before
55 // instantiating a FieldKey object with it.
IsValidKey(uint64_t key)56 static constexpr bool IsValidKey(uint64_t key) {
57 uint64_t field_number = key >> kFieldNumberShift;
58 uint32_t wire_type = key & kWireTypeMask;
59
60 return ValidFieldNumber(field_number) && (wire_type <= 2 || wire_type == 5);
61 }
62
63 // Creates a field key with the given field number and type.
64 //
65 // Precondition: The field number is valid.
FieldKey(uint32_t field_number,WireType wire_type)66 constexpr FieldKey(uint32_t field_number, WireType wire_type)
67 : key_(field_number << kFieldNumberShift |
68 static_cast<uint32_t>(wire_type)) {
69 PW_DASSERT(ValidFieldNumber(field_number));
70 }
71
72 // Parses a field key from its encoded representation.
73 //
74 // Precondition: The field number is valid. Call IsValidKey(key) first.
FieldKey(uint32_t key)75 constexpr FieldKey(uint32_t key) : key_(key) {
76 PW_DASSERT(ValidFieldNumber(field_number()));
77 }
78
uint32_t()79 constexpr operator uint32_t() { return key_; }
80
field_number()81 constexpr uint32_t field_number() const { return key_ >> kFieldNumberShift; }
wire_type()82 constexpr WireType wire_type() const {
83 return static_cast<WireType>(key_ & kWireTypeMask);
84 }
85
86 private:
87 static constexpr unsigned int kFieldNumberShift = 3u;
88 static constexpr unsigned int kWireTypeMask = (1u << kFieldNumberShift) - 1u;
89
90 uint32_t key_;
91 };
92
MakeKey(uint32_t field_number,WireType wire_type)93 [[deprecated("Use the FieldKey class")]] constexpr uint32_t MakeKey(
94 uint32_t field_number, WireType wire_type) {
95 return FieldKey(field_number, wire_type);
96 }
97
98 } // namespace pw::protobuf
99