Bond
 
Loading...
Searching...
No Matches
random_protocol.h
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#pragma once
5
6#include <bond/core/config.h>
7
8#include <bond/core/blob.h>
9#include <bond/core/containers.h>
10#include <bond/core/traits.h>
11
12#include <boost/static_assert.hpp>
13
14#include <cstring>
15
16namespace bond
17{
18
19template <typename T>
20class RandomProtocolEngine
21{
22public:
23 // We don't need a good pseudo-random number generator but we do need one
24 // that is consistent accross compilers/libraries so that we can use
25 // randomly generated data files from one platfom to verify compatibility
26 // on another platform.
27 RandomProtocolEngine()
28 {}
29
30 // Variant of Marsaglia's xorshift generator
31 // http://arxiv.org/pdf/1404.0390v1.pdf
32 uint64_t Next()
33 {
34 uint64_t s1 = state[0];
35 const uint64_t s0 = state[1];
36 state[0] = s0;
37 s1 ^= s1 << 23;
38 return (state[1] = (s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26))) + s0;
39 }
40
41 void Seed(uint64_t s0 = seed1, uint64_t s1 = seed2)
42 {
43 state[0] = s0;
44 state[1] = s1;
45 }
46
47 static uint64_t state[2];
48 const static uint64_t seed1 = 234578354U;
49 const static uint64_t seed2 = 753478U;
50};
51
52template <typename T>
53uint64_t RandomProtocolEngine<T>::state[2] = {seed1, seed2};
54
55//
56// Protocol which generates a random stream of bits.
57// In some cases it may be useful for initializing data in tests, e.g.:
58//
59// Params param;
60// Apply(bond::To<Params>(param), bond::bonded<Params>(bond::RandomProtocolReader()));
61//
62class RandomProtocolReader
63 : public RandomProtocolEngine<RandomProtocolReader>
64{
65public:
66 typedef bond::StaticParser<RandomProtocolReader&> Parser;
67
68 RandomProtocolReader(uint32_t max_string_length = 50, uint32_t max_list_size = 20, bool json = false)
69 : _max_string_length(max_string_length),
70 _max_list_size(max_list_size),
71 _json(json)
72 {}
73
74 bool operator==(const RandomProtocolReader&) const
75 {
76 return false;
77 }
78
79 bool ReadVersion()
80 {
81 return false;
82 }
83
84 template <typename T>
85 typename boost::disable_if<is_string_type<T> >::type
86 Read(T& value)
87 {
88 BOOST_STATIC_ASSERT(std::is_trivially_copyable<T>::value);
89 // We only have 64 bits of randomness, so T needs to fit in that.
90 BOOST_STATIC_ASSERT(sizeof(T) <= sizeof(uint64_t));
91
92 uint64_t random = RandomProtocolEngine::Next();
93 std::memcpy(&value, &random, sizeof(T));
94 }
95
96 void Read(uint64_t& value)
97 {
98 value = RandomProtocolEngine::Next();
99
100 if (_json)
101 {
102 // NewtonsoftJson used by C# implementation doesn't support
103 // numbers larger that max int64.
104 value >>= 1;
105 }
106 }
107
108 void Read(bool& value)
109 {
110 int8_t n;
111 Read(n);
112 value = n > 0;
113 }
114
115 void Read(double& value)
116 {
117 BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
118
119 uint8_t sign;
120 uint8_t exponent;
121 uint32_t mantissa;
122
123 Read(sign);
124 Read(exponent);
125 Read(mantissa);
126
127 // don't return special values: infinity, NaN
128 if (exponent == 0)
129 exponent = 0x80;
130
131 uint64_t bits = ((uint64_t)(sign) << 63) | ((uint64_t)(exponent) << (52 + 3)) | (uint64_t)mantissa;
132 std::memcpy(&value, &bits, sizeof(bits));
133 }
134
135 void Read(float& value)
136 {
137 BOOST_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t));
138
139 uint8_t sign;
140 uint8_t exponent;
141 uint16_t mantissa;
142
143 Read(sign);
144 Read(exponent);
145 Read(mantissa);
146
147 // don't return special values: infinity, NaN
148 if (exponent == 0 || exponent == 0xff)
149 exponent = 0x80;
150
151 uint32_t bits = ((uint32_t)(sign) << 31) | ((uint32_t)(exponent) << 23) | (uint32_t)mantissa;
152 std::memcpy(&value, &bits, sizeof(bits));
153 }
154
155 template <typename T>
156 typename boost::enable_if<is_string_type<T> >::type
157 Read(T& value)
158 {
159 uint32_t length = 0;
160
161 Read(length);
162
163 length %= _max_string_length;
164 length = length ? length : 1;
165
166 resize_string(value, length);
167
168 typename element_type<T>::type* p = string_data(value);
169 typename element_type<T>::type* const p_end = p + length;
170
171 for (; p != p_end; ++p)
172 {
173 uint8_t c;
174
175 Read(c);
176
177 *p = typename element_type<T>::type(' ') + c % ('z' - ' ');
178 }
179 }
180
181 // Read for blob
182 void Read(blob& value, uint32_t size)
183 {
184 boost::shared_ptr<char[]> buffer(boost::make_shared_noinit<char[]>(size));
185 char* p = buffer.get();
186 char* const p_end = p + size;
187
188 for (; p != p_end; ++p)
189 {
190 Read(*p);
191 }
192
193 value.assign(buffer, size);
194 }
195
196 template <typename T>
197 void Skip()
198 {}
199
200 template <typename T>
201 void Skip(const T&)
202 {}
203
204 template <typename T>
205 void ReadContainerBegin(uint32_t& size, T&)
206 {
207 Read(size);
208
209 size %= _max_list_size;
210 size = size ? size : 1;
211 }
212
213 void ReadContainerEnd() const
214 {
215 }
216
217private:
218 uint32_t _max_string_length;
219 uint32_t _max_list_size;
220 bool _json;
221};
222
223
224template <typename Unused> struct
225uses_marshaled_bonded<RandomProtocolReader, Unused>
226 : std::false_type {};
227
228template <typename Unused> struct
229uses_marshaled_bonded<RandomProtocolReader&, Unused>
230 : std::false_type {};
231
232}
namespace bond
Definition: apply.h:17
boost::enable_if< is_signed_int_or_enum< SignedT >, bool >::type operator==(const Variant &variant, SignedT value)
Compares variant for equality against the provided signed integer or enum value.
Definition: metadata.h:24