blob: 68d3b034b4151319acf939286e5ecb1321c2dc14 [file] [log] [blame]
Austin Schuh3e95e5d2019-09-20 00:08:54 -07001#include "aos/json_to_flatbuffer.h"
2
3#include <cstddef>
4#include "stdio.h"
5
Austin Schuhd339a9b2019-10-05 21:33:32 -07006#include "absl/strings/string_view.h"
Austin Schuh43c6a352019-09-30 22:22:10 -07007#include "aos/flatbuffer_utils.h"
Austin Schuh3e95e5d2019-09-20 00:08:54 -07008#include "aos/logging/logging.h"
9#include "flatbuffers/flatbuffers.h"
10#include "flatbuffers/minireflect.h"
11
12// TODO(austin): Can we just do an Offset<void> ? It doesn't matter, so maybe
13// just say that.
14//
15// TODO(austin): I've yet to see how to create an ET_UTYPE, so I don't know what
16// one is and how to test it. So everything rejects it.
17
18namespace aos {
19
20// Finds the field index in the table given the name.
21int FieldIndex(const flatbuffers::TypeTable *typetable,
22 const char *field_name) {
23 CHECK(typetable->values == nullptr);
24 for (size_t i = 0; i < typetable->num_elems; ++i) {
25 if (strcmp(field_name, typetable->names[i]) == 0) {
26 return i;
27 }
28 }
29 return -1;
30}
31
32namespace {
33
34// Class to hold one of the 3 json types for an array.
35struct Element {
36 // The type.
37 enum class ElementType { INT, DOUBLE, OFFSET };
38
39 // Constructs an Element holding an integer.
40 Element(int64_t new_int_element)
41 : int_element(new_int_element), type(ElementType::INT) {}
42 // Constructs an Element holding an double.
43 Element(double new_double_element)
44 : double_element(new_double_element), type(ElementType::DOUBLE) {}
45 // Constructs an Element holding an Offset.
46 Element(flatbuffers::Offset<flatbuffers::String> new_offset_element)
47 : offset_element(new_offset_element), type(ElementType::OFFSET) {}
48
49 // Union for the various datatypes.
50 union {
51 int64_t int_element;
52 double double_element;
53 flatbuffers::Offset<flatbuffers::String> offset_element;
54 };
55
56 // And an enum signaling which one is in use.
57 ElementType type;
58};
59
60// Structure to represent a field element.
61struct FieldElement {
62 FieldElement(int new_field_index, int64_t int_element)
63 : element(int_element), field_index(new_field_index) {}
64 FieldElement(int new_field_index, double double_element)
65 : element(double_element), field_index(new_field_index) {}
66 FieldElement(int new_field_index,
67 flatbuffers::Offset<flatbuffers::String> offset_element)
68 : element(offset_element), field_index(new_field_index) {}
69
70 // Data to write.
71 Element element;
72 // Field index. The type table which this index is for is stored outside this
73 // object.
74 int field_index;
75};
76
Austin Schuh43c6a352019-09-30 22:22:10 -070077// Adds a single element. This assumes that vectors have been dealt with
78// already. Returns true on success.
79bool AddSingleElement(const flatbuffers::TypeTable *typetable,
80 const FieldElement &field_element,
81 ::std::vector<bool> *fields_in_use,
82 flatbuffers::FlatBufferBuilder *fbb);
83bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
84 int64_t int_value, flatbuffers::FlatBufferBuilder *fbb);
85bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
86 double double_value, flatbuffers::FlatBufferBuilder *fbb);
87bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
88 flatbuffers::Offset<flatbuffers::String> offset_element,
89 flatbuffers::FlatBufferBuilder *fbb);
90
91
92// Writes an array of FieldElement (with the definition in the type
93// table) to the builder. Returns the offset of the table.
94flatbuffers::uoffset_t WriteTable(const flatbuffers::TypeTable *typetable,
95 const ::std::vector<FieldElement> &elements,
96 flatbuffers::FlatBufferBuilder *fbb) {
97 // End of a nested struct! Add it.
98 const flatbuffers::uoffset_t start = fbb->StartTable();
99
100 ::std::vector<bool> fields_in_use(typetable->num_elems, false);
101
102 for (const FieldElement &field_element : elements) {
103 AddSingleElement(typetable, field_element, &fields_in_use, fbb);
104 }
105
106 return fbb->EndTable(start);
107}
108
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700109// Class to parse JSON into a flatbuffer.
110//
111// The basic strategy is that we need to do everything backwards. So we need to
112// build up what we need to do fully in memory, then do it.
113//
114// The driver for this is that strings need to be fully created before the
115// tables that use them. Same for sub messages. But, we only know we have them
Austin Schuh43c6a352019-09-30 22:22:10 -0700116// all when the structure ends. So, store each sub message in a
117// FieldElement and put them in the table at the end when we finish up
118// each message. Same goes for vectors.
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700119class JsonParser {
120 public:
121 JsonParser() { fbb_.ForceDefaults(1); }
122 ~JsonParser() {}
123
124 // Parses the json into a flatbuffer. Returns either an empty vector on
125 // error, or a vector with the flatbuffer data in it.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700126 ::std::vector<uint8_t> Parse(const absl::string_view data,
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700127 const flatbuffers::TypeTable *typetable) {
Austin Schuh43c6a352019-09-30 22:22:10 -0700128 flatbuffers::uoffset_t end = 0;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700129 bool result = DoParse(typetable, data, &end);
130
131 if (result) {
132 // On success, finish the table and build the vector.
133 auto o = flatbuffers::Offset<flatbuffers::Table>(end);
134 fbb_.Finish(o);
135
136 const uint8_t *buf = fbb_.GetBufferPointer();
137 const int size = fbb_.GetSize();
138 return ::std::vector<uint8_t>(buf, buf + size);
139 } else {
140 // Otherwise return an empty vector.
141 return ::std::vector<uint8_t>();
142 }
143 }
144
145 private:
146 // Setters and getters for in_vector (at the current level of the stack)
147 bool in_vector() const { return stack_.back().in_vector; }
148 void set_in_vector(bool in_vector) { stack_.back().in_vector = in_vector; }
149
150 // Parses the flatbuffer. This is a second method so we can do easier
151 // cleanup at the top level. Returns true on success.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700152 bool DoParse(const flatbuffers::TypeTable *typetable,
153 const absl::string_view data, flatbuffers::uoffset_t *table_end);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700154
155 // Adds *_value for the provided field. If we are in a vector, queues the
156 // data up in vector_elements_. Returns true on success.
157 bool AddElement(int field_index, int64_t int_value);
158 bool AddElement(int field_index, double double_value);
159 bool AddElement(int field_index, const ::std::string &data);
160
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700161 // Finishes a vector for the provided field index. Returns true on success.
162 bool FinishVector(int field_index);
163
164 // Pushes an element as part of a vector. Returns true on success.
165 bool PushElement(flatbuffers::ElementaryType elementary_type,
166 int64_t int_value);
167 bool PushElement(flatbuffers::ElementaryType elementary_type,
168 double double_value);
169 bool PushElement(flatbuffers::ElementaryType elementary_type,
170 flatbuffers::Offset<flatbuffers::String> offset_value);
171
172 flatbuffers::FlatBufferBuilder fbb_;
173
174 // This holds the state information that is needed as you recurse into
175 // nested structures.
176 struct FlatBufferContext {
177 // Type of the current type.
178 const flatbuffers::TypeTable *typetable;
179 // If true, we are parsing a vector.
180 bool in_vector;
181 // The field index of the current field.
182 int field_index;
183 // Name of the current field.
184 ::std::string field_name;
185
186 // Field elements that need to be inserted.
187 ::std::vector<FieldElement> elements;
188 };
189 ::std::vector<FlatBufferContext> stack_;
190
191 // For scalar types (not strings, and not nested tables), the vector ends
192 // up being implemented as a start and end, and a block of data. So we
193 // can't just push offsets in as we go. We either need to reproduce the
194 // logic inside flatbuffers, or build up vectors of the data. Vectors will
195 // be a bit of extra stack space, but whatever.
196 //
197 // Strings and nested structures are vectors of offsets.
198 // into the vector. Once you get to the end, you build up a vector and
199 // push that into the field.
200 ::std::vector<Element> vector_elements_;
201};
202
203bool JsonParser::DoParse(const flatbuffers::TypeTable *typetable,
Austin Schuhd339a9b2019-10-05 21:33:32 -0700204 const absl::string_view data,
205 flatbuffers::uoffset_t *table_end) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700206 ::std::vector<const flatbuffers::TypeTable *> stack;
207
208 Tokenizer t(data);
209
210 // Main loop. Run until we get an end.
211 while (true) {
212 Tokenizer::TokenType token = t.Next();
213
214 switch (token) {
215 case Tokenizer::TokenType::kEnd:
216 if (stack_.size() != 0) {
217 printf("Failed to unwind stack all the way\n");
218 return false;
219 } else {
220 return true;
221 }
222 break;
223 case Tokenizer::TokenType::kError:
224 return false;
225 break;
226
227 case Tokenizer::TokenType::kStartObject: // {
228 if (stack_.size() == 0) {
229 stack_.push_back({typetable, false, -1, "", {}});
230 } else {
231 int field_index = stack_.back().field_index;
232
233 const flatbuffers::TypeCode &type_code =
234 stack_.back().typetable->type_codes[field_index];
235
236 if (type_code.base_type != flatbuffers::ET_SEQUENCE) {
237 printf("Field '%s' is not a sequence\n",
238 stack_.back().field_name.c_str());
239 return false;
240 }
241
242 flatbuffers::TypeFunction type_function =
243 stack_.back().typetable->type_refs[type_code.sequence_ref];
244
245 stack_.push_back({type_function(), false, -1, "", {}});
246 }
247 break;
248 case Tokenizer::TokenType::kEndObject: // }
249 if (stack_.size() == 0) {
250 // Somehow we popped more than we pushed. Error.
251 printf("Empty stack\n");
252 return false;
253 } else {
254 // End of a nested struct! Add it.
Austin Schuh43c6a352019-09-30 22:22:10 -0700255 const flatbuffers::uoffset_t end = WriteTable(
256 stack_.back().typetable, stack_.back().elements, &fbb_);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700257
258 // We now want to talk about the parent structure. Pop the child.
259 stack_.pop_back();
260
261 if (stack_.size() == 0) {
262 // Instead of queueing it up in the stack, return it through the
263 // passed in variable.
264 *table_end = end;
265 } else {
266 // And now we can add it.
267 const int field_index = stack_.back().field_index;
268
269 // Do the right thing if we are in a vector.
270 if (in_vector()) {
271 vector_elements_.emplace_back(
272 flatbuffers::Offset<flatbuffers::String>(end));
273 } else {
274 stack_.back().elements.emplace_back(
275 field_index, flatbuffers::Offset<flatbuffers::String>(end));
276 }
277 }
278 }
279 break;
280
281 case Tokenizer::TokenType::kStartArray: // [
282 if (stack_.size() == 0) {
283 // We don't support an array of structs at the root level.
284 return false;
285 }
286 // Sanity check that we aren't trying to make a vector of vectors.
287 if (in_vector()) {
288 return false;
289 }
290 set_in_vector(true);
291
292 break;
293 case Tokenizer::TokenType::kEndArray: { // ]
294 if (!in_vector()) {
295 return false;
296 }
297
298 const int field_index = stack_.back().field_index;
299
300 if (!FinishVector(field_index)) return false;
301
302 set_in_vector(false);
303 } break;
304
305 case Tokenizer::TokenType::kTrueValue: // true
306 case Tokenizer::TokenType::kFalseValue: // false
307 case Tokenizer::TokenType::kNumberValue: {
308 bool is_int = true;
309 double double_value;
310 long long int_value;
311 if (token == Tokenizer::TokenType::kTrueValue) {
312 int_value = 1;
313 } else if (token == Tokenizer::TokenType::kFalseValue) {
314 int_value = 0;
315 } else if (!t.FieldAsInt(&int_value)) {
316 if (t.FieldAsDouble(&double_value)) {
317 is_int = false;
318 } else {
319 fprintf(stderr, "Got a invalid number '%s'\n",
320 t.field_value().c_str());
321 return false;
322 }
323 }
324
325 const int field_index = stack_.back().field_index;
326
327 if (is_int) {
328 // No need to get too stressed about bool vs int. Convert them all.
329 int64_t val = int_value;
330 if (!AddElement(field_index, val)) return false;
331 } else {
332 if (!AddElement(field_index, double_value)) return false;
333 }
334 } break;
335 // TODO(austin): Need to detect int vs float.
336 /*
337 asdf
338 {
339 const int field_index = stack_.back().field_index;
340
341 } break;
342 */
343 case Tokenizer::TokenType::kStringValue: // string value
344 {
345 const int field_index = stack_.back().field_index;
346
347 if (!AddElement(field_index, t.field_value())) return false;
348 } break;
349 case Tokenizer::TokenType::kField: // field name
350 {
351 stack_.back().field_name = t.field_name();
352 stack_.back().field_index = FieldIndex(
353 stack_.back().typetable, stack_.back().field_name.c_str());
354
355 if (stack_.back().field_index == -1) {
356 printf("Invalid field name '%s'\n", stack_.back().field_name.c_str());
357 return false;
358 }
359 } break;
360 }
361 }
362 return false;
363}
364
365bool JsonParser::AddElement(int field_index, int64_t int_value) {
366 flatbuffers::TypeCode type_code =
367 stack_.back().typetable->type_codes[field_index];
368
369 if (type_code.is_vector != in_vector()) {
370 printf("Type and json disagree on if we are in a vector or not\n");
371 return false;
372 }
373
374 if (in_vector()) {
375 vector_elements_.emplace_back(int_value);
376 } else {
377 stack_.back().elements.emplace_back(field_index, int_value);
378 }
379 return true;
380}
381
382bool JsonParser::AddElement(int field_index, double double_value) {
383 flatbuffers::TypeCode type_code =
384 stack_.back().typetable->type_codes[field_index];
385
386 if (type_code.is_vector != in_vector()) {
387 printf("Type and json disagree on if we are in a vector or not\n");
388 return false;
389 }
390
391 if (in_vector()) {
392 vector_elements_.emplace_back(double_value);
393 } else {
394 stack_.back().elements.emplace_back(field_index, double_value);
395 }
396 return true;
397}
398
399bool JsonParser::AddElement(int field_index, const ::std::string &data) {
400 flatbuffers::TypeCode type_code =
401 stack_.back().typetable->type_codes[field_index];
402
403 if (type_code.is_vector != in_vector()) {
404 printf("Type and json disagree on if we are in a vector or not\n");
405 return false;
406 }
407
408 if (in_vector()) {
409 vector_elements_.emplace_back(fbb_.CreateString(data));
410
411 } else {
412 stack_.back().elements.emplace_back(field_index, fbb_.CreateString(data));
413 }
414 return true;
415}
416
Austin Schuh43c6a352019-09-30 22:22:10 -0700417bool AddSingleElement(const flatbuffers::TypeTable *typetable,
418 const FieldElement &field_element,
419 ::std::vector<bool> *fields_in_use,
420 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700421 if ((*fields_in_use)[field_element.field_index]) {
422 printf("Duplicate field: '%s'\n",
Austin Schuh43c6a352019-09-30 22:22:10 -0700423 typetable->names[field_element.field_index]);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700424 return false;
425 }
426
427 (*fields_in_use)[field_element.field_index] = true;
428
429 switch (field_element.element.type) {
430 case Element::ElementType::INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700431 return AddSingleElement(typetable, field_element.field_index,
432 field_element.element.int_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700433 case Element::ElementType::DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700434 return AddSingleElement(typetable, field_element.field_index,
435 field_element.element.double_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700436 case Element::ElementType::OFFSET:
Austin Schuh43c6a352019-09-30 22:22:10 -0700437 return AddSingleElement(typetable, field_element.field_index,
438 field_element.element.offset_element, fbb);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700439 }
440 return false;
441}
442
Austin Schuh43c6a352019-09-30 22:22:10 -0700443bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
444 int64_t int_value, flatbuffers::FlatBufferBuilder *fbb
445
446) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700447 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
448 static_cast<flatbuffers::voffset_t>(field_index));
449
Austin Schuh43c6a352019-09-30 22:22:10 -0700450 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700451
452 const flatbuffers::ElementaryType elementary_type =
453 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
454 switch (elementary_type) {
455 case flatbuffers::ET_BOOL:
Austin Schuh43c6a352019-09-30 22:22:10 -0700456 fbb->AddElement<bool>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700457 return true;
458 case flatbuffers::ET_CHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700459 fbb->AddElement<int8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700460 return true;
461 case flatbuffers::ET_UCHAR:
Austin Schuh43c6a352019-09-30 22:22:10 -0700462 fbb->AddElement<uint8_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700463 return true;
464 case flatbuffers::ET_SHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700465 fbb->AddElement<int16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700466 return true;
467 case flatbuffers::ET_USHORT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700468 fbb->AddElement<uint16_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700469 return true;
470 case flatbuffers::ET_INT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700471 fbb->AddElement<int32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700472 return true;
473 case flatbuffers::ET_UINT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700474 fbb->AddElement<uint32_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700475 return true;
476 case flatbuffers::ET_LONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700477 fbb->AddElement<int64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700478 return true;
479 case flatbuffers::ET_ULONG:
Austin Schuh43c6a352019-09-30 22:22:10 -0700480 fbb->AddElement<uint64_t>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700481 return true;
482 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700483 fbb->AddElement<float>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700484 return true;
485 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700486 fbb->AddElement<double>(field_offset, int_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700487 return true;
488 case flatbuffers::ET_STRING:
489 case flatbuffers::ET_UTYPE:
490 case flatbuffers::ET_SEQUENCE:
491 printf("Mismatched type for field '%s'. Got: integer, expected %s\n",
Austin Schuh43c6a352019-09-30 22:22:10 -0700492 typetable->names[field_index],
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700493 ElementaryTypeName(elementary_type));
494 return false;
495 };
496 return false;
497}
498
Austin Schuh43c6a352019-09-30 22:22:10 -0700499bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
500 double double_value,
501 flatbuffers::FlatBufferBuilder *fbb) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700502 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
503 static_cast<flatbuffers::voffset_t>(field_index));
504
Austin Schuh43c6a352019-09-30 22:22:10 -0700505 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700506
507 const flatbuffers::ElementaryType elementary_type =
508 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
509 switch (elementary_type) {
510 case flatbuffers::ET_UTYPE:
511 case flatbuffers::ET_BOOL:
512 case flatbuffers::ET_CHAR:
513 case flatbuffers::ET_UCHAR:
514 case flatbuffers::ET_SHORT:
515 case flatbuffers::ET_USHORT:
516 case flatbuffers::ET_INT:
517 case flatbuffers::ET_UINT:
518 case flatbuffers::ET_LONG:
519 case flatbuffers::ET_ULONG:
520 case flatbuffers::ET_STRING:
521 case flatbuffers::ET_SEQUENCE:
522 printf("Mismatched type for field '%s'. Got: double, expected %s\n",
Austin Schuh43c6a352019-09-30 22:22:10 -0700523 typetable->names[field_index],
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700524 ElementaryTypeName(elementary_type));
525 return false;
526 case flatbuffers::ET_FLOAT:
Austin Schuh43c6a352019-09-30 22:22:10 -0700527 fbb->AddElement<float>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700528 return true;
529 case flatbuffers::ET_DOUBLE:
Austin Schuh43c6a352019-09-30 22:22:10 -0700530 fbb->AddElement<double>(field_offset, double_value, 0);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700531 return true;
532 }
533 return false;
534}
Austin Schuh43c6a352019-09-30 22:22:10 -0700535bool AddSingleElement(const flatbuffers::TypeTable *typetable, int field_index,
536 flatbuffers::Offset<flatbuffers::String> offset_element,
537 flatbuffers::FlatBufferBuilder *fbb) {
538 flatbuffers::TypeCode type_code = typetable->type_codes[field_index];
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700539
540 flatbuffers::voffset_t field_offset = flatbuffers::FieldIndexToOffset(
541 static_cast<flatbuffers::voffset_t>(field_index));
542
543 // Vectors will always be Offset<>'s.
544 if (type_code.is_vector) {
Austin Schuh43c6a352019-09-30 22:22:10 -0700545 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700546 return true;
547 }
548
549 const flatbuffers::ElementaryType elementary_type =
550 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
551 switch (elementary_type) {
552 case flatbuffers::ET_UTYPE:
553 case flatbuffers::ET_BOOL:
554 case flatbuffers::ET_CHAR:
555 case flatbuffers::ET_UCHAR:
556 case flatbuffers::ET_SHORT:
557 case flatbuffers::ET_USHORT:
558 case flatbuffers::ET_INT:
559 case flatbuffers::ET_UINT:
560 case flatbuffers::ET_LONG:
561 case flatbuffers::ET_ULONG:
562 case flatbuffers::ET_FLOAT:
563 case flatbuffers::ET_DOUBLE:
564 printf("Mismatched type for field '%s'. Got: string, expected %s\n",
Austin Schuh43c6a352019-09-30 22:22:10 -0700565 typetable->names[field_index],
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700566 ElementaryTypeName(elementary_type));
567 return false;
568 case flatbuffers::ET_SEQUENCE:
569 case flatbuffers::ET_STRING:
Austin Schuh43c6a352019-09-30 22:22:10 -0700570 fbb->AddOffset(field_offset, offset_element);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700571 return true;
572 }
573 return false;
574}
575
576bool JsonParser::FinishVector(int field_index) {
577 flatbuffers::TypeCode type_code =
578 stack_.back().typetable->type_codes[field_index];
579
580 const flatbuffers::ElementaryType elementary_type =
581 static_cast<flatbuffers::ElementaryType>(type_code.base_type);
582
583 // Vectors have a start (unfortunately which needs to know the size)
584 fbb_.StartVector(
585 vector_elements_.size(),
586 flatbuffers::InlineSize(elementary_type, stack_.back().typetable));
587
588 // Then the data (in reverse order for some reason...)
589 for (size_t i = vector_elements_.size(); i > 0;) {
590 const Element &element = vector_elements_[--i];
591 switch (element.type) {
592 case Element::ElementType::INT:
593 if (!PushElement(elementary_type, element.int_element)) return false;
594 break;
595 case Element::ElementType::DOUBLE:
596 if (!PushElement(elementary_type, element.double_element)) return false;
597 break;
598 case Element::ElementType::OFFSET:
599 if (!PushElement(elementary_type, element.offset_element)) return false;
600 break;
601 }
602 }
603
604 // Then an End which is placed into the buffer the same as any other offset.
605 stack_.back().elements.emplace_back(
606 field_index, flatbuffers::Offset<flatbuffers::String>(
607 fbb_.EndVector(vector_elements_.size())));
608 return true;
609}
610
611bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
612 int64_t int_value) {
613 switch (elementary_type) {
614 case flatbuffers::ET_BOOL:
615 fbb_.PushElement<bool>(int_value);
616 return true;
617 case flatbuffers::ET_CHAR:
618 fbb_.PushElement<int8_t>(int_value);
619 return true;
620 case flatbuffers::ET_UCHAR:
621 fbb_.PushElement<uint8_t>(int_value);
622 return true;
623 case flatbuffers::ET_SHORT:
624 fbb_.PushElement<int16_t>(int_value);
625 return true;
626 case flatbuffers::ET_USHORT:
627 fbb_.PushElement<uint16_t>(int_value);
628 return true;
629 case flatbuffers::ET_INT:
630 fbb_.PushElement<int32_t>(int_value);
631 return true;
632 case flatbuffers::ET_UINT:
633 fbb_.PushElement<uint32_t>(int_value);
634 return true;
635 case flatbuffers::ET_LONG:
636 fbb_.PushElement<int64_t>(int_value);
637 return true;
638 case flatbuffers::ET_ULONG:
639 fbb_.PushElement<uint64_t>(int_value);
640 return true;
641 case flatbuffers::ET_FLOAT:
642 fbb_.PushElement<float>(int_value);
643 return true;
644 case flatbuffers::ET_DOUBLE:
645 fbb_.PushElement<double>(int_value);
646 return true;
647 case flatbuffers::ET_STRING:
648 case flatbuffers::ET_UTYPE:
649 case flatbuffers::ET_SEQUENCE:
650 printf("Mismatched type for field '%s'. Got: integer, expected %s\n",
651 stack_.back().field_name.c_str(),
652 ElementaryTypeName(elementary_type));
653 return false;
654 };
655 return false;
656}
657
658bool JsonParser::PushElement(flatbuffers::ElementaryType elementary_type,
659 double double_value) {
660 switch (elementary_type) {
661 case flatbuffers::ET_UTYPE:
662 case flatbuffers::ET_BOOL:
663 case flatbuffers::ET_CHAR:
664 case flatbuffers::ET_UCHAR:
665 case flatbuffers::ET_SHORT:
666 case flatbuffers::ET_USHORT:
667 case flatbuffers::ET_INT:
668 case flatbuffers::ET_UINT:
669 case flatbuffers::ET_LONG:
670 case flatbuffers::ET_ULONG:
671 case flatbuffers::ET_STRING:
672 case flatbuffers::ET_SEQUENCE:
673 printf("Mismatched type for field '%s'. Got: double, expected %s\n",
674 stack_.back().field_name.c_str(),
675 ElementaryTypeName(elementary_type));
676 return false;
677 case flatbuffers::ET_FLOAT:
678 fbb_.PushElement<float>(double_value);
679 return true;
680 case flatbuffers::ET_DOUBLE:
681 fbb_.PushElement<double>(double_value);
682 return true;
683 }
684 return false;
685}
686
687bool JsonParser::PushElement(
688 flatbuffers::ElementaryType elementary_type,
689 flatbuffers::Offset<flatbuffers::String> offset_value) {
690 switch (elementary_type) {
691 case flatbuffers::ET_UTYPE:
692 case flatbuffers::ET_BOOL:
693 case flatbuffers::ET_CHAR:
694 case flatbuffers::ET_UCHAR:
695 case flatbuffers::ET_SHORT:
696 case flatbuffers::ET_USHORT:
697 case flatbuffers::ET_INT:
698 case flatbuffers::ET_UINT:
699 case flatbuffers::ET_LONG:
700 case flatbuffers::ET_ULONG:
701 case flatbuffers::ET_FLOAT:
702 case flatbuffers::ET_DOUBLE:
703 printf("Mismatched type for field '%s'. Got: sequence, expected %s\n",
704 stack_.back().field_name.c_str(),
705 ElementaryTypeName(elementary_type));
706 return false;
707 case flatbuffers::ET_STRING:
708 case flatbuffers::ET_SEQUENCE:
709 fbb_.PushElement(offset_value);
710 return true;
711 }
712 return false;
713}
714
715} // namespace
716
717::std::vector<uint8_t> JsonToFlatbuffer(
Austin Schuhd339a9b2019-10-05 21:33:32 -0700718 const absl::string_view data, const flatbuffers::TypeTable *typetable) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700719 JsonParser p;
720 return p.Parse(data, typetable);
721}
722
723::std::string FlatbufferToJson(const uint8_t *buffer,
724 const ::flatbuffers::TypeTable *typetable,
725 bool multi_line) {
726 ::flatbuffers::ToStringVisitor tostring_visitor(
727 multi_line ? "\n" : " ", true, multi_line ? " " : "", multi_line);
728 IterateFlatBuffer(buffer, typetable, &tostring_visitor);
729 return tostring_visitor.s;
730}
731
732void Tokenizer::ConsumeWhitespace() {
733 while (true) {
Austin Schuhd339a9b2019-10-05 21:33:32 -0700734 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700735 return;
736 }
737 // Skip any whitespace.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700738 if (Char() == ' ' || Char() == '\r' || Char() == '\t') {
739 ConsumeChar();
740 } else if (Char() == '\n') {
741 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700742 ++linenumber_;
743 } else {
744 // There is no fail. Once we are out of whitespace (including 0 of it),
745 // declare success.
746 return;
747 }
748 }
749}
750
751bool Tokenizer::Consume(const char *token) {
Austin Schuhd339a9b2019-10-05 21:33:32 -0700752 const absl::string_view original = data_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700753 while (true) {
754 // Finishing the token is success.
755 if (*token == '\0') {
756 return true;
757 }
758
759 // But finishing the data first is failure.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700760 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700761 data_ = original;
762 return false;
763 }
764
765 // Missmatch is failure.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700766 if (*token != Char()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700767 data_ = original;
768 return false;
769 }
770
Austin Schuhd339a9b2019-10-05 21:33:32 -0700771 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700772 ++token;
773 }
774}
775
776bool Tokenizer::ConsumeString(::std::string *s) {
777 // Under no conditions is it acceptible to run out of data while parsing a
Austin Schuhd339a9b2019-10-05 21:33:32 -0700778 // string. Any AtEnd checks should confirm that.
779 const absl::string_view original = data_;
780 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700781 return false;
782 }
783
784 // Expect the leading "
Austin Schuhd339a9b2019-10-05 21:33:32 -0700785 if (Char() != '"') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700786 return false;
787 }
788
Austin Schuhd339a9b2019-10-05 21:33:32 -0700789 ConsumeChar();
790 absl::string_view last_parsed_data = data_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700791 *s = ::std::string();
792
793 while (true) {
Austin Schuhd339a9b2019-10-05 21:33:32 -0700794 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700795 data_ = original;
796 return false;
797 }
798
799 // If we get an end or an escape, do something special.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700800 if (Char() == '"' || Char() == '\\') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700801 // Save what we found up until now, not including this character.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700802 *s += ::std::string(
803 last_parsed_data.substr(0, last_parsed_data.size() - data_.size()));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700804
805 // Update the pointer.
806 last_parsed_data = data_;
807
808 // " is the end, declare victory.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700809 if (Char() == '"') {
810 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700811 return true;
812 } else {
Austin Schuhd339a9b2019-10-05 21:33:32 -0700813 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700814 // Now consume valid escape characters and add their representation onto
815 // the output string.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700816 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700817 data_ = original;
818 return false;
Austin Schuhd339a9b2019-10-05 21:33:32 -0700819 } else if (Char() == '"') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700820 *s += "\"";
Austin Schuhd339a9b2019-10-05 21:33:32 -0700821 } else if (Char() == '\\') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700822 *s += "\\";
Austin Schuhd339a9b2019-10-05 21:33:32 -0700823 } else if (Char() == '/') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700824 *s += "/";
Austin Schuhd339a9b2019-10-05 21:33:32 -0700825 } else if (Char() == 'b') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700826 *s += "\b";
Austin Schuhd339a9b2019-10-05 21:33:32 -0700827 } else if (Char() == 'f') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700828 *s += "\f";
Austin Schuhd339a9b2019-10-05 21:33:32 -0700829 } else if (Char() == 'n') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700830 *s += "\n";
Austin Schuhd339a9b2019-10-05 21:33:32 -0700831 } else if (Char() == 'r') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700832 *s += "\r";
Austin Schuhd339a9b2019-10-05 21:33:32 -0700833 } else if (Char() == 't') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700834 *s += "\t";
Austin Schuhd339a9b2019-10-05 21:33:32 -0700835 } else if (Char() == 'u') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700836 // TODO(austin): Unicode should be valid, but I really don't care to
837 // do this now...
838 fprintf(stderr, "Unexpected unicode on line %d\n", linenumber_);
839 data_ = original;
840 return false;
841 }
842 }
843 // And skip the escaped character.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700844 last_parsed_data = data_.substr(1);
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700845 }
846
Austin Schuhd339a9b2019-10-05 21:33:32 -0700847 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700848 }
849}
850
851bool Tokenizer::ConsumeNumber(::std::string *s) {
852 // Under no conditions is it acceptible to run out of data while parsing a
Austin Schuhd339a9b2019-10-05 21:33:32 -0700853 // number. Any AtEnd() checks should confirm that.
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700854 *s = ::std::string();
Austin Schuhd339a9b2019-10-05 21:33:32 -0700855 const absl::string_view original = data_;
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700856
857 // Consume the leading - unconditionally.
858 Consume("-");
859
860 // Then, we either get a 0, or we get a nonzero. Only nonzero can be followed
861 // by a second number.
862 if (!Consume("0")) {
Austin Schuhd339a9b2019-10-05 21:33:32 -0700863 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700864 return false;
Austin Schuhd339a9b2019-10-05 21:33:32 -0700865 } else if (Char() >= '1' && Char() <= '9') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700866 // This wasn't a zero, but was a valid digit. Consume it.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700867 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700868 } else {
869 return false;
870 }
871
872 // Now consume any number of any digits.
873 while (true) {
Austin Schuhd339a9b2019-10-05 21:33:32 -0700874 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700875 data_ = original;
876 return false;
877 }
Austin Schuhd339a9b2019-10-05 21:33:32 -0700878 if (Char() < '0' || Char() > '9') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700879 break;
880 }
Austin Schuhd339a9b2019-10-05 21:33:32 -0700881 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700882 }
883 }
884
885 // We could now have a decimal.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700886 if (Char() == '.') {
887 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700888 while (true) {
Austin Schuhd339a9b2019-10-05 21:33:32 -0700889 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700890 data_ = original;
891 return false;
892 }
893 // And any number of digits.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700894 if (Char() < '0' || Char() > '9') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700895 break;
896 }
Austin Schuhd339a9b2019-10-05 21:33:32 -0700897 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700898 }
899 }
900
901 // And now an exponent.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700902 if (Char() == 'e' || Char() == 'E') {
903 ConsumeChar();
904 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700905 data_ = original;
906 return false;
907 }
908
909 // Which could have a +-
Austin Schuhd339a9b2019-10-05 21:33:32 -0700910 if (Char() == '+' || Char() == '-') {
911 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700912 }
913 int count = 0;
914 while (true) {
Austin Schuhd339a9b2019-10-05 21:33:32 -0700915 if (AtEnd()) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700916 data_ = original;
917 return false;
918 }
919 // And digits.
Austin Schuhd339a9b2019-10-05 21:33:32 -0700920 if (Char() < '0' || Char() > '9') {
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700921 break;
922 }
Austin Schuhd339a9b2019-10-05 21:33:32 -0700923 ConsumeChar();
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700924 ++count;
925 }
926 // But, it is an error to have an exponent and nothing following it.
927 if (count == 0) {
928 data_ = original;
929 return false;
930 }
931 }
932
Austin Schuhd339a9b2019-10-05 21:33:32 -0700933 *s = ::std::string(original.substr(0, original.size() - data_.size()));
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700934 return true;
935}
936
937Tokenizer::TokenType Tokenizer::Next() {
938 switch (state_) {
939 case State::kExpectObjectStart:
940 // We should always start out with a {
941 if (!Consume("{")) return TokenType::kError;
942
943 // Document that we just started an object.
944 object_type_.push_back(ObjectType::kObject);
945
946 ConsumeWhitespace();
947
Austin Schuh84314af2019-10-03 09:11:34 -0700948 if (Consume("}")) {
949 ConsumeWhitespace();
950 state_ = State::kExpectObjectEnd;
951 } else {
952 state_ = State::kExpectField;
953 }
Austin Schuh3e95e5d2019-09-20 00:08:54 -0700954 return TokenType::kStartObject;
955
956 case State::kExpectField: {
957 // Fields are built up of strings, whitespace, and then a : (followed by
958 // whitespace...)
959 ::std::string s;
960 if (!ConsumeString(&s)) {
961 fprintf(stderr, "Error on line %d, expected string for field name.\n",
962 linenumber_);
963 return TokenType::kError;
964 }
965 field_name_ = ::std::move(s);
966
967 ConsumeWhitespace();
968
969 if (!Consume(":")) {
970 fprintf(stderr, "Error on line %d\n", linenumber_);
971 return TokenType::kError;
972 }
973
974 ConsumeWhitespace();
975
976 state_ = State::kExpectValue;
977
978 return TokenType::kField;
979 } break;
980 case State::kExpectValue: {
981 TokenType result = TokenType::kError;
982
983 ::std::string s;
984 if (Consume("{")) {
985 // Fields are in objects. Record and recurse.
986 object_type_.push_back(ObjectType::kObject);
987
988 ConsumeWhitespace();
989
990 state_ = State::kExpectField;
991 return TokenType::kStartObject;
992 } else if (Consume("[")) {
993 // Values are in arrays. Record and recurse.
994 object_type_.push_back(ObjectType::kArray);
995
996 ConsumeWhitespace();
997 state_ = State::kExpectValue;
998 return TokenType::kStartArray;
999 } else if (ConsumeString(&s)) {
1000 // Parsed as a string, grab it.
1001 field_value_ = ::std::move(s);
1002 result = TokenType::kStringValue;
1003 } else if (ConsumeNumber(&s)) {
1004 // Parsed as a number, grab it.
1005 field_value_ = ::std::move(s);
1006 result = TokenType::kNumberValue;
1007 } else if (Consume("true")) {
1008 // Parsed as a true, grab it.
1009 field_value_ = "true";
1010 result = TokenType::kTrueValue;
1011 } else if (Consume("false")) {
1012 // Parsed as a false, grab it.
1013 field_value_ = "false";
1014 result = TokenType::kFalseValue;
1015 } else {
1016 // Couldn't parse, so we have a syntax error.
1017 fprintf(stderr, "Error line %d, invalid field value.\n", linenumber_);
1018 }
1019
1020 ConsumeWhitespace();
1021
1022 // After a field, we either have a , and another field (or value if we are
1023 // in an array), or we should be closing out the object (or array).
1024 if (Consume(",")) {
1025 ConsumeWhitespace();
1026 switch (object_type_.back()) {
1027 case ObjectType::kObject:
1028 state_ = State::kExpectField;
1029 break;
1030 case ObjectType::kArray:
1031 state_ = State::kExpectValue;
1032 break;
1033 }
1034 } else {
1035 // Sanity check that the stack is deep enough.
1036 if (object_type_.size() == 0) {
1037 fprintf(stderr, "Error on line %d\n", linenumber_);
1038 return TokenType::kError;
1039 }
1040
1041 // And then require closing out the object.
1042 switch (object_type_.back()) {
1043 case ObjectType::kObject:
1044 if (Consume("}")) {
1045 ConsumeWhitespace();
1046 state_ = State::kExpectObjectEnd;
1047 } else {
1048 return TokenType::kError;
1049 }
1050 break;
1051 case ObjectType::kArray:
1052 if (Consume("]")) {
1053 ConsumeWhitespace();
1054 state_ = State::kExpectArrayEnd;
1055 } else {
1056 return TokenType::kError;
1057 }
1058 break;
1059 }
1060 }
1061 return result;
1062 } break;
1063
1064 case State::kExpectArrayEnd:
1065 case State::kExpectObjectEnd: {
1066 const TokenType result = state_ == State::kExpectArrayEnd
1067 ? TokenType::kEndArray
1068 : TokenType::kEndObject;
1069 // This is a transient state so we can send 2 tokens out in a row. We
1070 // discover the object or array end at the end of reading the value.
1071 object_type_.pop_back();
1072 if (object_type_.size() == 0) {
1073 // We unwound the outer object. We should send kEnd next.
1074 state_ = State::kExpectEnd;
1075 } else if (object_type_.back() == ObjectType::kObject) {
1076 // If we are going into an object, it should either have another field
1077 // or end.
1078 if (Consume(",")) {
1079 ConsumeWhitespace();
1080 state_ = State::kExpectField;
1081 } else if (Consume("}")) {
1082 ConsumeWhitespace();
1083 state_ = State::kExpectObjectEnd;
1084 } else {
1085 return TokenType::kError;
1086 }
1087 } else if (object_type_.back() == ObjectType::kArray) {
1088 // If we are going into an array, it should either have another value
1089 // or end.
1090 if (Consume(",")) {
1091 ConsumeWhitespace();
1092 state_ = State::kExpectValue;
1093 } else if (Consume("]")) {
1094 ConsumeWhitespace();
1095 state_ = State::kExpectArrayEnd;
1096 } else {
1097 return TokenType::kError;
1098 }
1099 }
1100 // And then send out the correct token.
1101 return result;
1102 }
1103 case State::kExpectEnd:
1104 // If we are supposed to be done, confirm nothing is after the end.
1105 if (AtEnd()) {
1106 return TokenType::kEnd;
1107 } else {
1108 fprintf(stderr, "Data past end at line %d\n", linenumber_);
1109 return TokenType::kError;
1110 }
1111 }
1112 return TokenType::kError;
1113}
1114
1115bool Tokenizer::FieldAsInt(long long *value) {
1116 const char *pos = field_value().c_str();
1117 errno = 0;
1118 *value = strtoll(field_value().c_str(), const_cast<char **>(&pos), 10);
Austin Schuh84314af2019-10-03 09:11:34 -07001119 if (pos != field_value().c_str() + field_value().size() || errno != 0) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -07001120 return false;
1121 }
1122 return true;
1123}
1124
1125bool Tokenizer::FieldAsDouble(double *value) {
1126 const char *pos = field_value().c_str();
1127 errno = 0;
1128 *value = strtod(field_value().c_str(), const_cast<char **>(&pos));
1129
Austin Schuh84314af2019-10-03 09:11:34 -07001130 if (pos != field_value().c_str() + field_value().size() || errno != 0) {
Austin Schuh3e95e5d2019-09-20 00:08:54 -07001131 return false;
1132 }
1133 return true;
1134}
1135
1136} // namespace aos