2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_RUNTIME_VM_BLOB_HELPER_H_
18 #define incl_HPHP_RUNTIME_VM_BLOB_HELPER_H_
23 #include <type_traits>
24 #include <unordered_set>
27 #include <folly/Optional.h>
28 #include <folly/Varint.h>
30 #include "hphp/runtime/base/builtin-functions.h"
31 #include "hphp/runtime/base/tv-helpers.h"
32 #include "hphp/runtime/base/type-string.h"
33 #include "hphp/runtime/vm/repo.h"
34 #include "hphp/runtime/vm/repo-global-data.h"
37 * This module contains helpers for serializing and deserializing
38 * metadata into blobs suitable for insertion into the hhbc repo.
40 * Types may provide their own serialization logic by implementing a
41 * single-argument member function template called `serde' (as in
42 * "serialization/deserialization"), and pushing data it wants to
43 * serialize into the parameter. Those members may in turn have
44 * customized serialization behavior, or they may "bottom out" to
45 * these helpers if they are basic-enough types.
49 * struct MyBlobableStuff {
51 * std::vector<const StringData*> m_strList;
52 * OtherBlobbableData m_other;
54 * template<class SerDe> void serde(SerDe& sd) {
55 * sd(m_foo)(m_strList)(m_other);
59 * The argument to SerDe will have a static const member called
60 * `deserializing' which can be used to do custom behavior depending
61 * on whether you're deserializing. But generally the goal here
62 * should be to eliminate a class of bugs by having the same (textual)
63 * code implement both serialization and deserialization.
68 //////////////////////////////////////////////////////////////////////
71 * Warning: some language abuse below.
73 * This defines a trait that determines whether or not a type T has a
74 * member function template called `serde' that specializes for the
75 * type `SerDe'. The point here is that calling one of the blob
76 * helpers on an object that knows how to do its own serialization
77 * will just reflect back to it.
79 * If you are unlucky enough to need to know how this works, look up
83 template<class T
, class SerDe
>
84 class IsNontrivialSerializable
{
85 template<class U
, void (U::*)(SerDe
&)> struct Checker
;
86 template<class U
> static char test(Checker
<U
,&U::template serde
<SerDe
> >*);
87 template<class> static long test(...);
89 enum { value
= sizeof(test
<T
>(0)) == 1 };
92 //////////////////////////////////////////////////////////////////////
95 static const bool deserializing
= false;
98 * Currently the most basic encoder/decode only works for integral
99 * types. (We don't want this to accidentally get used for things
100 * like pointers or aggregates.)
102 * Floating point support could be added later if we need it ...
105 typename
std::enable_if
<
106 std::is_integral
<T
>::value
||
107 std::is_enum
<T
>::value
110 const size_t start
= m_blob
.size();
111 unsigned char buf
[folly::kMaxVarintLength64
];
112 uint64_t value
= uint64_t{
113 static_cast<typename
std::make_unsigned
<T
>::type
>(t
)};
114 size_t buf_size
= folly::encodeVarint(value
, buf
);
116 m_blob
.resize(start
+ buf_size
);
118 std::copy(buf
, buf
+ buf_size
, &m_blob
[start
]);
122 typename
std::enable_if
<
123 IsNontrivialSerializable
<T
,BlobEncoder
>::value
124 >::type
encode(const T
& t
) {
125 const_cast<T
&>(t
).serde(*this);
128 void encode(bool b
) {
132 void encode(DataType t
) {
133 // always encode DataType as int8 even if it's a bigger size.
134 assert(DataType(int8_t(t
)) == t
);
138 void encode(const StringData
* sd
) {
139 if (!sd
) { return encode(uint32_t(0)); }
140 uint32_t sz
= sd
->size();
144 const size_t start
= m_blob
.size();
145 m_blob
.resize(start
+ sz
);
146 std::copy(sd
->data(), sd
->data() + sz
, &m_blob
[start
]);
149 void encode(const RepoAuthType::Array
* ar
) {
150 if (!ar
) return encode(std::numeric_limits
<uint32_t>::max());
154 void encode(const TypedValue
& tv
) {
155 if (tv
.m_type
== KindOfUninit
) {
156 return encode(staticEmptyString());
158 String s
= f_serialize(tvAsCVarRef(&tv
));
162 void encode(const TypedValueAux
& tv
) = delete;
165 void encode(const folly::Optional
<T
>& opt
) {
166 const bool some
= opt
.hasValue();
168 if (some
) encode(*opt
);
171 template<class K
, class V
>
172 void encode(const std::pair
<K
,V
>& kv
) {
178 void encode(const std::vector
<T
>& vec
) {
179 encodeContainer(vec
, "vector");
182 template<class K
, class V
, class H
, class C
>
183 void encode(const hphp_hash_map
<K
,V
,H
,C
>& map
) {
184 encodeContainer(map
, "map");
187 template<class V
, class H
, class C
>
188 void encode(const hphp_hash_set
<V
,H
,C
>& set
) {
189 encodeContainer(set
, "set");
192 template<class V
, class H
, class C
>
193 void encode(const std::unordered_set
<V
,H
,C
>& set
) {
194 encodeContainer(set
, "set");
198 BlobEncoder
& operator()(const T
& t
) {
203 size_t size() const { return m_blob
.size(); }
204 const void* data() const { return &m_blob
[0]; }
208 void encodeContainer(Cont
& cont
, const char* desc
) {
209 if (cont
.size() >= 0xffffffffu
) {
210 throw std::runtime_error("maximum " + std::string(desc
) +
211 " size exceeded in BlobEncoder");
213 encode(uint32_t(cont
.size()));
214 typedef typename
Cont::const_iterator iter
;
215 for (iter it
= cont
.begin(); it
!= cont
.end(); ++it
) {
221 std::vector
<char> m_blob
;
224 //////////////////////////////////////////////////////////////////////
227 static const bool deserializing
= true;
229 explicit BlobDecoder(const void* vp
, size_t sz
)
230 : m_p(static_cast<const unsigned char*>(vp
))
235 assert(m_p
>= m_last
);
238 // See encode() in BlobEncoder for why this only allows integral
241 typename
std::enable_if
<
242 std::is_integral
<T
>::value
||
243 std::is_enum
<T
>::value
246 folly::ByteRange
range(m_p
, m_last
);
247 t
= static_cast<T
>(folly::decodeVarint(range
));
252 typename
std::enable_if
<
253 IsNontrivialSerializable
<T
,BlobEncoder
>::value
254 >::type
decode(T
& t
) {
258 void decode(DataType
& t
) {
259 // always decode DataType as int8 even if it's a bigger size.
265 void decode(const StringData
*& sd
) {
266 String
s(decodeString());
267 sd
= s
.get() ? makeStaticString(s
) : 0;
270 void decode(LowStringPtr
& s
) {
271 const StringData
* sd
;
276 void decode(const RepoAuthType::Array
*& ar
) {
279 ar
= id
== std::numeric_limits
<uint32_t>::max()
281 : Repo::get().global().arrayTypeTable
.lookup(id
);
284 void decode(TypedValue
& tv
) {
287 String s
= decodeString();
289 if (s
.empty()) return;
291 tvAsVariant(&tv
) = unserialize_from_string(s
);
292 tvAsVariant(&tv
).setEvalScalar();
295 void decode(TypedValueAux
& tv
) = delete;
298 void decode(folly::Optional
<T
>& opt
) {
311 template<class K
, class V
>
312 void decode(std::pair
<K
,V
>& val
) {
318 void decode(std::vector
<T
>& vec
) {
321 for (uint32_t i
= 0; i
< size
; ++i
) {
327 template<class K
, class V
, class H
, class C
>
328 void decode(hphp_hash_map
<K
,V
,H
,C
>& map
) {
331 for (uint32_t i
= 0; i
< size
; ++i
) {
338 template<class V
, class H
, class C
>
339 void decode(hphp_hash_set
<V
,H
,C
>& set
) {
342 for (uint32_t i
= 0; i
< size
; ++i
) {
349 template<class V
, class H
, class C
>
350 void decode(std::unordered_set
<V
,H
,C
>& set
) {
353 for (uint32_t i
= 0; i
< size
; ++i
) {
361 BlobDecoder
& operator()(T
& t
) {
367 String
decodeString() {
370 if (sz
== 0) return String();
372 if (sz
== 0) return empty_string();
374 String s
= String(sz
, ReserveString
);
375 char* pch
= s
.mutableData();
376 assert(m_last
- m_p
>= sz
);
377 std::copy(m_p
, m_p
+ sz
, pch
);
384 const unsigned char* m_p
;
385 const unsigned char* const m_last
;
388 //////////////////////////////////////////////////////////////////////