1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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 <thrift/lib/cpp2/protocol/TableBasedSerializer.h>
18 
19 #include <glog/logging.h>
20 #include <folly/CppAttributes.h>
21 #include <folly/Range.h>
22 #include <thrift/lib/cpp2/protocol/ProtocolReaderStructReadState.h>
23 #include <thrift/lib/cpp2/protocol/ProtocolReaderWireTypeInfo.h>
24 
25 namespace apache {
26 namespace thrift {
27 namespace detail {
28 
29 constexpr TypeInfo kStopType = {
30     protocol::TType::T_STOP, nullptr, nullptr, nullptr};
31 constexpr FieldInfo kStopMarker = {0, false, nullptr, 0, 0, &kStopType};
32 
33 template <class Protocol_>
skip(Protocol_ * iprot,ProtocolReaderStructReadState<Protocol_> & readState)34 void skip(
35     Protocol_* iprot, ProtocolReaderStructReadState<Protocol_>& readState) {
36   readState.skip(iprot);
37   readState.readFieldEnd(iprot);
38   readState.readFieldBeginNoInline(iprot);
39 }
40 
getMember(const FieldInfo & fieldInfo,const void * object)41 inline const void* getMember(const FieldInfo& fieldInfo, const void* object) {
42   return static_cast<const char*>(object) + fieldInfo.memberOffset;
43 }
44 
getMember(const FieldInfo & fieldInfo,void * object)45 inline void* getMember(const FieldInfo& fieldInfo, void* object) {
46   return static_cast<char*>(object) + fieldInfo.memberOffset;
47 }
48 
getValue(const TypeInfo & typeInfo,const void * object)49 inline OptionalThriftValue getValue(
50     const TypeInfo& typeInfo, const void* object) {
51   if (typeInfo.get) {
52     return typeInfo.get(object);
53   }
54   return object ? folly::make_optional<ThriftValue>(object) : folly::none;
55 }
56 
invokeSet(VoidFuncPtr set,void * object)57 FOLLY_ERASE void* invokeSet(VoidFuncPtr set, void* object) {
58   return reinterpret_cast<void* (*)(void*)>(set)(object);
59 }
60 
invokeStructSet(const TypeInfo & info,void * object)61 FOLLY_ERASE void* invokeStructSet(const TypeInfo& info, void* object) {
62   return reinterpret_cast<void* (*)(void*, const TypeInfo&)>(info.set)(
63       object, info);
64 }
65 
66 template <class Protocol_>
findFieldInfo(Protocol_ * iprot,ProtocolReaderStructReadState<Protocol_> & readState,const StructInfo & structInfo)67 const FieldInfo* FOLLY_NULLABLE findFieldInfo(
68     Protocol_* iprot,
69     ProtocolReaderStructReadState<Protocol_>& readState,
70     const StructInfo& structInfo) {
71   auto* end = structInfo.fieldInfos + structInfo.numFields;
72   if (iprot->kUsesFieldNames()) {
73     const FieldInfo* found =
74         std::find_if(structInfo.fieldInfos, end, [&](const FieldInfo& val) {
75           return val.name == readState.fieldName();
76         });
77     if (found != end) {
78       readState.fieldId = found->id;
79       readState.fieldType = found->typeInfo->type;
80       if (readState.isCompatibleWithType(iprot, found->typeInfo->type)) {
81         return found;
82       }
83     }
84   } else {
85     const FieldInfo* found = std::lower_bound(
86         structInfo.fieldInfos,
87         end,
88         readState.fieldId,
89         [](const FieldInfo& lhs, FieldID rhs) { return lhs.id < rhs; });
90     if (found != end && found->id == readState.fieldId &&
91         readState.isCompatibleWithType(iprot, found->typeInfo->type)) {
92       return found;
93     }
94   }
95   return nullptr;
96 }
97 
98 // Returns active field id for a Thrift union object.
getActiveId(const void * object,const StructInfo & info)99 inline int getActiveId(const void* object, const StructInfo& info) {
100   auto getActiveIdFunc = info.unionExt->getActiveId;
101   if (getActiveIdFunc != nullptr) {
102     return getActiveIdFunc(object);
103   }
104   return *reinterpret_cast<const int*>(
105       static_cast<const char*>(object) + info.unionExt->unionTypeOffset);
106 }
107 
108 // Sets the active field id for a Thrift union object.
setActiveId(void * object,const StructInfo & info,int value)109 inline void setActiveId(void* object, const StructInfo& info, int value) {
110   auto setActiveIdFunc = info.unionExt->setActiveId;
111   if (setActiveIdFunc != nullptr) {
112     setActiveIdFunc(object, value);
113   }
114   *reinterpret_cast<int*>(
115       static_cast<char*>(object) + info.unionExt->unionTypeOffset) = value;
116 }
117 
isFieldSet(const void * object,const FieldInfo & info,const StructInfo & structInfo)118 inline bool isFieldSet(
119     const void* object, const FieldInfo& info, const StructInfo& structInfo) {
120   if (structInfo.getIsset != nullptr) {
121     return structInfo.getIsset(object, info.issetOffset);
122   }
123 
124   if (info.issetOffset == 0) {
125     return true; // return true for union fields
126   }
127   return *reinterpret_cast<const bool*>(
128       static_cast<const char*>(object) + info.issetOffset);
129 }
130 
markFieldAsSet(void * object,const FieldInfo & info,const StructInfo & structInfo)131 inline void markFieldAsSet(
132     void* object, const FieldInfo& info, const StructInfo& structInfo) {
133   if (structInfo.setIsset != nullptr) {
134     structInfo.setIsset(object, info.issetOffset, true);
135     return;
136   }
137   if (info.issetOffset == 0) {
138     return;
139   }
140   *reinterpret_cast<bool*>(static_cast<char*>(object) + info.issetOffset) =
141       true;
142 }
143 
144 template <class Protocol_>
read(Protocol_ * iprot,const TypeInfo & typeInfo,ProtocolReaderStructReadState<Protocol_> & readState,void * object)145 void read(
146     Protocol_* iprot,
147     const TypeInfo& typeInfo,
148     ProtocolReaderStructReadState<Protocol_>& readState,
149     void* object) {
150   using WireTypeInfo = ProtocolReaderWireTypeInfo<Protocol_>;
151   using WireType = typename WireTypeInfo::WireType;
152   switch (typeInfo.type) {
153     case protocol::TType::T_STRUCT:
154       readState.beforeSubobject(iprot);
155       read<Protocol_>(
156           iprot,
157           *static_cast<const StructInfo*>(typeInfo.typeExt),
158           typeInfo.set ? invokeStructSet(typeInfo, object) : object);
159       readState.afterSubobject(iprot);
160       break;
161     case protocol::TType::T_I64: {
162       std::int64_t temp;
163       iprot->readI64(temp);
164       reinterpret_cast<void (*)(void*, std::int64_t)>(typeInfo.set)(
165           object, temp);
166       break;
167     }
168     case protocol::TType::T_I32: {
169       std::int32_t temp;
170       iprot->readI32(temp);
171       reinterpret_cast<void (*)(void*, std::int32_t)>(typeInfo.set)(
172           object, temp);
173       break;
174     }
175     case protocol::TType::T_I16: {
176       std::int16_t temp;
177       iprot->readI16(temp);
178       reinterpret_cast<void (*)(void*, std::int16_t)>(typeInfo.set)(
179           object, temp);
180       break;
181     }
182     case protocol::TType::T_BYTE: {
183       std::int8_t temp;
184       iprot->readByte(temp);
185       reinterpret_cast<void (*)(void*, std::int8_t)>(typeInfo.set)(
186           object, temp);
187       break;
188     }
189     case protocol::TType::T_BOOL: {
190       bool temp;
191       iprot->readBool(temp);
192       reinterpret_cast<void (*)(void*, bool)>(typeInfo.set)(object, temp);
193       break;
194     }
195     case protocol::TType::T_DOUBLE: {
196       double temp;
197       iprot->readDouble(temp);
198       reinterpret_cast<void (*)(void*, double)>(typeInfo.set)(object, temp);
199       break;
200     }
201     case protocol::TType::T_FLOAT: {
202       float temp;
203       iprot->readFloat(temp);
204       reinterpret_cast<void (*)(void*, float)>(typeInfo.set)(object, temp);
205       break;
206     }
207     case protocol::TType::T_STRING: {
208       switch (*static_cast<const StringFieldType*>(typeInfo.typeExt)) {
209         case StringFieldType::String:
210           iprot->readString(*static_cast<std::string*>(object));
211           break;
212         case StringFieldType::StringView: {
213           std::string temp;
214           iprot->readBinary(temp);
215           reinterpret_cast<void (*)(void*, const std::string&)>(typeInfo.set)(
216               object, temp);
217           break;
218         }
219         case StringFieldType::Binary:
220           iprot->readBinary(*static_cast<std::string*>(object));
221           break;
222         case StringFieldType::IOBufObj: {
223           const OptionalThriftValue value = getValue(typeInfo, object);
224           if (value.hasValue()) {
225             iprot->readBinary(*value.value().iobuf);
226           } else {
227             folly::IOBuf temp;
228             iprot->readBinary(temp);
229             reinterpret_cast<void (*)(void*, const folly::IOBuf&)>(
230                 typeInfo.set)(object, temp);
231           }
232           break;
233         }
234         case StringFieldType::IOBuf:
235           iprot->readBinary(*static_cast<folly::IOBuf*>(object));
236           break;
237         case StringFieldType::IOBufPtr:
238           iprot->readBinary(
239               *static_cast<std::unique_ptr<folly::IOBuf>*>(object));
240           break;
241       }
242       break;
243     }
244     case protocol::TType::T_MAP: {
245       readState.beforeSubobject(iprot);
246       // Initialize the container to clear out current values.
247       auto* actualObject = invokeSet(typeInfo.set, object);
248       const MapFieldExt& ext =
249           *static_cast<const MapFieldExt*>(typeInfo.typeExt);
250       std::uint32_t size = ~0;
251       WireType reportedKeyType = WireTypeInfo::defaultValue();
252       WireType reportedMappedType = WireTypeInfo::defaultValue();
253       iprot->readMapBegin(reportedKeyType, reportedMappedType, size);
254       struct Context {
255         const TypeInfo* keyInfo;
256         const TypeInfo* valInfo;
257         Protocol_* iprot;
258         ProtocolReaderStructReadState<Protocol_>& readState;
259       };
260       const Context context = {
261           ext.keyInfo,
262           ext.valInfo,
263           iprot,
264           readState,
265       };
266       auto const keyReader = [](const void* context, void* key) {
267         const auto& typedContext = *static_cast<const Context*>(context);
268         read(
269             typedContext.iprot,
270             *typedContext.keyInfo,
271             typedContext.readState,
272             key);
273       };
274       auto const valueReader = [](const void* context, void* val) {
275         const auto& typedContext = *static_cast<const Context*>(context);
276         read(
277             typedContext.iprot,
278             *typedContext.valInfo,
279             typedContext.readState,
280             val);
281       };
282       if (iprot->kOmitsContainerSizes()) {
283         while (iprot->peekMap()) {
284           ext.consumeElem(&context, actualObject, keyReader, valueReader);
285         }
286       } else {
287         if (size > 0 &&
288             (ext.keyInfo->type != reportedKeyType ||
289              ext.valInfo->type != reportedMappedType)) {
290           skip_n(*iprot, size, {reportedKeyType, reportedMappedType});
291         } else {
292           if (!canReadNElements(
293                   *iprot, size, {reportedKeyType, reportedMappedType})) {
294             protocol::TProtocolException::throwTruncatedData();
295           }
296           ext.readMap(&context, actualObject, size, keyReader, valueReader);
297         }
298       }
299       iprot->readMapEnd();
300       readState.afterSubobject(iprot);
301       break;
302     }
303     case protocol::TType::T_SET: {
304       readState.beforeSubobject(iprot);
305       // Initialize the container to clear out current values.
306       auto* actualObject = invokeSet(typeInfo.set, object);
307       const SetFieldExt& ext =
308           *static_cast<const SetFieldExt*>(typeInfo.typeExt);
309       std::uint32_t size = ~0;
310       WireType reportedType = WireTypeInfo::defaultValue();
311       iprot->readSetBegin(reportedType, size);
312       struct Context {
313         const TypeInfo* valInfo;
314         Protocol_* iprot;
315         ProtocolReaderStructReadState<Protocol_>& readState;
316       };
317       const Context context = {
318           ext.valInfo,
319           iprot,
320           readState,
321       };
322       auto const reader = [](const void* context, void* value) {
323         const auto& typedContext = *static_cast<const Context*>(context);
324         read(
325             typedContext.iprot,
326             *typedContext.valInfo,
327             typedContext.readState,
328             value);
329       };
330       if (iprot->kOmitsContainerSizes()) {
331         while (iprot->peekSet()) {
332           ext.consumeElem(&context, actualObject, reader);
333         }
334       } else {
335         if (reportedType != ext.valInfo->type) {
336           skip_n(*iprot, size, {reportedType});
337         } else {
338           if (!canReadNElements(*iprot, size, {reportedType})) {
339             protocol::TProtocolException::throwTruncatedData();
340           }
341           ext.readSet(&context, actualObject, size, reader);
342         }
343       }
344       iprot->readSetEnd();
345       readState.afterSubobject(iprot);
346       break;
347     }
348     case protocol::TType::T_LIST: {
349       readState.beforeSubobject(iprot);
350       // Initialize the container to clear out current values.
351       auto* actualObject = invokeSet(typeInfo.set, object);
352       const ListFieldExt& ext =
353           *static_cast<const ListFieldExt*>(typeInfo.typeExt);
354       std::uint32_t size = ~0;
355       WireType reportedType = WireTypeInfo::defaultValue();
356 
357       iprot->readListBegin(reportedType, size);
358       struct Context {
359         const TypeInfo* valInfo;
360         Protocol_* iprot;
361         ProtocolReaderStructReadState<Protocol_>& readState;
362       };
363       const Context context = {
364           ext.valInfo,
365           iprot,
366           readState,
367       };
368       auto const reader = [](const void* context, void* value) {
369         const auto& typedContext = *static_cast<const Context*>(context);
370         read(
371             typedContext.iprot,
372             *typedContext.valInfo,
373             typedContext.readState,
374             value);
375       };
376       if (iprot->kOmitsContainerSizes()) {
377         while (iprot->peekList()) {
378           ext.consumeElem(&context, actualObject, reader);
379         }
380       } else {
381         if (reportedType != ext.valInfo->type) {
382           skip_n(*iprot, size, {reportedType});
383         } else {
384           if (!canReadNElements(*iprot, size, {reportedType})) {
385             protocol::TProtocolException::throwTruncatedData();
386           }
387           ext.readList(&context, actualObject, size, reader);
388         }
389       }
390       iprot->readListEnd();
391       readState.afterSubobject(iprot);
392       break;
393     }
394     case protocol::TType::T_STOP:
395     case protocol::TType::T_VOID:
396     case protocol::TType::T_UTF8:
397     case protocol::TType::T_U64:
398     case protocol::TType::T_UTF16:
399     case protocol::TType::T_STREAM:
400       skip(iprot, readState);
401   }
402 }
403 
404 template <class Protocol_>
write(Protocol_ * iprot,const TypeInfo & typeInfo,ThriftValue value)405 size_t write(Protocol_* iprot, const TypeInfo& typeInfo, ThriftValue value) {
406   switch (typeInfo.type) {
407     case protocol::TType::T_STRUCT:
408       return write(
409           iprot,
410           *static_cast<const StructInfo*>(typeInfo.typeExt),
411           value.object);
412     case protocol::TType::T_I64:
413       return iprot->writeI64(value.int64Value);
414     case protocol::TType::T_I32:
415       return iprot->writeI32(value.int32Value);
416     case protocol::TType::T_I16:
417       return iprot->writeI16(value.int16Value);
418     case protocol::TType::T_BYTE:
419       return iprot->writeByte(value.int8Value);
420     case protocol::TType::T_BOOL:
421       return iprot->writeBool(value.boolValue);
422     case protocol::TType::T_DOUBLE:
423       return iprot->writeDouble(value.doubleValue);
424     case protocol::TType::T_FLOAT:
425       return iprot->writeFloat(value.floatValue);
426     case protocol::TType::T_STRING: {
427       switch (*static_cast<const StringFieldType*>(typeInfo.typeExt)) {
428         case StringFieldType::String:
429           return iprot->writeString(
430               *static_cast<const std::string*>(value.object));
431         case StringFieldType::StringView:
432           return iprot->writeBinary(
433               static_cast<const folly::StringPiece>(value.stringViewValue));
434         case StringFieldType::Binary:
435           return iprot->writeBinary(
436               *static_cast<const std::string*>(value.object));
437         case StringFieldType::IOBufObj:
438           return iprot->writeBinary(
439               *static_cast<const folly::IOBuf*>(value.iobuf));
440         case StringFieldType::IOBuf:
441           return iprot->writeBinary(
442               *static_cast<const folly::IOBuf*>(value.object));
443         case StringFieldType::IOBufPtr:
444           return iprot->writeBinary(
445               *static_cast<const std::unique_ptr<folly::IOBuf>*>(value.object));
446       };
447     }
448       // For container types, when recursively writing with lambdas we
449       // intentionally skip checking OptionalThriftValue.hasValue and treat it
450       // as a user error if the value is a nullptr.
451     case protocol::TType::T_MAP: {
452       const auto& ext = *static_cast<const MapFieldExt*>(typeInfo.typeExt);
453       size_t written = iprot->writeMapBegin(
454           ext.keyInfo->type, ext.valInfo->type, ext.size(value.object));
455 
456       struct Context {
457         const TypeInfo* keyInfo;
458         const TypeInfo* valInfo;
459         Protocol_* iprot;
460       };
461       const Context context = {
462           ext.keyInfo,
463           ext.valInfo,
464           iprot,
465       };
466       written += ext.writeMap(
467           &context,
468           value.object,
469           iprot->kSortKeys(),
470           [](const void* context, const void* key, const void* val) {
471             const auto& typedContext = *static_cast<const Context*>(context);
472             const TypeInfo& keyInfo = *typedContext.keyInfo;
473             const TypeInfo& valInfo = *typedContext.valInfo;
474             return write(typedContext.iprot, keyInfo, *getValue(keyInfo, key)) +
475                 write(typedContext.iprot,
476                       *typedContext.valInfo,
477                       *getValue(valInfo, val));
478           });
479       written += iprot->writeMapEnd();
480       return written;
481     }
482     case protocol::TType::T_SET: {
483       const auto& ext = *static_cast<const SetFieldExt*>(typeInfo.typeExt);
484       size_t written =
485           iprot->writeSetBegin(ext.valInfo->type, ext.size(value.object));
486 
487       struct Context {
488         const TypeInfo* valInfo;
489         Protocol_* iprot;
490       };
491       const Context context = {
492           ext.valInfo,
493           iprot,
494       };
495       written += ext.writeSet(
496           &context,
497           value.object,
498           iprot->kSortKeys(),
499           [](const void* context, const void* value) {
500             const auto& typedContext = *static_cast<const Context*>(context);
501             const TypeInfo& valInfo = *typedContext.valInfo;
502             return write(
503                 typedContext.iprot, valInfo, *getValue(valInfo, value));
504           });
505       written += iprot->writeSetEnd();
506       return written;
507     }
508     case protocol::TType::T_LIST: {
509       const auto& ext = *static_cast<const ListFieldExt*>(typeInfo.typeExt);
510       size_t written =
511           iprot->writeListBegin(ext.valInfo->type, ext.size(value.object));
512 
513       struct Context {
514         const TypeInfo* valInfo;
515         Protocol_* iprot;
516       };
517       const Context context = {
518           ext.valInfo,
519           iprot,
520       };
521       written += ext.writeList(
522           &context, value.object, [](const void* context, const void* value) {
523             const auto& typedContext = *static_cast<const Context*>(context);
524             const TypeInfo& valInfo = *typedContext.valInfo;
525             return write(
526                 typedContext.iprot, valInfo, *getValue(valInfo, value));
527           });
528       written += iprot->writeListEnd();
529       return written;
530     }
531     case protocol::TType::T_STOP:
532     case protocol::TType::T_VOID:
533     case protocol::TType::T_STREAM:
534     case protocol::TType::T_UTF8:
535     case protocol::TType::T_U64:
536     case protocol::TType::T_UTF16:
537       DCHECK(false);
538       break;
539   }
540   return 0;
541 }
542 
543 template <class Protocol_>
writeField(Protocol_ * iprot,const FieldInfo & fieldInfo,const ThriftValue & value)544 size_t writeField(
545     Protocol_* iprot, const FieldInfo& fieldInfo, const ThriftValue& value) {
546   size_t written = iprot->writeFieldBegin(
547       fieldInfo.name, fieldInfo.typeInfo->type, fieldInfo.id);
548   written += write(iprot, *fieldInfo.typeInfo, value);
549   written += iprot->writeFieldEnd();
550   return written;
551 }
552 
553 template <class Protocol_>
read(Protocol_ * iprot,const StructInfo & structInfo,void * object)554 void read(Protocol_* iprot, const StructInfo& structInfo, void* object) {
555   DCHECK(object);
556   ProtocolReaderStructReadState<Protocol_> readState;
557   readState.readStructBegin(iprot);
558 
559   if (UNLIKELY(structInfo.unionExt != nullptr)) {
560     readState.fieldId = 0;
561     readState.readFieldBegin(iprot);
562     if (readState.atStop()) {
563       structInfo.unionExt->clear(object);
564       readState.readStructEnd(iprot);
565       return;
566     }
567     if (const auto* fieldInfo = findFieldInfo(iprot, readState, structInfo)) {
568       if (getActiveId(object, structInfo) != 0) {
569         structInfo.unionExt->clear(object);
570       }
571       void* value = getMember(*fieldInfo, object);
572       if (structInfo.unionExt->initMember[0] != nullptr) {
573         structInfo.unionExt->initMember[fieldInfo - structInfo.fieldInfos](
574             value);
575       }
576       read(iprot, *fieldInfo->typeInfo, readState, value);
577       setActiveId(object, structInfo, fieldInfo->id);
578     } else {
579       skip(iprot, readState);
580     }
581     readState.readFieldEnd(iprot);
582     readState.readFieldBegin(iprot);
583     if (UNLIKELY(!readState.atStop())) {
584       TProtocolException::throwUnionMissingStop();
585     }
586     readState.readStructEnd(iprot);
587     return;
588   }
589 
590   // Define out of loop to call advanceToNextField after the loop ends.
591   FieldID prevFieldId = 0;
592 
593   // The index of the expected field in the struct layout.
594   std::int16_t index = 0;
595 
596   // Every time advanceToNextField reports a field mismatch, either because the
597   // field is missing or if the serialized fields are not sorted (protocols
598   // don't guarantee a specific field order), we search for the field info
599   // matching the read bytes. Then we resume from the one past the found field
600   // to reduce the number of scans we have to do if the fields are sorted which
601   // is a common case. When we increment index past the number of fields we
602   // utilize the same search logic with a field info of type TType::T_STOP.
603   for (;; ++index) {
604     auto* fieldInfo = index < structInfo.numFields
605         ? &structInfo.fieldInfos[index]
606         : &kStopMarker;
607     // Try to match the next field in order against the current bytes.
608     if (UNLIKELY(!readState.advanceToNextField(
609             iprot, prevFieldId, fieldInfo->id, fieldInfo->typeInfo->type))) {
610       // Loop to skip until we find a match for both field id/name and type.
611       for (;;) {
612         readState.afterAdvanceFailure(iprot);
613         if (readState.atStop()) {
614           // Already at stop, return immediately.
615           readState.readStructEnd(iprot);
616           return;
617         }
618         fieldInfo = findFieldInfo(iprot, readState, structInfo);
619         // Found it.
620         if (fieldInfo) {
621           // Set the index to the field next in order to the found field.
622           index = fieldInfo - structInfo.fieldInfos;
623           break;
624         }
625         skip(iprot, readState);
626       }
627     } else if (UNLIKELY(index >= structInfo.numFields)) {
628       // We are at stop and have tried all of the fields, so return.
629       readState.readStructEnd(iprot);
630       return;
631     }
632     // Id and type are what we expect, try read.
633     prevFieldId = fieldInfo->id;
634     read(iprot, *fieldInfo->typeInfo, readState, getMember(*fieldInfo, object));
635     markFieldAsSet(object, *fieldInfo, structInfo);
636   }
637 }
638 
639 template <class Protocol_>
write(Protocol_ * iprot,const StructInfo & structInfo,const void * object)640 size_t write(
641     Protocol_* iprot, const StructInfo& structInfo, const void* object) {
642   DCHECK(object);
643   size_t written = iprot->writeStructBegin(structInfo.name);
644   if (UNLIKELY(structInfo.unionExt != nullptr)) {
645     const FieldInfo* end = structInfo.fieldInfos + structInfo.numFields;
646     int activeId = getActiveId(object, structInfo);
647     const FieldInfo* found = std::lower_bound(
648         structInfo.fieldInfos,
649         end,
650         activeId,
651         [](const FieldInfo& lhs, FieldID rhs) { return lhs.id < rhs; });
652     if (found < end && found->id == activeId) {
653       const OptionalThriftValue value =
654           getValue(*found->typeInfo, getMember(*found, object));
655       if (value.hasValue()) {
656         written += writeField(iprot, *found, value.value());
657       } else if (found->typeInfo->type == protocol::TType::T_STRUCT) {
658         written += iprot->writeFieldBegin(
659             found->name, found->typeInfo->type, found->id);
660         written += iprot->writeStructBegin(found->name);
661         written += iprot->writeStructEnd();
662         written += iprot->writeFieldStop();
663         written += iprot->writeFieldEnd();
664       }
665     }
666   } else {
667     for (std::int16_t index = 0; index < structInfo.numFields; index++) {
668       const auto& fieldInfo = structInfo.fieldInfos[index];
669       if (fieldInfo.isUnqualified ||
670           isFieldSet(object, fieldInfo, structInfo)) {
671         if (OptionalThriftValue value =
672                 getValue(*fieldInfo.typeInfo, getMember(fieldInfo, object))) {
673           written += writeField(iprot, fieldInfo, value.value());
674         }
675       }
676     }
677   }
678 
679   written += iprot->writeFieldStop();
680   written += iprot->writeStructEnd();
681   return written;
682 }
683 } // namespace detail
684 } // namespace thrift
685 } // namespace apache
686