DCCL v3
field_codec_default.h
1 // Copyright 2009-2017 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (for 2013-)
3 // Massachusetts Institute of Technology (for 2007-2014)
4 // Community contributors (see AUTHORS file)
5 //
6 //
7 // This file is part of the Dynamic Compact Control Language Library
8 // ("DCCL").
9 //
10 // DCCL is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 2.1 of the License, or
13 // (at your option) any later version.
14 //
15 // DCCL is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with DCCL. If not, see <http://www.gnu.org/licenses/>.
22 // implements FieldCodecBase for all the basic DCCL types
23 
24 #ifndef DCCLFIELDCODECDEFAULT20110322H
25 #define DCCLFIELDCODECDEFAULT20110322H
26 
27 #include <sys/time.h>
28 
29 #include <boost/utility.hpp>
30 #include <boost/type_traits.hpp>
31 #include <boost/static_assert.hpp>
32 #include <boost/numeric/conversion/bounds.hpp>
33 #include <boost/numeric/conversion/cast.hpp>
34 #include <boost/lexical_cast.hpp>
35 
36 #include <google/protobuf/descriptor.h>
37 
38 #include "dccl/option_extensions.pb.h"
39 
40 #include "dccl/codecs2/field_codec_default_message.h"
41 #include "dccl/field_codec_fixed.h"
42 #include "dccl/field_codec.h"
43 #include "dccl/binary.h"
44 
45 namespace dccl
46 {
48  namespace v2
49  {
53  template<typename WireType, typename FieldType = WireType>
54  class DefaultNumericFieldCodec : public TypedFixedFieldCodec<WireType, FieldType>
55  {
56  protected:
57 
58  virtual double max()
59  { return FieldCodecBase::dccl_field_options().max(); }
60 
61  virtual double min()
62  { return FieldCodecBase::dccl_field_options().min(); }
63 
64  virtual double precision()
65  { return FieldCodecBase::dccl_field_options().precision(); }
66 
67  virtual void validate()
68  {
70  "missing (dccl.field).min");
72  "missing (dccl.field).max");
73 
74  validate_numeric_bounds();
75  }
76 
77  void validate_numeric_bounds()
78  {
79 
80  // ensure given max and min fit within WireType ranges
81  FieldCodecBase::require(min() >= boost::numeric::bounds<WireType>::lowest(),
82  "(dccl.field).min must be >= minimum of this field type.");
83  FieldCodecBase::require(max() <= boost::numeric::bounds<WireType>::highest(),
84  "(dccl.field).max must be <= maximum of this field type.");
85 
86 
87  // ensure value fits into double
88  FieldCodecBase::require((precision() + std::ceil(std::log10(max() - min()))) <= std::numeric_limits<double>::digits10,
89  "[(dccl.field).max-(dccl.field).min]*10^(dccl.field).precision must fit in a double-precision floating point value. Please increase min, decrease max, or decrease precision.");
90 
91  }
92 
93 
95  {
96  return Bitset(size());
97  }
98 
99 
100  virtual Bitset encode(const WireType& value)
101  {
102  // round first, before checking bounds
103  WireType wire_value = dccl::round(value, precision());
104 
105  // check bounds, if out-of-bounds, send as zeros
106  if(wire_value < min() || wire_value > max())
107  return Bitset(size());
108 
109  wire_value -= dccl::round((WireType)min(), precision());
110 
111  if (precision() < 0) {
112  wire_value /= (WireType)std::pow(10.0, -precision());
113  } else if (precision() > 0) {
114  wire_value *= (WireType)std::pow(10.0, precision());
115  }
116 
117  dccl::uint64 uint_value = boost::numeric_cast<dccl::uint64>(dccl::round(wire_value, 0));
118 
119  // "presence" value (0)
121  uint_value += 1;
122 
123 
124  Bitset encoded;
125  encoded.from(uint_value, size());
126  return encoded;
127  }
128 
129  virtual WireType decode(Bitset* bits)
130  {
131  // The line below SHOULD BE:
132  // dccl::uint64 t = bits->to<dccl::uint64>();
133  // But GCC3.3 requires an explicit template modifier on the method.
134  // See, e.g., http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10959
135  dccl::uint64 uint_value = (bits->template to<dccl::uint64>)();
136 
138  {
139  if(!uint_value) throw NullValueException();
140  --uint_value;
141  }
142 
143  WireType wire_value = (WireType)uint_value;
144 
145  if (precision() < 0) {
146  wire_value *= (WireType)std::pow(10.0, -precision());
147  } else if (precision() > 0) {
148  wire_value /= (WireType)std::pow(10.0, precision());
149  }
150 
151  // round values again to properly handle cases where double precision
152  // leads to slightly off values (e.g. 2.099999999 instead of 2.1)
153  wire_value = dccl::round(wire_value + dccl::round((WireType)min(), precision()),
154  precision());
155 
156  return wire_value;
157  }
158 
159  unsigned size()
160  {
161  // if not required field, leave one value for unspecified (always encoded as 0)
162  unsigned NULL_VALUE = FieldCodecBase::use_required() ? 0 : 1;
163 
164  return dccl::ceil_log2((max()-min())*std::pow(10.0, precision())+1 + NULL_VALUE);
165  }
166 
167  };
168 
173  {
174  private:
175  Bitset encode(const bool& wire_value);
176  Bitset encode();
177  bool decode(Bitset* bits);
178  unsigned size();
179  void validate();
180  };
181 
185  class DefaultStringCodec : public TypedFieldCodec<std::string>
186  {
187  private:
188  Bitset encode();
189  Bitset encode(const std::string& wire_value);
190  std::string decode(Bitset* bits);
191  unsigned size();
192  unsigned size(const std::string& wire_value);
193  unsigned max_size();
194  unsigned min_size();
195  void validate();
196  private:
197  enum { MAX_STRING_LENGTH = 255 };
198 
199  };
200 
201 
203  class DefaultBytesCodec : public TypedFieldCodec<std::string>
204  {
205  private:
206  Bitset encode();
207  Bitset encode(const std::string& wire_value);
208  std::string decode(Bitset* bits);
209  unsigned size();
210  unsigned size(const std::string& wire_value);
211  unsigned max_size();
212  unsigned min_size();
213  void validate();
214  };
215 
218  : public DefaultNumericFieldCodec<int32, const google::protobuf::EnumValueDescriptor*>
219  {
220  public:
221  int32 pre_encode(const google::protobuf::EnumValueDescriptor* const& field_value);
222  const google::protobuf::EnumValueDescriptor* post_decode(const int32& wire_value);
223 
224  private:
225  void validate() { }
226 
227  double max()
228  {
229  const google::protobuf::EnumDescriptor* e = this_field()->enum_type();
230  return e->value_count()-1;
231  }
232  double min()
233  { return 0; }
234  };
235 
236 
240 
241  typedef double time_wire_type;
242  template<typename TimeType, int conversion_factor>
243  class TimeCodecBase : public DefaultNumericFieldCodec<time_wire_type, TimeType>
244  {
245  public:
246  time_wire_type pre_encode(const TimeType& time_of_day) {
247  time_wire_type max_secs = max();
248  return std::fmod(time_of_day / static_cast<time_wire_type>(conversion_factor), max_secs);
249  }
250 
251  TimeType post_decode(const time_wire_type& encoded_time) {
252 
253  int64 max_secs = (int64)max();
254  timeval t;
255  gettimeofday(&t, 0);
256  int64 now = t.tv_sec;
257  int64 daystart = now - (now % max_secs);
258  int64 today_time = now - daystart;
259 
260  // If time is more than 12 hours ahead of now, assume it's yesterday.
261  if ((encoded_time - today_time) > (max_secs/2)) {
262  daystart -= max_secs;
263  } else if ((today_time - encoded_time) > (max_secs/2)) {
264  daystart += max_secs;
265  }
266 
267  return dccl::round((TimeType)(conversion_factor * (daystart + encoded_time)),
268  precision() - std::log10((double)conversion_factor));
269  }
270 
271  private:
272  void validate()
273  {
275  }
276 
277  double max() {
278  return FieldCodecBase::dccl_field_options().num_days() * SECONDS_IN_DAY;
279  }
280 
281  double min() { return 0; }
282  double precision()
283  {
284  if(!FieldCodecBase::dccl_field_options().has_precision())
285  return 0; // default to second precision
286  else
287  {
288  return FieldCodecBase::dccl_field_options().precision() + (double)std::log10((double)conversion_factor);
289  }
290 
291  }
292 
293 
294  private:
295  enum { SECONDS_IN_DAY = 86400 };
296  };
297 
298  template<typename TimeType>
299  class TimeCodec : public TimeCodecBase<TimeType, 0>
300  { BOOST_STATIC_ASSERT(sizeof(TimeCodec) == 0); };
301 
302  template<> class TimeCodec<uint64> : public TimeCodecBase<uint64, 1000000> { };
303  template<> class TimeCodec<int64> : public TimeCodecBase<int64, 1000000> { };
304  template<> class TimeCodec<double> : public TimeCodecBase<double, 1> { };
305 
306 
308  template<typename T>
310  {
311  Bitset encode(const T&)
312  { return Bitset(size()); }
313 
314  Bitset encode()
315  { return Bitset(size()); }
316 
317  T decode(Bitset* bits)
318  {
319  return boost::lexical_cast<T>(
320  FieldCodecBase::dccl_field_options().static_value());
321  }
322 
323  unsigned size()
324  { return 0; }
325 
326  void validate()
327  {
328  FieldCodecBase::require(FieldCodecBase::dccl_field_options().has_static_value(), "missing (dccl.field).static_value");
329 
330  std::string t = FieldCodecBase::dccl_field_options().static_value();
331  try
332  {
333  boost::lexical_cast<T>(t);
334  }
335  catch(boost::bad_lexical_cast&)
336  {
337  FieldCodecBase::require(false, "invalid static_value for this type.");
338  }
339  }
340 
341  };
342  }
343 }
344 
345 
346 #endif
Provides an variable length ASCII string encoder. Can encode strings up to 255 bytes by using a lengt...
void from(IntType value, size_type num_bits=std::numeric_limits< IntType >::digits)
Sets value of the Bitset to the contents of an integer.
Definition: bitset.h:245
dccl::DCCLFieldOptions dccl_field_options() const
Get the DCCL field option extension value for the current field.
Definition: field_codec.h:317
virtual WireType decode(Bitset *bits)
Decode a field. If the field is empty (i.e. was encoded using the zero-argument encode()), throw NullValueException to indicate this.
Exception used to signal null (non-existent) value within field codecs during decode.
Definition: exception.h:39
time_wire_type pre_encode(const TimeType &time_of_day)
Convert from the FieldType representation (used in the Google Protobuf message) to the WireType repre...
Bitset encode()
Encode an empty field.
Provides an fixed length byte string encoder.
virtual WireType pre_encode(const FieldType &field_value)=0
Convert from the FieldType representation (used in the Google Protobuf message) to the WireType repre...
const google::protobuf::FieldDescriptor * this_field() const
Returns the FieldDescriptor (field schema meta-data) for this field.
Definition: field_codec.h:75
void require(bool b, const std::string &description)
Essentially an assertion to be used in the validate() virtual method.
Definition: field_codec.h:331
unsigned size()
The size of the encoded message in bits. Use TypedFieldCodec if the size depends on the data...
google::protobuf::int64 int64
a signed 64 bit integer
Definition: common.h:61
double time_wire_type
Encodes time of day (default: second precision, but can be set with (dccl.field).precision extension)...
unsigned ceil_log2(dccl::uint64 v)
Definition: binary.h:178
Provides a basic bounded arbitrary length numeric (double, float, uint32, uint64, int32...
google::protobuf::uint64 uint64
an unsigned 64 bit integer
Definition: common.h:59
Base class for static-typed (no boost::any) field encoders/decoders. Most single-valued user defined ...
Placeholder codec that takes no space on the wire (0 bits).
virtual void validate()
Validate a field. Use require() inside your overloaded validate() to assert requirements or throw Exc...
boost::enable_if< boost::is_integral< Int >, Int >::type round(Int value, int precision)
Definition: common.h:97
virtual Bitset encode(const WireType &value)
Encode a non-empty field.
Dynamic Compact Control Language namespace.
Base class for static-typed field encoders/decoders that use a fixed number of bits on the wire regar...
Provides an enum encoder. This converts the enumeration to an integer (based on the enumeration index...
virtual FieldType post_decode(const WireType &wire_value)=0
Convert from the WireType representation (used with encode() and decode(), i.e. "on the wire") to the...
google::protobuf::int32 int32
a signed 32 bit integer
Definition: common.h:57
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy...
Definition: bitset.h:38
bool use_required()
Whether to use the required or optional encoding.
Definition: field_codec.h:344
Provides a bool encoder. Uses 1 bit if field is required, 2 bits if optional