3 * Copyright (c) 2013 The Native Client Authors. All rights reserved.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #ifndef NATIVE_CLIENT_SRC_SHARED_SERIALIZATION_SERIALIZATION_H_
9 #define NATIVE_CLIENT_SRC_SHARED_SERIALIZATION_SERIALIZATION_H_
11 #if defined(__native_client__) || NACL_LINUX
12 # define NACL_HAS_IEEE_754
13 // Make sure fp is not dead code and is tested. DO NOT USE the fp
14 // interface until we have a portability version of ieee754.h!
17 #if defined(NACL_HAS_IEEE_754)
24 #include "native_client/src/include/portability.h"
25 #include "native_client/src/include/nacl_compiler_annotations.h"
26 #include "native_client/src/shared/platform/nacl_check.h"
28 // SerializationBuffer enables serializing basic types and vectors
32 class SerializationBuffer;
34 template<typename T> class SerializationTraits;
48 #if defined(NACL_HAS_IEEE_754)
57 kRecursiveVector = 31,
61 class SerializationBuffer {
63 SerializationBuffer();
65 // This initializes the Serialization buffer from |data_buffer|
66 // containing |nbytes| of data. A copy of the data is made rather
67 // than transferring ownership, which is suboptimal.
68 SerializationBuffer(uint8_t const *data_buffer, size_t nbytes);
70 template<typename T> bool Serialize(T basic) NACL_WUR;
72 template<typename T> bool Serialize(std::vector<T> const& v) NACL_WUR;
74 bool Serialize(char const *cstr) NACL_WUR;
75 bool Serialize(char const *cstr, size_t char_count) NACL_WUR;
77 bool Serialize(std::string str) NACL_WUR;
80 if (bytes_unread() < kTagBytes) {
83 return buffer_[read_ix_++];
86 template<typename T> bool Deserialize(T *basic) NACL_WUR;
88 template<typename T> bool Deserialize(std::vector<T> *v) NACL_WUR;
90 // This function deserializes into the provided buffer at |cstr|.
91 // The parameter *buffer_size is an in-out parameter, initially
92 // containing the available space at |cstr|. If there are decoding
93 // errors, this function returns false. If it returns true, the
94 // caller should check *buffer_size -- if there were insufficient
95 // space, the read position is unchanged and *buffer_size is updated
96 // to reflect the amount of space that is required; otherwise
97 // *buffer_size is updated to reflect the actual number of bytes
99 bool Deserialize(char *cstr, size_t *buffer_size) NACL_WUR;
100 // caller provides buffer
102 // This method deserializes a NUL-terminated C-style string. The
103 // caller receives ownnership of the memory allocated via new[] and
104 // is responsible for delete[]ing it to release the storage.
105 bool Deserialize(char **cstr_out) NACL_WUR;
107 bool Deserialize(std::string *str) NACL_WUR;
109 size_t num_bytes() const {
113 uint8_t const *data() const {
114 // return buffer_.data(); // C++11 only, not available on windows
129 static const size_t kTagBytes = 1;
132 template<typename T> void AddTag();
134 template<typename T> bool CheckTag();
136 void AddUint8(uint8_t value);
137 void AddUint16(uint16_t value);
138 void AddUint32(uint32_t value);
139 void AddUint64(uint64_t value);
140 #if defined(NACL_HAS_IEEE_754)
141 void AddFloat(float value);
142 void AddDouble(double value);
143 void AddLongDouble(long double value);
146 bool GetUint8(uint8_t *val);
147 bool GetUint16(uint16_t *val);
148 bool GetUint32(uint32_t *val);
149 bool GetUint64(uint64_t *val);
150 #if defined(NACL_HAS_IEEE_754)
151 bool GetFloat(float *value);
152 bool GetDouble(double *value);
153 bool GetLongDouble(long double *value);
156 template<typename T> void AddVal(T value) {
157 int T_must_be_integral_type[static_cast<T>(1)];
158 UNREFERENCED_PARAMETER(T_must_be_integral_type);
159 if (sizeof(T) == 1) {
160 AddUint8(static_cast<uint8_t>(value));
161 } else if (sizeof(T) == 2) {
162 AddUint16(static_cast<uint16_t>(value));
163 } else if (sizeof(T) == 4) {
164 AddUint32(static_cast<uint32_t>(value));
165 } else if (sizeof(T) == 8) {
166 AddUint64(static_cast<uint64_t>(value));
170 template<typename T> bool GetVal(T *basic) {
171 int T_must_be_integral_type[static_cast<T>(1)];
172 UNREFERENCED_PARAMETER(T_must_be_integral_type);
173 if (sizeof(T) == 1) {
175 return GetUint8(&val) ? ((*basic = static_cast<T>(val)), true) : false;
176 } else if (sizeof(T) == 2) {
178 return GetUint16(&val) ? ((*basic = static_cast<T>(val)), true) : false;
179 } else if (sizeof(T) == 4) {
181 return GetUint32(&val) ? ((*basic = static_cast<T>(val)), true) : false;
182 } else if (sizeof(T) == 8) {
184 return GetUint64(&val) ? ((*basic = static_cast<T>(val)), true) : false;
189 #if defined(NACL_HAS_IEEE_754)
190 void AddVal(float value) {
194 bool GetVal(float *value) {
195 return GetFloat(value);
198 void AddVal(double value) {
202 bool GetVal(double *value) {
203 return GetDouble(value);
206 void AddVal(long double value) {
207 AddLongDouble(value);
210 bool GetVal(long double *value) {
211 return GetLongDouble(value);
215 template<typename T, bool nested_tagging>
216 bool Serialize(std::vector<T> const& v);
218 // Template metaprogramming to determine at compile time, based on
219 // whether the type T is a container type or not, whether to tag the
220 // elements with their own type tag, or to just write the elements
221 // sans type tag. For vector containers of simple types such as
222 // int8_t, tagging every byte is excessive overhead. NB: see the
223 // definition below of kTag for vectors.
224 template<typename T, bool nested_tagging> class SerializeHelper {
226 static bool DoSerialize(SerializationBuffer *buf,
227 std::vector<T> const& v) {
228 size_t orig = buf->cur_write_pos();
229 size_t num_elt = v.size();
230 if (num_elt > ~(uint32_t) 0) {
233 buf->AddTag<std::vector<T> >();
234 buf->AddVal(static_cast<uint32_t>(num_elt));
236 for (size_t ix = 0; ix < v.size(); ++ix) {
237 if (!buf->Serialize(v[ix])) {
238 buf->reset_write_pos(orig);
246 template<typename T> class SerializeHelper<T, false> {
248 static bool DoSerialize(SerializationBuffer *buf,
249 std::vector<T> const& v) {
250 size_t num_elt = v.size();
251 if (num_elt > ~(uint32_t) 0) {
254 buf->AddTag<std::vector<T> >();
255 buf->AddVal(static_cast<uint32_t>(num_elt));
257 for (size_t ix = 0; ix < v.size(); ++ix) {
264 template<typename T, bool b> friend class SerializeHelper;
266 template<typename T, bool nested_tagging> class DeserializeHelper {
268 static bool DoDeserialize(SerializationBuffer *buf,
270 size_t orig = buf->cur_read_pos();
271 if (buf->ReadTag() != SerializationTraits<std::vector<T> >::kTag) {
272 buf->reset_read_pos(orig);
276 if (!buf->GetVal(&num_elt)) {
277 buf->reset_read_pos(orig);
280 for (size_t ix = 0; ix < num_elt; ++ix) {
282 if (!buf->Deserialize(&val)) {
283 buf->reset_read_pos(orig);
292 template<typename T> class DeserializeHelper<T, false> {
294 static bool DoDeserialize(SerializationBuffer *buf,
296 size_t orig = buf->cur_read_pos();
297 if (buf->ReadTag() != SerializationTraits<std::vector<T> >::kTag) {
298 buf->reset_read_pos(orig);
302 if (!buf->GetVal(&num_elt)) {
303 buf->reset_read_pos(orig);
306 for (size_t ix = 0; ix < num_elt; ++ix) {
308 if (!buf->GetVal(&val)) {
309 buf->reset_read_pos(orig);
318 template<typename T, bool b> friend class DeserializeHelper;
320 // TODO(bsy): consider doing something along the lines of
322 // template<typename T> Serialize(T stl_container) {
323 // AddTag<T>(); // how?
324 // for (T::const_iterator it = stl_container.begin();
325 // it != stl_container.end();
328 // // Or AddVal, when SerializationTraits<T::value_type>::kNestedTag
333 // This means that the container type would probably be omitted or a
334 // generic stl_container type tag would be used -- or we'd have to
335 // enumerate all container types.
338 std::vector<uint8_t> buffer_;
343 void EnsureTotalSize(size_t req_size);
344 void EnsureAvailableSpace(size_t req_space);
346 size_t bytes_unread() const {
347 return in_use_ - read_ix_;
350 size_t cur_read_pos() const {
354 void reset_read_pos(size_t pos) {
358 size_t cur_write_pos() const {
362 void reset_write_pos(size_t pos) {
367 template<typename T> void SerializationBuffer::AddTag() {
368 AddUint8(SerializationTraits<T>::kTag);
371 template<typename T> bool SerializationBuffer::Serialize(T basic) {
377 template<typename T> bool SerializationBuffer::Serialize(
378 std::vector<T> const& v) {
379 return SerializeHelper<T, SerializationTraits<T>::kNestedTag>::
380 DoSerialize(this, v);
383 template<typename T> bool SerializationBuffer::Deserialize(T *basic) {
384 size_t orig = cur_read_pos();
385 if (bytes_unread() < kTagBytes + SerializationTraits<T>::kBytes) {
389 if ((tag = ReadTag()) != SerializationTraits<T>::kTag) {
390 reset_read_pos(orig);
393 // if BytesAvail >= tag + serialization_size
394 (void) GetVal(basic);
398 template<typename T> bool SerializationBuffer::Deserialize(
400 return DeserializeHelper<T, SerializationTraits<T>::kNestedTag>::
401 DoDeserialize(this, v);
404 template<> class SerializationTraits<uint8_t> {
406 static const int kTag = kUint8;
407 static const int kBytes = 1;
408 static const bool kNestedTag = false;
411 template<> class SerializationTraits<int8_t> {
413 static const int kTag = kInt8;
414 static const int kBytes = 1;
415 static const bool kNestedTag = false;
418 template<> class SerializationTraits<uint16_t> {
420 static const int kTag = kUint16;
421 static const int kBytes = 2;
422 static const bool kNestedTag = false;
425 template<> class SerializationTraits<int16_t> {
427 static const int kTag = kInt16;
428 static const int kBytes = 2;
429 static const bool kNestedTag = false;
432 template<> class SerializationTraits<uint32_t> {
434 static const int kTag = kUint32;
435 static const int kBytes = 4;
436 static const bool kNestedTag = false;
439 template<> class SerializationTraits<int32_t> {
441 static const int kTag = kInt32;
442 static const int kBytes = 4;
443 static const bool kNestedTag = false;
446 template<> class SerializationTraits<uint64_t> {
448 static const int kTag = kUint64;
449 static const int kBytes = 8;
450 static const bool kNestedTag = false;
453 template<> class SerializationTraits<int64_t> {
455 static const int kTag = kInt64;
456 static const int kBytes = 8;
457 static const bool kNestedTag = false;
460 #if defined(NACL_HAS_IEEE_754)
461 template<> class SerializationTraits<float> {
463 static const int kTag = kFloat;
464 static const int kBytes = 4;
465 static const bool kNestedTag = false;
468 template<> class SerializationTraits<double> {
470 static const int kTag = kDouble;
471 static const int kBytes = 8;
472 static const bool kNestedTag = false;
475 template<> class SerializationTraits<long double> {
477 static const int kTag = kLongDouble;
478 static const int kBytes = 10;
479 static const bool kNestedTag = false;
483 template<> class SerializationTraits<char *> {
485 static const int kTag = kCString;
486 static const bool kNestedTag = true;
489 template<> class SerializationTraits<std::string> {
491 static const int kTag = kString;
492 static const bool kNestedTag = true;
495 // We want the type tag for vector<T>, when the type T is a basic
496 // type, to incorporate the type tag for T. This way, we do not tag
497 // each vector element (see SerializeHelper etc above), and yet the
498 // type information is present. When T is not a basic type (e.g., it
499 // is a string, a vector<U>, or some other container to be added), we
500 // don't want to just add the kVectorOffset to the type tag for T,
501 // since deep nesting of containers could cause the tag to overflow.
502 // Assuming that the type T nested containers are not empty, paying
503 // the cost of tagging each element of the vector is not a huge
505 template<typename T> class SerializationTraits<std::vector<T> > {
507 template<typename S, bool b> class RecursiveOrNotTag {
509 static const int kVectorTag = kRecursiveVector;
511 template<typename S> class RecursiveOrNotTag<S, false> {
513 static const int kVectorTag = kVectorOffset + SerializationTraits<S>::kTag;
516 static const int kTag =
517 RecursiveOrNotTag<T, SerializationTraits<T>::kNestedTag>::kVectorTag;
518 static const bool kNestedTag = true;