Codemod asserts to assertxs in the runtime
[hiphop-php.git] / hphp / runtime / vm / blob-helper.h
blob624d2e21497936a7bf959593aa16657ca3acffe6
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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_
20 #include <algorithm>
21 #include <cstdlib>
22 #include <limits>
23 #include <type_traits>
24 #include <unordered_set>
25 #include <vector>
27 #include <folly/Optional.h>
28 #include <folly/Varint.h>
30 #include "hphp/runtime/base/builtin-functions.h"
31 #include "hphp/runtime/base/tv-mutate.h"
32 #include "hphp/runtime/base/tv-variant.h"
33 #include "hphp/runtime/base/type-string.h"
34 #include "hphp/runtime/base/variable-serializer.h"
35 #include "hphp/runtime/vm/repo.h"
36 #include "hphp/runtime/vm/repo-global-data.h"
39 * This module contains helpers for serializing and deserializing
40 * metadata into blobs suitable for insertion into the hhbc repo.
42 * Types may provide their own serialization logic by implementing a
43 * single-argument member function template called `serde' (as in
44 * "serialization/deserialization"), and pushing data it wants to
45 * serialize into the parameter. Those members may in turn have
46 * customized serialization behavior, or they may "bottom out" to
47 * these helpers if they are basic-enough types.
49 * For example:
51 * struct MyBlobableStuff {
52 * int m_foo;
53 * std::vector<const StringData*> m_strList;
54 * OtherBlobbableData m_other;
56 * template<class SerDe> void serde(SerDe& sd) {
57 * sd(m_foo)(m_strList)(m_other);
58 * }
59 * };
61 * The argument to SerDe will have a static const member called
62 * `deserializing' which can be used to do custom behavior depending
63 * on whether you're deserializing. But generally the goal here
64 * should be to eliminate a class of bugs by having the same (textual)
65 * code implement both serialization and deserialization.
68 namespace HPHP {
70 //////////////////////////////////////////////////////////////////////
73 * Warning: some language abuse below.
75 * This defines a trait that determines whether or not a type T has a
76 * member function template called `serde' that specializes for the
77 * type `SerDe'. The point here is that calling one of the blob
78 * helpers on an object that knows how to do its own serialization
79 * will just reflect back to it.
81 * If you are unlucky enough to need to know how this works, look up
82 * "SFINAE".
85 template<class T, class SerDe>
86 struct IsNontrivialSerializable {
87 private:
88 template<class U, void (U::*)(SerDe&)> struct Checker;
89 template<class U> static char test(Checker<U,&U::template serde<SerDe> >*);
90 template<class> static long test(...);
91 public:
92 enum { value = sizeof(test<T>(0)) == 1 };
95 //////////////////////////////////////////////////////////////////////
97 struct BlobEncoder {
98 static const bool deserializing = false;
101 * Currently the most basic encoder/decode only works for integral
102 * types. (We don't want this to accidentally get used for things
103 * like pointers or aggregates.)
105 * Floating point support could be added later if we need it ...
107 template<class T>
108 typename std::enable_if<
109 std::is_integral<T>::value ||
110 std::is_enum<T>::value
111 >::type
112 encode(const T& t) {
113 const size_t start = m_blob.size();
114 unsigned char buf[folly::kMaxVarintLength64];
115 uint64_t value = uint64_t{
116 static_cast<typename std::make_unsigned<T>::type>(t)};
117 size_t buf_size = folly::encodeVarint(value, buf);
119 m_blob.resize(start + buf_size);
121 std::copy(buf, buf + buf_size, &m_blob[start]);
124 template<class T>
125 typename std::enable_if<
126 IsNontrivialSerializable<T,BlobEncoder>::value
127 >::type encode(const T& t) {
128 const_cast<T&>(t).serde(*this);
131 void encode(bool b) {
132 encode(b ? 1 : 0);
135 void encode(DataType t) {
136 // always encode DataType as int8 even if it's a bigger size.
137 assertx(DataType(int8_t(t)) == t);
138 encode(int8_t(t));
141 void encode(const StringData* sd) {
142 if (!sd) { return encode(uint32_t(0)); }
143 uint32_t sz = sd->size();
144 encode(sz + 1);
145 if (!sz) { return; }
147 const size_t start = m_blob.size();
148 m_blob.resize(start + sz);
149 std::copy(sd->data(), sd->data() + sz, &m_blob[start]);
152 void encode(const std::string& s) {
153 uint32_t sz = s.size();
154 encode(sz + 1);
155 if (!sz) { return; }
157 const size_t start = m_blob.size();
158 m_blob.resize(start + sz);
159 std::copy(s.data(), s.data() + sz, &m_blob[start]);
162 void encode(const TypedValue& tv) {
163 if (tv.m_type == KindOfUninit) {
164 return encode(staticEmptyString());
166 auto s = internal_serialize(tvAsCVarRef(&tv));
167 encode(s.get());
170 void encode(const TypedValueAux& tv) = delete;
172 template<class T>
173 void encode(const folly::Optional<T>& opt) {
174 const bool some = opt.hasValue();
175 encode(some);
176 if (some) encode(*opt);
179 template <size_t I = 0, typename... Ts>
180 typename std::enable_if<I == sizeof...(Ts), void>::type
181 encode(const std::tuple<Ts...>& /*val*/) {}
183 template<size_t I = 0, typename ...Ts>
184 typename std::enable_if<I < sizeof...(Ts), void>::type
185 encode(const std::tuple<Ts...>& val) {
186 encode(std::get<I>(val));
187 encode<I + 1, Ts...>(val);
190 template<class K, class V>
191 void encode(const std::pair<K,V>& kv) {
192 encode(kv.first);
193 encode(kv.second);
196 template<class T>
197 void encode(const std::vector<T>& vec) {
198 encodeContainer(vec, "vector");
201 template<class T>
202 typename std::enable_if<
203 std::is_same<typename T::value_type,
204 std::pair<typename T::key_type const,
205 typename T::mapped_type>>::value &&
206 !IsNontrivialSerializable<T,BlobEncoder>::value
207 >::type encode(const T& map) {
208 encodeContainer(map, "map");
211 template<class T>
212 typename std::enable_if<
213 std::is_same<typename T::key_type,
214 typename T::value_type>::value &&
215 !IsNontrivialSerializable<T,BlobEncoder>::value
216 >::type encode(const T& set) {
217 encodeContainer(set, "set");
220 template<class T>
221 BlobEncoder& operator()(const T& t) {
222 encode(t);
223 return *this;
226 size_t size() const { return m_blob.size(); }
227 const void* data() const { return &m_blob[0]; }
229 private:
230 template<class Cont>
231 void encodeContainer(Cont& cont, const char* desc) {
232 if (cont.size() >= 0xffffffffu) {
233 throw std::runtime_error("maximum " + std::string(desc) +
234 " size exceeded in BlobEncoder");
236 encode(uint32_t(cont.size()));
237 typedef typename Cont::const_iterator iter;
238 for (iter it = cont.begin(); it != cont.end(); ++it) {
239 encode(*it);
243 private:
244 std::vector<char> m_blob;
247 //////////////////////////////////////////////////////////////////////
249 struct BlobDecoder {
250 static const bool deserializing = true;
252 explicit BlobDecoder(const void* vp, size_t sz)
253 : m_p(static_cast<const unsigned char*>(vp))
254 , m_last(m_p + sz)
257 void assertDone() {
258 assertx(m_p >= m_last);
261 // See encode() in BlobEncoder for why this only allows integral
262 // types.
263 template<class T>
264 typename std::enable_if<
265 std::is_integral<T>::value ||
266 std::is_enum<T>::value
267 >::type
268 decode(T& t) {
269 folly::ByteRange range(m_p, m_last);
270 t = static_cast<T>(folly::decodeVarint(range));
271 m_p = range.begin();
274 template<class T>
275 typename std::enable_if<
276 IsNontrivialSerializable<T,BlobDecoder>::value
277 >::type decode(T& t) {
278 t.serde(*this);
281 void decode(DataType& t) {
282 // always decode DataType as int8 even if it's a bigger size.
283 int8_t t2;
284 decode(t2);
285 t = DataType(t2);
288 void decode(const StringData*& sd) {
289 String s(decodeString());
290 sd = s.get() ? makeStaticString(s) : 0;
293 void decode(LowStringPtr& s) {
294 const StringData* sd;
295 decode(sd);
296 s = sd;
299 void decode(std::string& s) {
300 String str(decodeString());
301 s = str.toCppString();
304 void decode(TypedValue& tv) {
305 tvWriteUninit(tv);
307 String s = decodeString();
308 assertx(!!s);
309 if (s.empty()) return;
311 tvAsVariant(&tv) =
312 unserialize_from_string(s, VariableUnserializer::Type::Internal);
313 tvAsVariant(&tv).setEvalScalar();
316 void decode(TypedValueAux& tv) = delete;
318 template<class T>
319 void decode(folly::Optional<T>& opt) {
320 bool some;
321 decode(some);
323 if (!some) {
324 opt = folly::none;
325 } else {
326 T value;
327 decode(value);
328 opt = value;
332 template <size_t I = 0, typename... Ts>
333 typename std::enable_if<I == sizeof...(Ts), void>::type
334 decode(std::tuple<Ts...>& /*val*/) {}
336 template<size_t I = 0, typename ...Ts>
337 typename std::enable_if<I < sizeof...(Ts), void>::type
338 decode(std::tuple<Ts...>& val) {
339 decode(std::get<I>(val));
340 decode<I + 1, Ts...>(val);
343 template<class K, class V>
344 void decode(std::pair<K,V>& val) {
345 decode(val.first);
346 decode(val.second);
349 template<class T>
350 void decode(std::vector<T>& vec) {
351 uint32_t size;
352 decode(size);
353 for (uint32_t i = 0; i < size; ++i) {
354 vec.push_back(T());
355 decode(vec.back());
359 template<class T>
360 typename std::enable_if<
361 std::is_same<typename T::value_type,
362 std::pair<typename T::key_type const,
363 typename T::mapped_type>>::value &&
364 !IsNontrivialSerializable<T,BlobDecoder>::value
365 >::type decode(T& map) {
366 uint32_t size;
367 decode(size);
368 for (uint32_t i = 0; i < size; ++i) {
369 typename T::key_type key;
370 decode(key);
371 typename T::mapped_type val;
372 decode(val);
373 map.emplace(key, val);
377 template<class T>
378 typename std::enable_if<
379 std::is_same<typename T::key_type,
380 typename T::value_type>::value &&
381 !IsNontrivialSerializable<T,BlobDecoder>::value
382 >::type decode(T& set) {
383 uint32_t size;
384 decode(size);
385 for (uint32_t i = 0; i < size; ++i) {
386 typename T::value_type val;
387 decode(val);
388 set.insert(val);
392 template<class T>
393 BlobDecoder& operator()(T& t) {
394 decode(t);
395 return *this;
398 private:
399 String decodeString() {
400 uint32_t sz;
401 decode(sz);
402 if (sz == 0) return String();
403 sz--;
404 if (sz == 0) return empty_string();
406 String s = String(sz, ReserveString);
407 char* pch = s.mutableData();
408 assertx(m_last - m_p >= sz);
409 std::copy(m_p, m_p + sz, pch);
410 m_p += sz;
411 s.setSize(sz);
412 return s;
415 private:
416 const unsigned char* m_p;
417 const unsigned char* const m_last;
420 //////////////////////////////////////////////////////////////////////
424 #endif