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
106  if(wire_value < min() || wire_value > max())
107  {
108  // strict mode
109  if(this->strict())
110  throw(dccl::OutOfRangeException(std::string("Value exceeds min/max bounds for field: ") + FieldCodecBase::this_field()->DebugString(), this->this_field()));
111  // non-strict (default): if out-of-bounds, send as zeros
112  else
113  return Bitset(size());
114  }
115 
116  wire_value -= dccl::round((WireType)min(), precision());
117 
118  if (precision() < 0) {
119  wire_value /= (WireType)std::pow(10.0, -precision());
120  } else if (precision() > 0) {
121  wire_value *= (WireType)std::pow(10.0, precision());
122  }
123 
124  dccl::uint64 uint_value = boost::numeric_cast<dccl::uint64>(dccl::round(wire_value, 0));
125 
126  // "presence" value (0)
128  uint_value += 1;
129 
130 
131  Bitset encoded;
132  encoded.from(uint_value, size());
133  return encoded;
134  }
135 
136  virtual WireType decode(Bitset* bits)
137  {
138  // The line below SHOULD BE:
139  // dccl::uint64 t = bits->to<dccl::uint64>();
140  // But GCC3.3 requires an explicit template modifier on the method.
141  // See, e.g., http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10959
142  dccl::uint64 uint_value = (bits->template to<dccl::uint64>)();
143 
145  {
146  if(!uint_value) throw NullValueException();
147  --uint_value;
148  }
149 
150  WireType wire_value = (WireType)uint_value;
151 
152  if (precision() < 0) {
153  wire_value *= (WireType)std::pow(10.0, -precision());
154  } else if (precision() > 0) {
155  wire_value /= (WireType)std::pow(10.0, precision());
156  }
157 
158  // round values again to properly handle cases where double precision
159  // leads to slightly off values (e.g. 2.099999999 instead of 2.1)
160  wire_value = dccl::round(wire_value + dccl::round((WireType)min(), precision()),
161  precision());
162 
163  return wire_value;
164  }
165 
166  unsigned size()
167  {
168  // if not required field, leave one value for unspecified (always encoded as 0)
169  unsigned NULL_VALUE = FieldCodecBase::use_required() ? 0 : 1;
170 
171  return dccl::ceil_log2((max()-min())*std::pow(10.0, precision())+1 + NULL_VALUE);
172  }
173 
174  };
175 
180  {
181  private:
182  Bitset encode(const bool& wire_value);
183  Bitset encode();
184  bool decode(Bitset* bits);
185  unsigned size();
186  void validate();
187  };
188 
192  class DefaultStringCodec : public TypedFieldCodec<std::string>
193  {
194  private:
195  Bitset encode();
196  Bitset encode(const std::string& wire_value);
197  std::string decode(Bitset* bits);
198  unsigned size();
199  unsigned size(const std::string& wire_value);
200  unsigned max_size();
201  unsigned min_size();
202  void validate();
203  private:
204  enum { MAX_STRING_LENGTH = 255 };
205 
206  };
207 
208 
210  class DefaultBytesCodec : public TypedFieldCodec<std::string>
211  {
212  private:
213  Bitset encode();
214  Bitset encode(const std::string& wire_value);
215  std::string decode(Bitset* bits);
216  unsigned size();
217  unsigned size(const std::string& wire_value);
218  unsigned max_size();
219  unsigned min_size();
220  void validate();
221  };
222 
225  : public DefaultNumericFieldCodec<int32, const google::protobuf::EnumValueDescriptor*>
226  {
227  public:
228  int32 pre_encode(const google::protobuf::EnumValueDescriptor* const& field_value);
229  const google::protobuf::EnumValueDescriptor* post_decode(const int32& wire_value);
230 
231  private:
232  void validate() { }
233 
234  double max()
235  {
236  const google::protobuf::EnumDescriptor* e = this_field()->enum_type();
237  return e->value_count()-1;
238  }
239  double min()
240  { return 0; }
241  };
242 
243 
247 
248  typedef double time_wire_type;
249  template<typename TimeType, int conversion_factor>
250  class TimeCodecBase : public DefaultNumericFieldCodec<time_wire_type, TimeType>
251  {
252  public:
253  time_wire_type pre_encode(const TimeType& time_of_day) {
254  time_wire_type max_secs = max();
255  return std::fmod(time_of_day / static_cast<time_wire_type>(conversion_factor), max_secs);
256  }
257 
258  TimeType post_decode(const time_wire_type& encoded_time) {
259 
260  int64 max_secs = (int64)max();
261  timeval t;
262  gettimeofday(&t, 0);
263  int64 now = t.tv_sec;
264  int64 daystart = now - (now % max_secs);
265  int64 today_time = now - daystart;
266 
267  // If time is more than 12 hours ahead of now, assume it's yesterday.
268  if ((encoded_time - today_time) > (max_secs/2)) {
269  daystart -= max_secs;
270  } else if ((today_time - encoded_time) > (max_secs/2)) {
271  daystart += max_secs;
272  }
273 
274  return dccl::round((TimeType)(conversion_factor * (daystart + encoded_time)),
275  precision() - std::log10((double)conversion_factor));
276  }
277 
278  private:
279  void validate()
280  {
282  }
283 
284  double max() {
285  return FieldCodecBase::dccl_field_options().num_days() * SECONDS_IN_DAY;
286  }
287 
288  double min() { return 0; }
289  double precision()
290  {
291  if(!FieldCodecBase::dccl_field_options().has_precision())
292  return 0; // default to second precision
293  else
294  {
295  return FieldCodecBase::dccl_field_options().precision() + (double)std::log10((double)conversion_factor);
296  }
297 
298  }
299 
300 
301  private:
302  enum { SECONDS_IN_DAY = 86400 };
303  };
304 
305  template<typename TimeType>
306  class TimeCodec : public TimeCodecBase<TimeType, 0>
307  { BOOST_STATIC_ASSERT(sizeof(TimeCodec) == 0); };
308 
309  template<> class TimeCodec<uint64> : public TimeCodecBase<uint64, 1000000> { };
310  template<> class TimeCodec<int64> : public TimeCodecBase<int64, 1000000> { };
311  template<> class TimeCodec<double> : public TimeCodecBase<double, 1> { };
312 
313 
315  template<typename T>
317  {
318  Bitset encode(const T&)
319  { return Bitset(size()); }
320 
321  Bitset encode()
322  { return Bitset(size()); }
323 
324  T decode(Bitset* bits)
325  {
326  return boost::lexical_cast<T>(
327  FieldCodecBase::dccl_field_options().static_value());
328  }
329 
330  unsigned size()
331  { return 0; }
332 
333  void validate()
334  {
335  FieldCodecBase::require(FieldCodecBase::dccl_field_options().has_static_value(), "missing (dccl.field).static_value");
336 
337  std::string t = FieldCodecBase::dccl_field_options().static_value();
338  try
339  {
340  boost::lexical_cast<T>(t);
341  }
342  catch(boost::bad_lexical_cast&)
343  {
344  FieldCodecBase::require(false, "invalid static_value for this type.");
345  }
346  }
347 
348  };
349  }
350 }
351 
352 
353 #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:321
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:40
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:335
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:180
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:348
Provides a bool encoder. Uses 1 bit if field is required, 2 bits if optional