-
Notifications
You must be signed in to change notification settings - Fork 16.1k
Expand file tree
/
Copy pathgenerated_enum_util.cc
More file actions
223 lines (195 loc) · 7.46 KB
/
generated_enum_util.cc
File metadata and controls
223 lines (195 loc) · 7.46 KB
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "google/protobuf/generated_enum_util.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <vector>
#include "absl/log/absl_check.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "google/protobuf/generated_message_util.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace internal {
namespace {
bool EnumCompareByName(const EnumEntry& a, const EnumEntry& b) {
return absl::string_view(a.name) < absl::string_view(b.name);
}
// Gets the numeric value of the EnumEntry at the given index, but returns a
// special value for the index -1. This gives a way to use std::lower_bound on a
// sorted array of indices while searching for value that we associate with -1.
int GetValue(const EnumEntry* enums, int i, int target) {
if (i == -1) {
return target;
} else {
return enums[i].value;
}
}
} // namespace
bool LookUpEnumValue(const EnumEntry* enums, size_t size,
absl::string_view name, int* value) {
EnumEntry target{name, 0};
auto it = std::lower_bound(enums, enums + size, target, EnumCompareByName);
if (it != enums + size && it->name == name) {
*value = it->value;
return true;
}
return false;
}
int LookUpEnumName(const EnumEntry* enums, const int* sorted_indices,
size_t size, int value) {
auto comparator = [enums, value](int a, int b) {
return GetValue(enums, a, value) < GetValue(enums, b, value);
};
auto it =
std::lower_bound(sorted_indices, sorted_indices + size, -1, comparator);
if (it != sorted_indices + size && enums[*it].value == value) {
return it - sorted_indices;
}
return -1;
}
bool InitializeEnumStrings(
const EnumEntry* enums, const int* sorted_indices, size_t size,
internal::ExplicitlyConstructed<std::string>* enum_strings) {
for (size_t i = 0; i < size; ++i) {
enum_strings[i].Construct(enums[sorted_indices[i]].name);
internal::OnShutdownDestroyString(enum_strings[i].get_mutable());
}
return true;
}
bool ValidateEnum(int value, const uint32_t* data) {
return ValidateEnumInlined(value, data);
}
struct EytzingerLayoutSorter {
absl::Span<const int32_t> input;
absl::Span<uint32_t> output;
size_t i;
// This is recursive, but the maximum depth is log(N), so it should be safe.
void Sort(size_t output_index = 0) {
if (output_index < input.size()) {
Sort(2 * output_index + 1);
output[output_index] = input[i++];
Sort(2 * output_index + 2);
}
}
};
std::vector<uint32_t> GenerateEnumData(absl::Span<const int32_t> values) {
const auto sorted_and_unique = [&] {
for (size_t i = 0; i + 1 < values.size(); ++i) {
if (values[i] >= values[i + 1]) return false;
}
return true;
};
ABSL_DCHECK(sorted_and_unique());
std::vector<int32_t> fallback_values_too_large, fallback_values_after_bitmap;
std::vector<uint32_t> bitmap_values;
constexpr size_t kBitmapBlockSize = 32;
absl::optional<int16_t> start_sequence;
uint32_t sequence_length = 0;
for (int32_t v : values) {
// If we don't yet have a sequence, start it.
if (!start_sequence.has_value()) {
// But only if we can fit it in the sequence.
if (static_cast<int16_t>(v) != v) {
fallback_values_too_large.push_back(v);
continue;
}
start_sequence = v;
sequence_length = 1;
continue;
}
// If we can extend the sequence, do so.
if (v == static_cast<int32_t>(*start_sequence) +
static_cast<int32_t>(sequence_length) &&
sequence_length < 0xFFFF) {
++sequence_length;
continue;
}
// We adjust the bitmap values to be relative to the end of the sequence.
const auto adjust = [&](int32_t v) -> uint32_t {
// Cast to int64_t first to avoid overflow. The result is guaranteed to be
// positive and fit in uint32_t.
int64_t a = static_cast<int64_t>(v) - *start_sequence - sequence_length;
ABSL_DCHECK(a >= 0);
ABSL_DCHECK_EQ(a, static_cast<uint32_t>(a));
return a;
};
const uint32_t adjusted = adjust(v);
const auto add_bit = [&](uint32_t bit) {
bitmap_values[bit / kBitmapBlockSize] |= uint32_t{1}
<< (bit % kBitmapBlockSize);
};
// If we can fit it on the already allocated bitmap, do so.
if (adjusted < kBitmapBlockSize * bitmap_values.size()) {
// We can fit it in the existing bitmap.
ABSL_DCHECK_EQ(fallback_values_after_bitmap.size(), 0);
add_bit(adjusted);
continue;
}
// We can't fit in the sequence and we can't fit in the current bitmap.
// Evaluate if it is better to add to fallback, or to collapse all the
// fallback values after the bitmap into the bitmap.
const size_t cost_if_fallback =
bitmap_values.size() + (1 + fallback_values_after_bitmap.size());
const size_t rounded_bitmap_size =
(adjusted + kBitmapBlockSize) / kBitmapBlockSize;
const size_t cost_if_collapse = rounded_bitmap_size;
if (cost_if_collapse <= cost_if_fallback &&
kBitmapBlockSize * rounded_bitmap_size < 0x10000) {
// Collapse the existing values, and add the new one.
ABSL_DCHECK_GT(rounded_bitmap_size, bitmap_values.size());
bitmap_values.resize(rounded_bitmap_size);
for (int32_t to_collapse : fallback_values_after_bitmap) {
add_bit(adjust(to_collapse));
}
fallback_values_after_bitmap.clear();
add_bit(adjusted);
} else {
fallback_values_after_bitmap.push_back(v);
}
}
std::vector<int32_t> fallback_values;
if (fallback_values_after_bitmap.empty()) {
fallback_values = std::move(fallback_values_too_large);
} else if (fallback_values_too_large.empty()) {
fallback_values = std::move(fallback_values_after_bitmap);
} else {
fallback_values.resize(fallback_values_too_large.size() +
fallback_values_after_bitmap.size());
std::merge(fallback_values_too_large.begin(),
fallback_values_too_large.end(),
fallback_values_after_bitmap.begin(),
fallback_values_after_bitmap.end(), &fallback_values[0]);
}
std::vector<uint32_t> output(
2 /* seq start + seq len + bitmap len + ordered len */ +
bitmap_values.size() + fallback_values.size());
uint32_t* p = output.data();
ABSL_DCHECK_EQ(sequence_length, static_cast<uint16_t>(sequence_length));
*p++ = uint32_t{static_cast<uint16_t>(start_sequence.value_or(0))} |
(uint32_t{sequence_length} << 16);
ABSL_DCHECK_EQ(
kBitmapBlockSize * bitmap_values.size(),
static_cast<uint16_t>(kBitmapBlockSize * bitmap_values.size()));
ABSL_DCHECK_EQ(fallback_values.size(),
static_cast<uint16_t>(fallback_values.size()));
*p++ = static_cast<uint32_t>(kBitmapBlockSize * bitmap_values.size()) |
static_cast<uint32_t>(fallback_values.size() << 16);
p = std::copy(bitmap_values.begin(), bitmap_values.end(), p);
EytzingerLayoutSorter{fallback_values,
absl::MakeSpan(p, fallback_values.size())}
.Sort();
return output;
}
} // namespace internal
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"