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  public:
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  // bring size(const WireType&) into scope so callers can access it
168 
169  unsigned size()
170  {
171  // if not required field, leave one value for unspecified (always encoded as 0)
172  unsigned NULL_VALUE = FieldCodecBase::use_required() ? 0 : 1;
173 
174  return dccl::ceil_log2((max()-min())*std::pow(10.0, precision())+1 + NULL_VALUE);
175  }
176 
177  };
178 
183  {
184  private:
185  Bitset encode(const bool& wire_value);
186  Bitset encode();
187  bool decode(Bitset* bits);
188  unsigned size();
189  void validate();
190  };
191 
195  class DefaultStringCodec : public TypedFieldCodec<std::string>
196  {
197  private:
198  Bitset encode();
199  Bitset encode(const std::string& wire_value);
200  std::string decode(Bitset* bits);
201  unsigned size();
202  unsigned size(const std::string& wire_value);
203  unsigned max_size();
204  unsigned min_size();
205  void validate();
206  private:
207  enum { MAX_STRING_LENGTH = 255 };
208 
209  };
210 
211 
213  class DefaultBytesCodec : public TypedFieldCodec<std::string>
214  {
215  private:
216  Bitset encode();
217  Bitset encode(const std::string& wire_value);
218  std::string decode(Bitset* bits);
219  unsigned size();
220  unsigned size(const std::string& wire_value);
221  unsigned max_size();
222  unsigned min_size();
223  void validate();
224  };
225 
228  : public DefaultNumericFieldCodec<int32, const google::protobuf::EnumValueDescriptor*>
229  {
230  public:
231  int32 pre_encode(const google::protobuf::EnumValueDescriptor* const& field_value);
232  const google::protobuf::EnumValueDescriptor* post_decode(const int32& wire_value);
233 
234  private:
235  void validate() { }
236 
237  double max()
238  {
239  const google::protobuf::EnumDescriptor* e = this_field()->enum_type();
240  return e->value_count()-1;
241  }
242  double min()
243  { return 0; }
244  };
245 
246 
247  typedef double time_wire_type;
251  template<typename TimeType, int conversion_factor>
252  class TimeCodecBase : public DefaultNumericFieldCodec<time_wire_type, TimeType>
253  {
254  public:
255  time_wire_type pre_encode(const TimeType& time_of_day) {
256  time_wire_type max_secs = max();
257  return std::fmod(time_of_day / static_cast<time_wire_type>(conversion_factor), max_secs);
258  }
259 
260  TimeType post_decode(const time_wire_type& encoded_time) {
261 
262  int64 max_secs = (int64)max();
263  timeval t;
264  gettimeofday(&t, 0);
265  int64 now = t.tv_sec;
266  int64 daystart = now - (now % max_secs);
267  int64 today_time = now - daystart;
268 
269  // If time is more than 12 hours ahead of now, assume it's yesterday.
270  if ((encoded_time - today_time) > (max_secs/2)) {
271  daystart -= max_secs;
272  } else if ((today_time - encoded_time) > (max_secs/2)) {
273  daystart += max_secs;
274  }
275 
276  return dccl::round((TimeType)(conversion_factor * (daystart + encoded_time)),
277  precision() - std::log10((double)conversion_factor));
278  }
279 
280  private:
281  void validate()
282  {
284  }
285 
286  double max() {
287  return FieldCodecBase::dccl_field_options().num_days() * SECONDS_IN_DAY;
288  }
289 
290  double min() { return 0; }
291  double precision()
292  {
293  if(!FieldCodecBase::dccl_field_options().has_precision())
294  return 0; // default to second precision
295  else
296  {
297  return FieldCodecBase::dccl_field_options().precision() + (double)std::log10((double)conversion_factor);
298  }
299 
300  }
301 
302 
303  private:
304  enum { SECONDS_IN_DAY = 86400 };
305  };
306 
307  template<typename TimeType>
308  class TimeCodec : public TimeCodecBase<TimeType, 0>
309  { BOOST_STATIC_ASSERT(sizeof(TimeCodec) == 0); };
310 
311  template<> class TimeCodec<uint64> : public TimeCodecBase<uint64, 1000000> { };
312  template<> class TimeCodec<int64> : public TimeCodecBase<int64, 1000000> { };
313  template<> class TimeCodec<double> : public TimeCodecBase<double, 1> { };
314 
315 
317  template<typename T>
319  {
320  Bitset encode(const T&)
321  { return Bitset(size()); }
322 
323  Bitset encode()
324  { return Bitset(size()); }
325 
326  T decode(Bitset* bits)
327  {
328  return boost::lexical_cast<T>(
329  FieldCodecBase::dccl_field_options().static_value());
330  }
331 
332  unsigned size()
333  { return 0; }
334 
335  void validate()
336  {
337  FieldCodecBase::require(FieldCodecBase::dccl_field_options().has_static_value(), "missing (dccl.field).static_value");
338 
339  std::string t = FieldCodecBase::dccl_field_options().static_value();
340  try
341  {
342  boost::lexical_cast<T>(t);
343  }
344  catch(boost::bad_lexical_cast&)
345  {
346  FieldCodecBase::require(false, "invalid static_value for this type.");
347  }
348  }
349 
350  };
351  }
352 }
353 
354 
355 #endif
Provides an variable length ASCII string encoder. Can encode strings up to 255 bytes by using a lengt...
Encodes time of day (default: second precision, but can be set with (dccl.field).precision extension)...
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:326
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:340
unsigned size()
The size of the encoded message in bits. Use TypedFieldCodec if the size depends on the data...
unsigned min_size()
Calculate minimum size of the field in bits.
google::protobuf::int64 int64
a signed 64 bit integer
Definition: common.h:61
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...
unsigned max_size()
Calculate maximum size of the field in bits.
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:353
Provides a bool encoder. Uses 1 bit if field is required, 2 bits if optional