2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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/compiler/option.h"
31 #include "hphp/runtime/base/builtin-functions.h"
32 #include "hphp/runtime/base/tv-mutate.h"
33 #include "hphp/runtime/base/tv-variant.h"
34 #include "hphp/runtime/base/type-string.h"
35 #include "hphp/runtime/base/variable-serializer.h"
36 #include "hphp/runtime/vm/litstr-table.h"
37 #include "hphp/runtime/vm/repo.h"
38 #include "hphp/runtime/vm/repo-global-data.h"
41 * This module contains helpers for serializing and deserializing
42 * metadata into blobs suitable for insertion into the hhbc repo.
44 * Types may provide their own serialization logic by implementing a
45 * single-argument member function template called `serde' (as in
46 * "serialization/deserialization"), and pushing data it wants to
47 * serialize into the parameter. Those members may in turn have
48 * customized serialization behavior, or they may "bottom out" to
49 * these helpers if they are basic-enough types.
53 * struct MyBlobableStuff {
55 * std::vector<const StringData*> m_strList;
56 * OtherBlobbableData m_other;
58 * template<class SerDe> void serde(SerDe& sd) {
59 * sd(m_foo)(m_strList)(m_other);
63 * The argument to SerDe will have a static const member called
64 * `deserializing' which can be used to do custom behavior depending
65 * on whether you're deserializing. But generally the goal here
66 * should be to eliminate a class of bugs by having the same (textual)
67 * code implement both serialization and deserialization.
72 //////////////////////////////////////////////////////////////////////
75 * Warning: some language abuse below.
77 * This defines a trait that determines whether or not a type T has a
78 * member function template called `serde' that specializes for the
79 * type `SerDe'. The point here is that calling one of the blob
80 * helpers on an object that knows how to do its own serialization
81 * will just reflect back to it.
83 * If you are unlucky enough to need to know how this works, look up
87 template<class T
, class SerDe
>
88 struct IsNontrivialSerializable
{
90 template<class U
, void (U::*)(SerDe
&)> struct Checker
;
91 template<class U
> static char test(Checker
<U
,&U::template serde
<SerDe
> >*);
92 template<class> static long test(...);
94 enum { value
= sizeof(test
<T
>(0)) == 1 };
97 //////////////////////////////////////////////////////////////////////
100 static const bool deserializing
= false;
103 * Currently the most basic encoder/decode only works for integral
104 * types. (We don't want this to accidentally get used for things
105 * like pointers or aggregates.)
107 * Floating point support could be added later if we need it ...
110 typename
std::enable_if
<
111 std::is_integral
<T
>::value
&& std::is_signed
<T
>::value
114 encode(folly::encodeZigZag(static_cast<int64_t>(t
)));
118 typename
std::enable_if
<
119 (std::is_integral
<T
>::value
&& !std::is_signed
<T
>::value
) ||
120 std::is_enum
<T
>::value
123 const size_t start
= m_blob
.size();
124 unsigned char buf
[folly::kMaxVarintLength64
];
125 uint64_t value
= uint64_t{
126 static_cast<typename
std::make_unsigned
<T
>::type
>(t
)};
127 size_t buf_size
= folly::encodeVarint(value
, buf
);
129 m_blob
.resize(start
+ buf_size
);
131 std::copy(buf
, buf
+ buf_size
, &m_blob
[start
]);
135 typename
std::enable_if
<
136 IsNontrivialSerializable
<T
,BlobEncoder
>::value
137 >::type
encode(const T
& t
) {
138 const_cast<T
&>(t
).serde(*this);
141 void encode(bool b
) {
145 void encode(DataType t
) {
146 // always encode DataType as int8 even if it's a bigger size.
147 assertx(DataType(int8_t(t
)) == t
);
151 void encode(const LowStringPtr
& s
) {
152 const StringData
* sd
= s
;
153 if (Option::WholeProgram
) {
154 Id id
= LitstrTable::get().mergeLitstr(sd
);
162 void encode(const StringData
* sd
) {
163 if (!sd
) { return encode(uint32_t(0)); }
164 uint32_t sz
= sd
->size();
168 const size_t start
= m_blob
.size();
169 m_blob
.resize(start
+ sz
);
170 std::copy(sd
->data(), sd
->data() + sz
, &m_blob
[start
]);
173 void encode(const std::string
& s
) {
174 uint32_t sz
= s
.size();
178 const size_t start
= m_blob
.size();
179 m_blob
.resize(start
+ sz
);
180 std::copy(s
.data(), s
.data() + sz
, &m_blob
[start
]);
183 void encode(const TypedValue
& tv
) {
184 if (tv
.m_type
== KindOfUninit
) {
185 return encode(staticEmptyString());
187 auto s
= internal_serialize(tvAsCVarRef(&tv
));
191 void encode(const TypedValueAux
& tv
) = delete;
194 void encode(const folly::Optional
<T
>& opt
) {
195 const bool some
= opt
.hasValue();
197 if (some
) encode(*opt
);
200 template <size_t I
= 0, typename
... Ts
>
201 typename
std::enable_if
<I
== sizeof...(Ts
), void>::type
202 encode(const std::tuple
<Ts
...>& /*val*/) {}
204 template<size_t I
= 0, typename
...Ts
>
205 typename
std::enable_if
<I
< sizeof...(Ts
), void>::type
206 encode(const std::tuple
<Ts
...>& val
) {
207 encode(std::get
<I
>(val
));
208 encode
<I
+ 1, Ts
...>(val
);
211 template<class K
, class V
>
212 void encode(const std::pair
<K
,V
>& kv
) {
217 template<class T
, typename FDeltaEncode
>
218 void encode(const std::vector
<T
>& cont
, FDeltaEncode deltaEncode
) {
219 if (cont
.size() >= 0xffffffffu
) {
220 throw std::runtime_error("maximum vector size exceeded in BlobEncoder");
224 encode(uint32_t(cont
.size()));
225 for (auto it
= cont
.begin(); it
!= cont
.end(); ++it
) {
226 encode(deltaEncode(prev
, *it
));
232 void encode(const std::vector
<T
>& vec
) {
233 encodeContainer(vec
, "vector");
237 typename
std::enable_if
<
238 std::is_same
<typename
T::value_type
,
239 std::pair
<typename
T::key_type
const,
240 typename
T::mapped_type
>>::value
&&
241 !IsNontrivialSerializable
<T
,BlobEncoder
>::value
242 >::type
encode(const T
& map
) {
243 encodeContainer(map
, "map");
247 typename
std::enable_if
<
248 std::is_same
<typename
T::key_type
,
249 typename
T::value_type
>::value
&&
250 !IsNontrivialSerializable
<T
,BlobEncoder
>::value
251 >::type
encode(const T
& set
) {
252 encodeContainer(set
, "set");
256 BlobEncoder
& operator()(const T
& t
) {
261 template<class T
, class DeltaEncode
>
262 BlobEncoder
& operator()(T
& t
, DeltaEncode e
) {
267 template<class T
, class DeltaEncode
, class DeltaDecode
>
268 BlobEncoder
& operator()(T
& t
, DeltaEncode e
, DeltaDecode d
) {
273 size_t size() const { return m_blob
.size(); }
274 const void* data() const { return &m_blob
[0]; }
278 void encodeContainer(Cont
& cont
, const char* desc
) {
279 if (cont
.size() >= 0xffffffffu
) {
280 throw std::runtime_error("maximum " + std::string(desc
) +
281 " size exceeded in BlobEncoder");
283 encode(uint32_t(cont
.size()));
284 typedef typename
Cont::const_iterator iter
;
285 for (iter it
= cont
.begin(); it
!= cont
.end(); ++it
) {
291 std::vector
<char> m_blob
;
294 //////////////////////////////////////////////////////////////////////
297 static const bool deserializing
= true;
299 explicit BlobDecoder(const void* vp
, size_t sz
)
300 : m_p(static_cast<const unsigned char*>(vp
))
305 assertx(m_p
>= m_last
);
308 // See encode() in BlobEncoder for why this only allows integral
311 typename
std::enable_if
<
312 std::is_integral
<T
>::value
&& std::is_signed
<T
>::value
317 t
= static_cast<T
>(folly::decodeZigZag(value
));
321 typename
std::enable_if
<
322 (std::is_integral
<T
>::value
&& !std::is_signed
<T
>::value
) ||
323 std::is_enum
<T
>::value
326 folly::ByteRange
range(m_p
, m_last
);
327 t
= static_cast<T
>(folly::decodeVarint(range
));
332 typename
std::enable_if
<
333 IsNontrivialSerializable
<T
,BlobDecoder
>::value
334 >::type
decode(T
& t
) {
338 void decode(DataType
& t
) {
339 // always decode DataType as int8 even if it's a bigger size.
345 void decode(const StringData
*& sd
) {
346 String
s(decodeString());
347 sd
= s
.get() ? makeStaticString(s
) : 0;
350 void decode(LowStringPtr
& s
) {
351 if (RuntimeOption::RepoAuthoritative
) {
354 s
= LitstrTable::get().lookupLitstrId(id
);
358 String
st(decodeString());
359 s
= st
.get() ? makeStaticString(st
) : 0;
362 void decode(std::string
& s
) {
363 String
str(decodeString());
364 s
= str
.toCppString();
367 void decode(TypedValue
& tv
) {
370 String s
= decodeString();
372 if (s
.empty()) return;
375 unserialize_from_string(s
, VariableUnserializer::Type::Internal
);
376 tvAsVariant(&tv
).setEvalScalar();
379 void decode(TypedValueAux
& tv
) = delete;
382 void decode(folly::Optional
<T
>& opt
) {
395 template <size_t I
= 0, typename
... Ts
>
396 typename
std::enable_if
<I
== sizeof...(Ts
), void>::type
397 decode(std::tuple
<Ts
...>& /*val*/) {}
399 template<size_t I
= 0, typename
...Ts
>
400 typename
std::enable_if
<I
< sizeof...(Ts
), void>::type
401 decode(std::tuple
<Ts
...>& val
) {
402 decode(std::get
<I
>(val
));
403 decode
<I
+ 1, Ts
...>(val
);
406 template<class K
, class V
>
407 void decode(std::pair
<K
,V
>& val
) {
413 void decode(std::vector
<T
>& vec
) {
416 for (uint32_t i
= 0; i
< size
; ++i
) {
422 template<class T
, typename FDeltaDecode
>
423 void decode(std::vector
<T
>& vec
, FDeltaDecode deltaDecode
) {
430 for (uint32_t i
= 0; i
< size
; ++i
) {
432 prev
= deltaDecode(prev
, delta
);
438 typename
std::enable_if
<
439 std::is_same
<typename
T::value_type
,
440 std::pair
<typename
T::key_type
const,
441 typename
T::mapped_type
>>::value
&&
442 !IsNontrivialSerializable
<T
,BlobDecoder
>::value
443 >::type
decode(T
& map
) {
446 for (uint32_t i
= 0; i
< size
; ++i
) {
447 typename
T::key_type key
;
449 typename
T::mapped_type val
;
451 map
.emplace(key
, val
);
456 typename
std::enable_if
<
457 std::is_same
<typename
T::key_type
,
458 typename
T::value_type
>::value
&&
459 !IsNontrivialSerializable
<T
,BlobDecoder
>::value
460 >::type
decode(T
& set
) {
463 for (uint32_t i
= 0; i
< size
; ++i
) {
464 typename
T::value_type val
;
471 BlobDecoder
& operator()(T
& t
) {
476 template<class T
, class DeltaDecode
>
477 BlobDecoder
& operator()(T
& t
, DeltaDecode d
) {
482 template<class T
, class DeltaEncode
, class DeltaDecode
>
483 BlobDecoder
& operator()(T
& t
, DeltaEncode e
, DeltaDecode d
) {
489 String
decodeString() {
492 if (sz
== 0) return String();
494 if (sz
== 0) return empty_string();
496 String s
= String(sz
, ReserveString
);
497 char* pch
= s
.mutableData();
498 assertx(m_last
- m_p
>= sz
);
499 std::copy(m_p
, m_p
+ sz
, pch
);
506 const unsigned char* m_p
;
507 const unsigned char* const m_last
;
510 //////////////////////////////////////////////////////////////////////