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 #pragma once
18 
19 #include <memory>
20 #include <type_traits>
21 
22 #include <thrift/lib/cpp2/Thrift.h>
23 #include <thrift/lib/cpp2/protocol/Traits.h>
24 #include <thrift/lib/cpp2/protocol/detail/protocol_methods.h>
25 #include <thrift/lib/cpp2/reflection/reflection.h>
26 
27 namespace apache {
28 namespace thrift {
29 namespace detail {
30 
31 // is_smart_pointer is a helper for determining if a type is a supported
32 // pointer type for cpp2.ref fields, while discrimiminating against the
33 // pointer corner case in Thrift (e.g., a unqiue_pointer<folly::IOBuf>)
34 template <typename>
35 struct is_smart_pointer : std::false_type {};
36 template <typename D>
37 struct is_smart_pointer<std::unique_ptr<folly::IOBuf, D>> : std::false_type {};
38 
39 // supported smart pointer types for cpp2.ref_type fields
40 template <typename T, typename D>
41 struct is_smart_pointer<std::unique_ptr<T, D>> : std::true_type {};
42 template <typename T>
43 struct is_smart_pointer<std::shared_ptr<T>> : std::true_type {};
44 
45 template <typename T>
46 using enable_if_smart_pointer =
47     typename std::enable_if<is_smart_pointer<T>::value>::type;
48 
49 template <typename T>
50 using disable_if_smart_pointer =
51     typename std::enable_if<!is_smart_pointer<T>::value>::type;
52 
53 } /* namespace detail */
54 
55 namespace detail {
56 
57 // helper predicate for determining if a struct's MemberInfo is required
58 // to be read out of the protocol
59 struct is_required_field {
60   template <typename MemberInfo>
61   using apply = std::integral_constant<
62       bool,
63       MemberInfo::optional::value == optionality::required>;
64 };
65 
66 struct extract_descriptor_fid {
67   template <typename T>
68   using apply = typename T::metadata::id;
69 };
70 
71 template <typename T, typename Enable = void>
72 struct deref;
73 
74 // General case: methods on deref are no-op, returning their input
75 template <typename T>
76 struct deref<T, disable_if_smart_pointer<T>> {
77   static T& clear_and_get(T& in) { return in; }
78   static T const& get_const(T const& in) { return in; }
79 };
80 
81 // Special case: We specifically *do not* dereference a unique pointer to
82 // an IOBuf, because this is a type that the protocol can (de)serialize
83 // directly
84 template <>
85 struct deref<std::unique_ptr<folly::IOBuf>> {
86   using T = std::unique_ptr<folly::IOBuf>;
87   static T& clear_and_get(T& in) { return in; }
88   static T const& get_const(T const& in) { return in; }
89 };
90 
91 // General case: deref returns a reference to what the
92 // unique pointer contains
93 template <typename PtrType>
94 struct deref<PtrType, enable_if_smart_pointer<PtrType>> {
95   using T = typename std::remove_const<typename PtrType::element_type>::type;
96   static T& clear_and_get(std::shared_ptr<T const>& in) {
97     auto t = std::make_shared<T>();
98     auto ret = t.get();
99     in = std::move(t);
100     return *ret;
101   }
102   static T& clear_and_get(std::shared_ptr<T>& in) {
103     in = std::make_shared<T>();
104     return *in;
105   }
106   static T& clear_and_get(std::unique_ptr<T>& in) {
107     in = std::make_unique<T>();
108     return *in;
109   }
110   static T const& get_const(PtrType const& in) { return *in; }
111 };
112 
113 } // namespace detail
114 
115 /**
116  * Entrypoints for using new serialization methods
117  *
118  * // C++
119  * MyStruct a;
120  * MyUnion b;
121  * CompactProtocolReader reader;
122  * CompactProtocolReader writer;
123  *
124  * serializer_read(a, reader);
125  * serializer_write(b, writer);
126  *
127  * @author: Dylan Knutson <dymk@fb.com>
128  */
129 
130 template <typename Type, typename Protocol>
131 std::size_t serializer_read(Type& out, Protocol& protocol) {
132   using TypeClass = type_class_of_thrift_class_t<Type>;
133   auto xferStart = protocol.getCursorPosition();
134   apache::thrift::detail::pm::protocol_methods<TypeClass, Type>::read(
135       protocol, out);
136   return protocol.getCursorPosition() - xferStart;
137 }
138 
139 template <typename Type, typename Protocol>
140 std::size_t serializer_write(Type const& in, Protocol& protocol) {
141   using TypeClass = type_class_of_thrift_class_t<Type>;
142   return apache::thrift::detail::pm::protocol_methods<TypeClass, Type>::write(
143       protocol, in);
144 }
145 
146 template <typename Type, typename Protocol>
147 std::size_t serializer_serialized_size(Type const& in, Protocol& protocol) {
148   using TypeClass = type_class_of_thrift_class_t<Type>;
149   return apache::thrift::detail::pm::protocol_methods<TypeClass, Type>::
150       template serializedSize<false>(protocol, in);
151 }
152 
153 template <typename Type, typename Protocol>
154 std::size_t serializer_serialized_size_zc(Type const& in, Protocol& protocol) {
155   using TypeClass = type_class_of_thrift_class_t<Type>;
156   return apache::thrift::detail::pm::protocol_methods<TypeClass, Type>::
157       template serializedSize<true>(protocol, in);
158 }
159 
160 } // namespace thrift
161 } // namespace apache
162