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