1 | // Protocol Buffers - Google's data interchange format |
2 | // Copyright 2008 Google Inc. All rights reserved. |
3 | // https://developers.google.com/protocol-buffers/ |
4 | // |
5 | // Redistribution and use in source and binary forms, with or without |
6 | // modification, are permitted provided that the following conditions are |
7 | // met: |
8 | // |
9 | // * Redistributions of source code must retain the above copyright |
10 | // notice, this list of conditions and the following disclaimer. |
11 | // * Redistributions in binary form must reproduce the above |
12 | // copyright notice, this list of conditions and the following disclaimer |
13 | // in the documentation and/or other materials provided with the |
14 | // distribution. |
15 | // * Neither the name of Google Inc. nor the names of its |
16 | // contributors may be used to endorse or promote products derived from |
17 | // this software without specific prior written permission. |
18 | // |
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | |
31 | #include <google/protobuf/util/internal/utility.h> |
32 | |
33 | #include <algorithm> |
34 | #include <cmath> |
35 | #include <cstdint> |
36 | #include <limits> |
37 | |
38 | #include <google/protobuf/stubs/callback.h> |
39 | #include <google/protobuf/stubs/common.h> |
40 | #include <google/protobuf/stubs/logging.h> |
41 | #include <google/protobuf/wrappers.pb.h> |
42 | #include <google/protobuf/descriptor.pb.h> |
43 | #include <google/protobuf/descriptor.h> |
44 | #include <google/protobuf/stubs/strutil.h> |
45 | #include <google/protobuf/util/internal/constants.h> |
46 | #include <google/protobuf/stubs/map_util.h> |
47 | |
48 | // clang-format off |
49 | #include <google/protobuf/port_def.inc> |
50 | // clang-format on |
51 | |
52 | namespace google { |
53 | namespace protobuf { |
54 | namespace util { |
55 | namespace converter { |
56 | |
57 | bool GetBoolOptionOrDefault( |
58 | const RepeatedPtrField<google::protobuf::Option>& options, |
59 | StringPiece option_name, bool default_value) { |
60 | const google::protobuf::Option* opt = FindOptionOrNull(options, option_name); |
61 | if (opt == nullptr) { |
62 | return default_value; |
63 | } |
64 | return GetBoolFromAny(any: opt->value()); |
65 | } |
66 | |
67 | int64_t GetInt64OptionOrDefault( |
68 | const RepeatedPtrField<google::protobuf::Option>& options, |
69 | StringPiece option_name, int64_t default_value) { |
70 | const google::protobuf::Option* opt = FindOptionOrNull(options, option_name); |
71 | if (opt == nullptr) { |
72 | return default_value; |
73 | } |
74 | return GetInt64FromAny(any: opt->value()); |
75 | } |
76 | |
77 | double GetDoubleOptionOrDefault( |
78 | const RepeatedPtrField<google::protobuf::Option>& options, |
79 | StringPiece option_name, double default_value) { |
80 | const google::protobuf::Option* opt = FindOptionOrNull(options, option_name); |
81 | if (opt == nullptr) { |
82 | return default_value; |
83 | } |
84 | return GetDoubleFromAny(any: opt->value()); |
85 | } |
86 | |
87 | std::string GetStringOptionOrDefault( |
88 | const RepeatedPtrField<google::protobuf::Option>& options, |
89 | StringPiece option_name, StringPiece default_value) { |
90 | const google::protobuf::Option* opt = FindOptionOrNull(options, option_name); |
91 | if (opt == nullptr) { |
92 | return std::string(default_value); |
93 | } |
94 | return GetStringFromAny(any: opt->value()); |
95 | } |
96 | |
97 | template <typename T> |
98 | void ParseFromAny(const std::string& data, T* result) { |
99 | result->ParseFromString(data); |
100 | } |
101 | |
102 | // Returns a boolean value contained in Any type. |
103 | // TODO(skarvaje): Add type checking & error messages here. |
104 | bool GetBoolFromAny(const google::protobuf::Any& any) { |
105 | google::protobuf::BoolValue b; |
106 | ParseFromAny(data: any.value(), result: &b); |
107 | return b.value(); |
108 | } |
109 | |
110 | int64_t GetInt64FromAny(const google::protobuf::Any& any) { |
111 | google::protobuf::Int64Value i; |
112 | ParseFromAny(data: any.value(), result: &i); |
113 | return i.value(); |
114 | } |
115 | |
116 | double GetDoubleFromAny(const google::protobuf::Any& any) { |
117 | google::protobuf::DoubleValue i; |
118 | ParseFromAny(data: any.value(), result: &i); |
119 | return i.value(); |
120 | } |
121 | |
122 | std::string GetStringFromAny(const google::protobuf::Any& any) { |
123 | google::protobuf::StringValue s; |
124 | ParseFromAny(data: any.value(), result: &s); |
125 | return s.value(); |
126 | } |
127 | |
128 | const StringPiece GetTypeWithoutUrl(StringPiece type_url) { |
129 | if (type_url.size() > kTypeUrlSize && type_url[kTypeUrlSize] == '/') { |
130 | return type_url.substr(pos: kTypeUrlSize + 1); |
131 | } else { |
132 | size_t idx = type_url.rfind(c: '/'); |
133 | if (idx != type_url.npos) { |
134 | type_url.remove_prefix(n: idx + 1); |
135 | } |
136 | return type_url; |
137 | } |
138 | } |
139 | |
140 | const std::string GetFullTypeWithUrl(StringPiece simple_type) { |
141 | return StrCat(a: kTypeServiceBaseUrl, b: "/" , c: simple_type); |
142 | } |
143 | |
144 | const google::protobuf::Option* FindOptionOrNull( |
145 | const RepeatedPtrField<google::protobuf::Option>& options, |
146 | StringPiece option_name) { |
147 | for (int i = 0; i < options.size(); ++i) { |
148 | const google::protobuf::Option& opt = options.Get(index: i); |
149 | if (opt.name() == option_name) { |
150 | return &opt; |
151 | } |
152 | } |
153 | return nullptr; |
154 | } |
155 | |
156 | const google::protobuf::Field* FindFieldInTypeOrNull( |
157 | const google::protobuf::Type* type, StringPiece field_name) { |
158 | if (type != nullptr) { |
159 | for (int i = 0; i < type->fields_size(); ++i) { |
160 | const google::protobuf::Field& field = type->fields(index: i); |
161 | if (field.name() == field_name) { |
162 | return &field; |
163 | } |
164 | } |
165 | } |
166 | return nullptr; |
167 | } |
168 | |
169 | const google::protobuf::Field* FindJsonFieldInTypeOrNull( |
170 | const google::protobuf::Type* type, StringPiece json_name) { |
171 | if (type != nullptr) { |
172 | for (int i = 0; i < type->fields_size(); ++i) { |
173 | const google::protobuf::Field& field = type->fields(index: i); |
174 | if (field.json_name() == json_name) { |
175 | return &field; |
176 | } |
177 | } |
178 | } |
179 | return nullptr; |
180 | } |
181 | |
182 | const google::protobuf::Field* FindFieldInTypeByNumberOrNull( |
183 | const google::protobuf::Type* type, int32_t number) { |
184 | if (type != nullptr) { |
185 | for (int i = 0; i < type->fields_size(); ++i) { |
186 | const google::protobuf::Field& field = type->fields(index: i); |
187 | if (field.number() == number) { |
188 | return &field; |
189 | } |
190 | } |
191 | } |
192 | return nullptr; |
193 | } |
194 | |
195 | const google::protobuf::EnumValue* FindEnumValueByNameOrNull( |
196 | const google::protobuf::Enum* enum_type, StringPiece enum_name) { |
197 | if (enum_type != nullptr) { |
198 | for (int i = 0; i < enum_type->enumvalue_size(); ++i) { |
199 | const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(index: i); |
200 | if (enum_value.name() == enum_name) { |
201 | return &enum_value; |
202 | } |
203 | } |
204 | } |
205 | return nullptr; |
206 | } |
207 | |
208 | const google::protobuf::EnumValue* FindEnumValueByNumberOrNull( |
209 | const google::protobuf::Enum* enum_type, int32_t value) { |
210 | if (enum_type != nullptr) { |
211 | for (int i = 0; i < enum_type->enumvalue_size(); ++i) { |
212 | const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(index: i); |
213 | if (enum_value.number() == value) { |
214 | return &enum_value; |
215 | } |
216 | } |
217 | } |
218 | return nullptr; |
219 | } |
220 | |
221 | const google::protobuf::EnumValue* FindEnumValueByNameWithoutUnderscoreOrNull( |
222 | const google::protobuf::Enum* enum_type, StringPiece enum_name) { |
223 | if (enum_type != nullptr) { |
224 | for (int i = 0; i < enum_type->enumvalue_size(); ++i) { |
225 | const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(index: i); |
226 | std::string enum_name_without_underscore = enum_value.name(); |
227 | |
228 | // Remove underscore from the name. |
229 | enum_name_without_underscore.erase( |
230 | first: std::remove(first: enum_name_without_underscore.begin(), |
231 | last: enum_name_without_underscore.end(), value: '_'), |
232 | last: enum_name_without_underscore.end()); |
233 | // Make the name uppercase. |
234 | for (std::string::iterator it = enum_name_without_underscore.begin(); |
235 | it != enum_name_without_underscore.end(); ++it) { |
236 | *it = ascii_toupper(c: *it); |
237 | } |
238 | |
239 | if (enum_name_without_underscore == enum_name) { |
240 | return &enum_value; |
241 | } |
242 | } |
243 | } |
244 | return nullptr; |
245 | } |
246 | |
247 | std::string EnumValueNameToLowerCamelCase(StringPiece input) { |
248 | std::string input_string(input); |
249 | std::transform(first: input_string.begin(), last: input_string.end(), result: input_string.begin(), |
250 | unary_op: ::tolower); |
251 | return ToCamelCase(input: input_string); |
252 | } |
253 | |
254 | std::string ToCamelCase(StringPiece input) { |
255 | bool capitalize_next = false; |
256 | bool was_cap = true; |
257 | bool is_cap = false; |
258 | bool first_word = true; |
259 | std::string result; |
260 | result.reserve(res_arg: input.size()); |
261 | |
262 | for (size_t i = 0; i < input.size(); ++i, was_cap = is_cap) { |
263 | is_cap = ascii_isupper(c: input[i]); |
264 | if (input[i] == '_') { |
265 | capitalize_next = true; |
266 | if (!result.empty()) first_word = false; |
267 | continue; |
268 | } else if (first_word) { |
269 | // Consider when the current character B is capitalized, |
270 | // first word ends when: |
271 | // 1) following a lowercase: "...aB..." |
272 | // 2) followed by a lowercase: "...ABc..." |
273 | if (!result.empty() && is_cap && |
274 | (!was_cap || |
275 | (i + 1 < input.size() && ascii_islower(c: input[i + 1])))) { |
276 | first_word = false; |
277 | result.push_back(c: input[i]); |
278 | } else { |
279 | result.push_back(c: ascii_tolower(c: input[i])); |
280 | continue; |
281 | } |
282 | } else if (capitalize_next) { |
283 | capitalize_next = false; |
284 | if (ascii_islower(c: input[i])) { |
285 | result.push_back(c: ascii_toupper(c: input[i])); |
286 | continue; |
287 | } else { |
288 | result.push_back(c: input[i]); |
289 | continue; |
290 | } |
291 | } else { |
292 | result.push_back(c: ascii_tolower(c: input[i])); |
293 | } |
294 | } |
295 | return result; |
296 | } |
297 | |
298 | std::string ToSnakeCase(StringPiece input) { |
299 | bool was_not_underscore = false; // Initialize to false for case 1 (below) |
300 | bool was_not_cap = false; |
301 | std::string result; |
302 | result.reserve(res_arg: input.size() << 1); |
303 | |
304 | for (size_t i = 0; i < input.size(); ++i) { |
305 | if (ascii_isupper(c: input[i])) { |
306 | // Consider when the current character B is capitalized: |
307 | // 1) At beginning of input: "B..." => "b..." |
308 | // (e.g. "Biscuit" => "biscuit") |
309 | // 2) Following a lowercase: "...aB..." => "...a_b..." |
310 | // (e.g. "gBike" => "g_bike") |
311 | // 3) At the end of input: "...AB" => "...ab" |
312 | // (e.g. "GoogleLAB" => "google_lab") |
313 | // 4) Followed by a lowercase: "...ABc..." => "...a_bc..." |
314 | // (e.g. "GBike" => "g_bike") |
315 | if (was_not_underscore && // case 1 out |
316 | (was_not_cap || // case 2 in, case 3 out |
317 | (i + 1 < input.size() && // case 3 out |
318 | ascii_islower(c: input[i + 1])))) { // case 4 in |
319 | // We add an underscore for case 2 and case 4. |
320 | result.push_back(c: '_'); |
321 | } |
322 | result.push_back(c: ascii_tolower(c: input[i])); |
323 | was_not_underscore = true; |
324 | was_not_cap = false; |
325 | } else { |
326 | result.push_back(c: input[i]); |
327 | was_not_underscore = input[i] != '_'; |
328 | was_not_cap = true; |
329 | } |
330 | } |
331 | return result; |
332 | } |
333 | |
334 | std::set<std::string>* well_known_types_ = nullptr; |
335 | PROTOBUF_NAMESPACE_ID::internal::once_flag well_known_types_init_; |
336 | const char* well_known_types_name_array_[] = { |
337 | "google.protobuf.Timestamp" , "google.protobuf.Duration" , |
338 | "google.protobuf.DoubleValue" , "google.protobuf.FloatValue" , |
339 | "google.protobuf.Int64Value" , "google.protobuf.UInt64Value" , |
340 | "google.protobuf.Int32Value" , "google.protobuf.UInt32Value" , |
341 | "google.protobuf.BoolValue" , "google.protobuf.StringValue" , |
342 | "google.protobuf.BytesValue" , "google.protobuf.FieldMask" }; |
343 | |
344 | void DeleteWellKnownTypes() { delete well_known_types_; } |
345 | |
346 | void InitWellKnownTypes() { |
347 | well_known_types_ = new std::set<std::string>; |
348 | for (int i = 0; i < GOOGLE_ARRAYSIZE(well_known_types_name_array_); ++i) { |
349 | well_known_types_->insert(x: well_known_types_name_array_[i]); |
350 | } |
351 | google::protobuf::internal::OnShutdown(func: &DeleteWellKnownTypes); |
352 | } |
353 | |
354 | bool IsWellKnownType(const std::string& type_name) { |
355 | PROTOBUF_NAMESPACE_ID::internal::call_once(args&: well_known_types_init_, |
356 | args&: InitWellKnownTypes); |
357 | return ContainsKey(collection: *well_known_types_, key: type_name); |
358 | } |
359 | |
360 | bool IsValidBoolString(StringPiece bool_string) { |
361 | return bool_string == "true" || bool_string == "false" || |
362 | bool_string == "1" || bool_string == "0" ; |
363 | } |
364 | |
365 | bool IsMap(const google::protobuf::Field& field, |
366 | const google::protobuf::Type& type) { |
367 | return field.cardinality() == google::protobuf::Field::CARDINALITY_REPEATED && |
368 | (GetBoolOptionOrDefault(options: type.options(), option_name: "map_entry" , default_value: false) || |
369 | GetBoolOptionOrDefault(options: type.options(), |
370 | option_name: "google.protobuf.MessageOptions.map_entry" , |
371 | default_value: false)); |
372 | } |
373 | |
374 | bool IsMessageSetWireFormat(const google::protobuf::Type& type) { |
375 | return GetBoolOptionOrDefault(options: type.options(), option_name: "message_set_wire_format" , |
376 | default_value: false) || |
377 | GetBoolOptionOrDefault( |
378 | options: type.options(), |
379 | option_name: "google.protobuf.MessageOptions.message_set_wire_format" , default_value: false); |
380 | } |
381 | |
382 | std::string DoubleAsString(double value) { |
383 | if (value == std::numeric_limits<double>::infinity()) return "Infinity" ; |
384 | if (value == -std::numeric_limits<double>::infinity()) return "-Infinity" ; |
385 | if (std::isnan(x: value)) return "NaN" ; |
386 | |
387 | return SimpleDtoa(value); |
388 | } |
389 | |
390 | std::string FloatAsString(float value) { |
391 | if (std::isfinite(x: value)) return SimpleFtoa(value); |
392 | return DoubleAsString(value); |
393 | } |
394 | |
395 | bool SafeStrToFloat(StringPiece str, float* value) { |
396 | double double_value; |
397 | if (!safe_strtod(str, value: &double_value)) { |
398 | return false; |
399 | } |
400 | |
401 | if (std::isinf(x: double_value) || std::isnan(x: double_value)) return false; |
402 | |
403 | // Fail if the value is not representable in float. |
404 | if (double_value > std::numeric_limits<float>::max() || |
405 | double_value < -std::numeric_limits<float>::max()) { |
406 | return false; |
407 | } |
408 | |
409 | *value = static_cast<float>(double_value); |
410 | return true; |
411 | } |
412 | |
413 | } // namespace converter |
414 | } // namespace util |
415 | } // namespace protobuf |
416 | } // namespace google |
417 | |