1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "utils/flatbuffers/mutable.h"
18
19 #include <vector>
20
21 #include "utils/flatbuffers/reflection.h"
22 #include "utils/strings/numbers.h"
23 #include "utils/variant.h"
24 #include "flatbuffers/reflection_generated.h"
25
26 namespace libtextclassifier3 {
27 namespace {
28
Parse(const std::string & str_value,float * value)29 bool Parse(const std::string& str_value, float* value) {
30 double double_value;
31 if (!ParseDouble(str_value.data(), &double_value)) {
32 return false;
33 }
34 *value = static_cast<float>(double_value);
35 return true;
36 }
37
Parse(const std::string & str_value,double * value)38 bool Parse(const std::string& str_value, double* value) {
39 return ParseDouble(str_value.data(), value);
40 }
41
Parse(const std::string & str_value,int64 * value)42 bool Parse(const std::string& str_value, int64* value) {
43 return ParseInt64(str_value.data(), value);
44 }
45
Parse(const std::string & str_value,int32 * value)46 bool Parse(const std::string& str_value, int32* value) {
47 return ParseInt32(str_value.data(), value);
48 }
49
Parse(const std::string & str_value,std::string * value)50 bool Parse(const std::string& str_value, std::string* value) {
51 *value = str_value;
52 return true;
53 }
54
55 template <typename T>
ParseAndSetField(const reflection::Field * field,const std::string & str_value,MutableFlatbuffer * buffer)56 bool ParseAndSetField(const reflection::Field* field,
57 const std::string& str_value, MutableFlatbuffer* buffer) {
58 T value;
59 if (!Parse(str_value, &value)) {
60 TC3_LOG(ERROR) << "Could not parse '" << str_value << "'";
61 return false;
62 }
63 if (field->type()->base_type() == reflection::Vector) {
64 buffer->Repeated(field)->Add(value);
65 return true;
66 } else {
67 return buffer->Set<T>(field, value);
68 }
69 }
70
71 } // namespace
72
MutableFlatbufferBuilder(const reflection::Schema * schema,StringPiece root_type)73 MutableFlatbufferBuilder::MutableFlatbufferBuilder(
74 const reflection::Schema* schema, StringPiece root_type)
75 : schema_(schema), root_type_(TypeForName(schema, root_type)) {}
76
NewRoot() const77 std::unique_ptr<MutableFlatbuffer> MutableFlatbufferBuilder::NewRoot() const {
78 return NewTable(root_type_);
79 }
80
NewTable(StringPiece table_name) const81 std::unique_ptr<MutableFlatbuffer> MutableFlatbufferBuilder::NewTable(
82 StringPiece table_name) const {
83 return NewTable(TypeForName(schema_, table_name));
84 }
85
NewTable(const int type_id) const86 std::unique_ptr<MutableFlatbuffer> MutableFlatbufferBuilder::NewTable(
87 const int type_id) const {
88 if (type_id < 0 || type_id >= schema_->objects()->size()) {
89 TC3_LOG(ERROR) << "Invalid type id: " << type_id;
90 return nullptr;
91 }
92 return NewTable(schema_->objects()->Get(type_id));
93 }
94
NewTable(const reflection::Object * type) const95 std::unique_ptr<MutableFlatbuffer> MutableFlatbufferBuilder::NewTable(
96 const reflection::Object* type) const {
97 if (type == nullptr) {
98 return nullptr;
99 }
100 return std::make_unique<MutableFlatbuffer>(schema_, type);
101 }
102
GetFieldOrNull(const StringPiece field_name) const103 const reflection::Field* MutableFlatbuffer::GetFieldOrNull(
104 const StringPiece field_name) const {
105 return libtextclassifier3::GetFieldOrNull(type_, field_name);
106 }
107
GetFieldOrNull(const FlatbufferField * field) const108 const reflection::Field* MutableFlatbuffer::GetFieldOrNull(
109 const FlatbufferField* field) const {
110 return libtextclassifier3::GetFieldOrNull(type_, field);
111 }
112
GetFieldWithParent(const FlatbufferFieldPath * field_path,MutableFlatbuffer ** parent,reflection::Field const ** field)113 bool MutableFlatbuffer::GetFieldWithParent(
114 const FlatbufferFieldPath* field_path, MutableFlatbuffer** parent,
115 reflection::Field const** field) {
116 const auto* path = field_path->field();
117 if (path == nullptr || path->size() == 0) {
118 return false;
119 }
120
121 for (int i = 0; i < path->size(); i++) {
122 *parent = (i == 0 ? this : (*parent)->Mutable(*field));
123 if (*parent == nullptr) {
124 return false;
125 }
126 *field = (*parent)->GetFieldOrNull(path->Get(i));
127 if (*field == nullptr) {
128 return false;
129 }
130 }
131
132 return true;
133 }
134
GetFieldOrNull(const int field_offset) const135 const reflection::Field* MutableFlatbuffer::GetFieldOrNull(
136 const int field_offset) const {
137 return libtextclassifier3::GetFieldOrNull(type_, field_offset);
138 }
139
SetFromEnumValueName(const reflection::Field * field,StringPiece value_name)140 bool MutableFlatbuffer::SetFromEnumValueName(const reflection::Field* field,
141 StringPiece value_name) {
142 if (!IsEnum(field->type())) {
143 return false;
144 }
145 Variant variant_value = ParseEnumValue(schema_, field->type(), value_name);
146 if (!variant_value.HasValue()) {
147 return false;
148 }
149 fields_[field] = variant_value;
150 return true;
151 }
152
SetFromEnumValueName(StringPiece field_name,StringPiece value_name)153 bool MutableFlatbuffer::SetFromEnumValueName(StringPiece field_name,
154 StringPiece value_name) {
155 if (const reflection::Field* field = GetFieldOrNull(field_name)) {
156 return SetFromEnumValueName(field, value_name);
157 }
158 return false;
159 }
160
SetFromEnumValueName(const FlatbufferFieldPath * path,StringPiece value_name)161 bool MutableFlatbuffer::SetFromEnumValueName(const FlatbufferFieldPath* path,
162 StringPiece value_name) {
163 MutableFlatbuffer* parent;
164 const reflection::Field* field;
165 if (!GetFieldWithParent(path, &parent, &field)) {
166 return false;
167 }
168 return parent->SetFromEnumValueName(field, value_name);
169 }
170
ParseAndSet(const reflection::Field * field,const std::string & value)171 bool MutableFlatbuffer::ParseAndSet(const reflection::Field* field,
172 const std::string& value) {
173 // Try parsing as an enum value.
174 if (IsEnum(field->type()) && SetFromEnumValueName(field, value)) {
175 return true;
176 }
177 switch (field->type()->base_type() == reflection::Vector
178 ? field->type()->element()
179 : field->type()->base_type()) {
180 case reflection::String:
181 return ParseAndSetField<std::string>(field, value, this);
182 case reflection::Int:
183 return ParseAndSetField<int32>(field, value, this);
184 case reflection::Long:
185 return ParseAndSetField<int64>(field, value, this);
186 case reflection::Float:
187 return ParseAndSetField<float>(field, value, this);
188 case reflection::Double:
189 return ParseAndSetField<double>(field, value, this);
190 default:
191 TC3_LOG(ERROR) << "Unhandled field type: " << field->type()->base_type();
192 return false;
193 }
194 }
195
ParseAndSet(const FlatbufferFieldPath * path,const std::string & value)196 bool MutableFlatbuffer::ParseAndSet(const FlatbufferFieldPath* path,
197 const std::string& value) {
198 MutableFlatbuffer* parent;
199 const reflection::Field* field;
200 if (!GetFieldWithParent(path, &parent, &field)) {
201 return false;
202 }
203 return parent->ParseAndSet(field, value);
204 }
205
Add(StringPiece field_name)206 MutableFlatbuffer* MutableFlatbuffer::Add(StringPiece field_name) {
207 const reflection::Field* field = GetFieldOrNull(field_name);
208 if (field == nullptr) {
209 return nullptr;
210 }
211
212 if (field->type()->base_type() != reflection::BaseType::Vector) {
213 return nullptr;
214 }
215
216 return Add(field);
217 }
218
Add(const reflection::Field * field)219 MutableFlatbuffer* MutableFlatbuffer::Add(const reflection::Field* field) {
220 if (field == nullptr) {
221 return nullptr;
222 }
223 return Repeated(field)->Add();
224 }
225
Mutable(const StringPiece field_name)226 MutableFlatbuffer* MutableFlatbuffer::Mutable(const StringPiece field_name) {
227 if (const reflection::Field* field = GetFieldOrNull(field_name)) {
228 return Mutable(field);
229 }
230 TC3_LOG(ERROR) << "Unknown field: " << field_name.ToString();
231 return nullptr;
232 }
233
Mutable(const reflection::Field * field)234 MutableFlatbuffer* MutableFlatbuffer::Mutable(const reflection::Field* field) {
235 if (field->type()->base_type() != reflection::Obj) {
236 return nullptr;
237 }
238 const auto entry = children_.find(field);
239 if (entry != children_.end()) {
240 return entry->second.get();
241 }
242 const auto it = children_.insert(
243 /*hint=*/entry,
244 std::make_pair(
245 field,
246 std::unique_ptr<MutableFlatbuffer>(new MutableFlatbuffer(
247 schema_, schema_->objects()->Get(field->type()->index())))));
248 return it->second.get();
249 }
250
Mutable(const FlatbufferFieldPath * path)251 MutableFlatbuffer* MutableFlatbuffer::Mutable(const FlatbufferFieldPath* path) {
252 const auto* field_path = path->field();
253 if (field_path == nullptr || field_path->size() == 0) {
254 return this;
255 }
256 MutableFlatbuffer* object = this;
257 for (int i = 0; i < field_path->size(); i++) {
258 const reflection::Field* field = object->GetFieldOrNull(field_path->Get(i));
259 if (field == nullptr) {
260 return nullptr;
261 }
262 object = object->Mutable(field);
263 if (object == nullptr) {
264 return nullptr;
265 }
266 }
267 return object;
268 }
269
Repeated(StringPiece field_name)270 RepeatedField* MutableFlatbuffer::Repeated(StringPiece field_name) {
271 if (const reflection::Field* field = GetFieldOrNull(field_name)) {
272 return Repeated(field);
273 }
274 TC3_LOG(ERROR) << "Unknown field: " << field_name.ToString();
275 return nullptr;
276 }
277
Repeated(const reflection::Field * field)278 RepeatedField* MutableFlatbuffer::Repeated(const reflection::Field* field) {
279 if (field->type()->base_type() != reflection::Vector) {
280 TC3_LOG(ERROR) << "Field is not of type Vector.";
281 return nullptr;
282 }
283
284 // If the repeated field was already set, return its instance.
285 const auto entry = repeated_fields_.find(field);
286 if (entry != repeated_fields_.end()) {
287 return entry->second.get();
288 }
289
290 // Otherwise, create a new instance and store it.
291 std::unique_ptr<RepeatedField> repeated_field(
292 new RepeatedField(schema_, field));
293 const auto it = repeated_fields_.insert(
294 /*hint=*/entry, std::make_pair(field, std::move(repeated_field)));
295 return it->second.get();
296 }
297
Repeated(const FlatbufferFieldPath * path)298 RepeatedField* MutableFlatbuffer::Repeated(const FlatbufferFieldPath* path) {
299 MutableFlatbuffer* parent;
300 const reflection::Field* field;
301 if (!GetFieldWithParent(path, &parent, &field)) {
302 return nullptr;
303 }
304 return parent->Repeated(field);
305 }
306
Serialize(flatbuffers::FlatBufferBuilder * builder) const307 flatbuffers::uoffset_t MutableFlatbuffer::Serialize(
308 flatbuffers::FlatBufferBuilder* builder) const {
309 // Build all children before we can start with this table.
310 std::vector<
311 std::pair</* field vtable offset */ int,
312 /* field data offset in buffer */ flatbuffers::uoffset_t>>
313 offsets;
314 offsets.reserve(children_.size() + repeated_fields_.size());
315 for (const auto& it : children_) {
316 offsets.push_back({it.first->offset(), it.second->Serialize(builder)});
317 }
318
319 // Create strings.
320 for (const auto& it : fields_) {
321 if (it.second.Has<std::string>()) {
322 offsets.push_back(
323 {it.first->offset(),
324 builder->CreateString(it.second.ConstRefValue<std::string>()).o});
325 }
326 }
327
328 // Build the repeated fields.
329 for (const auto& it : repeated_fields_) {
330 offsets.push_back({it.first->offset(), it.second->Serialize(builder)});
331 }
332
333 // Build the table now.
334 const flatbuffers::uoffset_t table_start = builder->StartTable();
335
336 // Add scalar fields.
337 for (const auto& it : fields_) {
338 switch (it.second.GetType()) {
339 case Variant::TYPE_BOOL_VALUE:
340 builder->AddElement<uint8_t>(
341 it.first->offset(), static_cast<uint8_t>(it.second.Value<bool>()),
342 static_cast<uint8_t>(it.first->default_integer()));
343 continue;
344 case Variant::TYPE_INT8_VALUE:
345 builder->AddElement<int8_t>(
346 it.first->offset(), static_cast<int8_t>(it.second.Value<int8>()),
347 static_cast<int8_t>(it.first->default_integer()));
348 continue;
349 case Variant::TYPE_UINT8_VALUE:
350 builder->AddElement<uint8_t>(
351 it.first->offset(), static_cast<uint8_t>(it.second.Value<uint8>()),
352 static_cast<uint8_t>(it.first->default_integer()));
353 continue;
354 case Variant::TYPE_INT_VALUE:
355 builder->AddElement<int32>(
356 it.first->offset(), it.second.Value<int>(),
357 static_cast<int32>(it.first->default_integer()));
358 continue;
359 case Variant::TYPE_UINT_VALUE:
360 builder->AddElement<uint32>(
361 it.first->offset(), it.second.Value<uint>(),
362 static_cast<uint32>(it.first->default_integer()));
363 continue;
364 case Variant::TYPE_INT64_VALUE:
365 builder->AddElement<int64>(it.first->offset(), it.second.Value<int64>(),
366 it.first->default_integer());
367 continue;
368 case Variant::TYPE_UINT64_VALUE:
369 builder->AddElement<uint64>(it.first->offset(),
370 it.second.Value<uint64>(),
371 it.first->default_integer());
372 continue;
373 case Variant::TYPE_FLOAT_VALUE:
374 builder->AddElement<float>(
375 it.first->offset(), it.second.Value<float>(),
376 static_cast<float>(it.first->default_real()));
377 continue;
378 case Variant::TYPE_DOUBLE_VALUE:
379 builder->AddElement<double>(it.first->offset(),
380 it.second.Value<double>(),
381 it.first->default_real());
382 continue;
383 default:
384 continue;
385 }
386 }
387
388 // Add strings, subtables and repeated fields.
389 for (const auto& it : offsets) {
390 builder->AddOffset(it.first, flatbuffers::Offset<void>(it.second));
391 }
392
393 return builder->EndTable(table_start);
394 }
395
Serialize() const396 std::string MutableFlatbuffer::Serialize() const {
397 flatbuffers::FlatBufferBuilder builder;
398 builder.Finish(flatbuffers::Offset<void>(Serialize(&builder)));
399 return std::string(reinterpret_cast<const char*>(builder.GetBufferPointer()),
400 builder.GetSize());
401 }
402
MergeFrom(const flatbuffers::Table * from)403 bool MutableFlatbuffer::MergeFrom(const flatbuffers::Table* from) {
404 // No fields to set.
405 if (type_->fields() == nullptr) {
406 return true;
407 }
408
409 for (const reflection::Field* field : *type_->fields()) {
410 // Skip fields that are not explicitly set.
411 if (!from->CheckField(field->offset())) {
412 continue;
413 }
414 const reflection::BaseType type = field->type()->base_type();
415 switch (type) {
416 case reflection::Bool:
417 Set<bool>(field, from->GetField<uint8_t>(field->offset(),
418 field->default_integer()));
419 break;
420 case reflection::Byte:
421 Set<int8_t>(field, from->GetField<int8_t>(field->offset(),
422 field->default_integer()));
423 break;
424 case reflection::UByte:
425 Set<uint8_t>(field, from->GetField<uint8_t>(field->offset(),
426 field->default_integer()));
427 break;
428 case reflection::Int:
429 Set<int32>(field, from->GetField<int32>(field->offset(),
430 field->default_integer()));
431 break;
432 case reflection::UInt:
433 Set<uint32>(field, from->GetField<uint32>(field->offset(),
434 field->default_integer()));
435 break;
436 case reflection::Long:
437 Set<int64>(field, from->GetField<int64>(field->offset(),
438 field->default_integer()));
439 break;
440 case reflection::ULong:
441 Set<uint64>(field, from->GetField<uint64>(field->offset(),
442 field->default_integer()));
443 break;
444 case reflection::Float:
445 Set<float>(field, from->GetField<float>(field->offset(),
446 field->default_real()));
447 break;
448 case reflection::Double:
449 Set<double>(field, from->GetField<double>(field->offset(),
450 field->default_real()));
451 break;
452 case reflection::String:
453 Set<std::string>(
454 field, from->GetPointer<const flatbuffers::String*>(field->offset())
455 ->str());
456 break;
457 case reflection::Obj:
458 if (MutableFlatbuffer* nested_field = Mutable(field);
459 nested_field == nullptr ||
460 !nested_field->MergeFrom(
461 from->GetPointer<const flatbuffers::Table* const>(
462 field->offset()))) {
463 return false;
464 }
465 break;
466 case reflection::Vector: {
467 if (RepeatedField* repeated_field = Repeated(field);
468 repeated_field == nullptr || !repeated_field->Extend(from)) {
469 return false;
470 }
471 break;
472 }
473 default:
474 TC3_LOG(ERROR) << "Unsupported type: " << type
475 << " for field: " << field->name()->str();
476 return false;
477 }
478 }
479 return true;
480 }
481
MergeFromSerializedFlatbuffer(StringPiece from)482 bool MutableFlatbuffer::MergeFromSerializedFlatbuffer(StringPiece from) {
483 return MergeFrom(flatbuffers::GetAnyRoot(
484 reinterpret_cast<const unsigned char*>(from.data())));
485 }
486
AsFlatMap(const std::string & key_separator,const std::string & key_prefix,std::map<std::string,Variant> * result) const487 void MutableFlatbuffer::AsFlatMap(
488 const std::string& key_separator, const std::string& key_prefix,
489 std::map<std::string, Variant>* result) const {
490 // Add direct fields.
491 for (const auto& it : fields_) {
492 (*result)[key_prefix + it.first->name()->str()] = it.second;
493 }
494
495 // Add nested messages.
496 for (const auto& it : children_) {
497 it.second->AsFlatMap(key_separator,
498 key_prefix + it.first->name()->str() + key_separator,
499 result);
500 }
501 }
502
ToTextProto() const503 std::string RepeatedField::ToTextProto() const {
504 std::string result = " [";
505 std::string current_field_separator;
506 for (int index = 0; index < Size(); index++) {
507 if (is_primitive_) {
508 result.append(current_field_separator + items_.at(index).ToString());
509 } else {
510 result.append(current_field_separator + "{" +
511 Get<MutableFlatbuffer*>(index)->ToTextProto() + "}");
512 }
513 current_field_separator = ", ";
514 }
515 result.append("] ");
516 return result;
517 }
518
ToTextProto() const519 std::string MutableFlatbuffer::ToTextProto() const {
520 std::string result;
521 std::string current_field_separator;
522 // Add direct fields.
523 for (const auto& field_value_pair : fields_) {
524 const std::string field_name = field_value_pair.first->name()->str();
525 const Variant& value = field_value_pair.second;
526 std::string quotes;
527 if (value.GetType() == Variant::TYPE_STRING_VALUE) {
528 quotes = "'";
529 }
530 result.append(current_field_separator + field_name + ": " + quotes +
531 value.ToString() + quotes);
532 current_field_separator = ", ";
533 }
534
535 // Add repeated message
536 for (const auto& repeated_fb_pair : repeated_fields_) {
537 result.append(current_field_separator +
538 repeated_fb_pair.first->name()->c_str() + ": " +
539 repeated_fb_pair.second->ToTextProto());
540 current_field_separator = ", ";
541 }
542
543 // Add nested messages.
544 for (const auto& field_flatbuffer_pair : children_) {
545 const std::string field_name = field_flatbuffer_pair.first->name()->str();
546 result.append(current_field_separator + field_name + " {" +
547 field_flatbuffer_pair.second->ToTextProto() + "}");
548 current_field_separator = ", ";
549 }
550
551 return result;
552 }
553
554 //
555 // Repeated field methods.
556 //
557
Add()558 MutableFlatbuffer* RepeatedField::Add() {
559 if (is_primitive_) {
560 TC3_LOG(ERROR) << "Trying to add sub-message on a primitive-typed field.";
561 return nullptr;
562 }
563
564 object_items_.emplace_back(new MutableFlatbuffer(
565 schema_, schema_->objects()->Get(field_->type()->index())));
566 return object_items_.back().get();
567 }
568
569 namespace {
570
571 template <typename T>
TypedSerialize(const std::vector<Variant> & values,flatbuffers::FlatBufferBuilder * builder)572 flatbuffers::uoffset_t TypedSerialize(const std::vector<Variant>& values,
573 flatbuffers::FlatBufferBuilder* builder) {
574 std::vector<T> typed_values;
575 typed_values.reserve(values.size());
576 for (const Variant& item : values) {
577 typed_values.push_back(item.Value<T>());
578 }
579 return builder->CreateVector(typed_values).o;
580 }
581
582 } // namespace
583
Extend(const flatbuffers::Table * from)584 bool RepeatedField::Extend(const flatbuffers::Table* from) {
585 switch (field_->type()->element()) {
586 case reflection::Int:
587 AppendFromVector<int32>(from);
588 return true;
589 case reflection::UInt:
590 AppendFromVector<uint>(from);
591 return true;
592 case reflection::Long:
593 AppendFromVector<int64>(from);
594 return true;
595 case reflection::ULong:
596 AppendFromVector<uint64>(from);
597 return true;
598 case reflection::Byte:
599 AppendFromVector<int8_t>(from);
600 return true;
601 case reflection::UByte:
602 AppendFromVector<uint8_t>(from);
603 return true;
604 case reflection::String:
605 AppendFromVector<std::string>(from);
606 return true;
607 case reflection::Obj:
608 AppendFromVector<MutableFlatbuffer>(from);
609 return true;
610 case reflection::Double:
611 AppendFromVector<double>(from);
612 return true;
613 case reflection::Float:
614 AppendFromVector<float>(from);
615 return true;
616 default:
617 TC3_LOG(ERROR) << "Repeated unsupported type: "
618 << field_->type()->element()
619 << " for field: " << field_->name()->str();
620 return false;
621 }
622 }
623
Serialize(flatbuffers::FlatBufferBuilder * builder) const624 flatbuffers::uoffset_t RepeatedField::Serialize(
625 flatbuffers::FlatBufferBuilder* builder) const {
626 switch (field_->type()->element()) {
627 case reflection::String:
628 return SerializeString(builder);
629 break;
630 case reflection::Obj:
631 return SerializeObject(builder);
632 break;
633 case reflection::Bool:
634 return TypedSerialize<bool>(items_, builder);
635 break;
636 case reflection::Byte:
637 return TypedSerialize<int8_t>(items_, builder);
638 break;
639 case reflection::UByte:
640 return TypedSerialize<uint8_t>(items_, builder);
641 break;
642 case reflection::Int:
643 return TypedSerialize<int>(items_, builder);
644 break;
645 case reflection::UInt:
646 return TypedSerialize<uint>(items_, builder);
647 break;
648 case reflection::Long:
649 return TypedSerialize<int64>(items_, builder);
650 break;
651 case reflection::ULong:
652 return TypedSerialize<uint64>(items_, builder);
653 break;
654 case reflection::Float:
655 return TypedSerialize<float>(items_, builder);
656 break;
657 case reflection::Double:
658 return TypedSerialize<double>(items_, builder);
659 break;
660 default:
661 TC3_LOG(FATAL) << "Unsupported type: " << field_->type()->element();
662 break;
663 }
664 TC3_LOG(FATAL) << "Invalid state.";
665 return 0;
666 }
667
SerializeString(flatbuffers::FlatBufferBuilder * builder) const668 flatbuffers::uoffset_t RepeatedField::SerializeString(
669 flatbuffers::FlatBufferBuilder* builder) const {
670 std::vector<flatbuffers::Offset<flatbuffers::String>> offsets(items_.size());
671 for (int i = 0; i < items_.size(); i++) {
672 offsets[i] = builder->CreateString(items_[i].ConstRefValue<std::string>());
673 }
674 return builder->CreateVector(offsets).o;
675 }
676
SerializeObject(flatbuffers::FlatBufferBuilder * builder) const677 flatbuffers::uoffset_t RepeatedField::SerializeObject(
678 flatbuffers::FlatBufferBuilder* builder) const {
679 std::vector<flatbuffers::Offset<void>> offsets(object_items_.size());
680 for (int i = 0; i < object_items_.size(); i++) {
681 offsets[i] = object_items_[i]->Serialize(builder);
682 }
683 return builder->CreateVector(offsets).o;
684 }
685
686 } // namespace libtextclassifier3
687