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  {
283  DefaultNumericFieldCodec<time_wire_type, TimeType>::validate_numeric_bounds();
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
dccl::int32
google::protobuf::int32 int32
a signed 32 bit integer
Definition: common.h:57
dccl::v2::StaticCodec
Placeholder codec that takes no space on the wire (0 bits).
Definition: field_codec_default.h:318
dccl::v2::DefaultNumericFieldCodec::validate
virtual void validate()
Validate a field. Use require() inside your overloaded validate() to assert requirements or throw Exc...
Definition: field_codec_default.h:67
dccl::v2::DefaultEnumCodec::pre_encode
int32 pre_encode(const google::protobuf::EnumValueDescriptor *const &field_value)
Convert from the FieldType representation (used in the Google Protobuf message) to the WireType repre...
Definition: field_codec_default.cpp:268
dccl::FieldCodecBase::this_field
const google::protobuf::FieldDescriptor * this_field() const
Returns the FieldDescriptor (field schema meta-data) for this field.
Definition: field_codec.h:75
dccl
Dynamic Compact Control Language namespace.
Definition: gen_units_class_plugin.h:49
dccl::v2::DefaultNumericFieldCodec::encode
virtual Bitset encode(const WireType &value)
Encode a non-empty field.
Definition: field_codec_default.h:100
dccl::v2::TimeCodec
Definition: field_codec_default.h:308
dccl::uint64
google::protobuf::uint64 uint64
an unsigned 64 bit integer
Definition: common.h:59
dccl::TypedFixedFieldCodec
Base class for static-typed field encoders/decoders that use a fixed number of bits on the wire regar...
Definition: field_codec_fixed.h:34
dccl::FieldCodecBase::require
void require(bool b, const std::string &description)
Essentially an assertion to be used in the validate() virtual method.
Definition: field_codec.h:340
dccl::v2::TimeCodecBase
Encodes time of day (default: second precision, but can be set with (dccl.field).precision extension)
Definition: field_codec_default.h:252
dccl::Bitset::from
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::ceil_log2
unsigned ceil_log2(dccl::uint64 v)
Definition: binary.h:180
dccl::v2::DefaultStringCodec
Provides an variable length ASCII string encoder. Can encode strings up to 255 bytes by using a lengt...
Definition: field_codec_default.h:195
dccl::v2::TimeCodecBase::pre_encode
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...
Definition: field_codec_default.h:255
dccl::NullValueException
Exception used to signal null (non-existent) value within field codecs during decode.
Definition: exception.h:40
dccl::Bitset
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy....
Definition: bitset.h:38
dccl::v2::DefaultBoolCodec
Provides a bool encoder. Uses 1 bit if field is required, 2 bits if optional
Definition: field_codec_default.h:182
dccl::int64
google::protobuf::int64 int64
a signed 64 bit integer
Definition: common.h:61
dccl::OutOfRangeException
Definition: exception.h:48
dccl::v2::DefaultEnumCodec
Provides an enum encoder. This converts the enumeration to an integer (based on the enumeration index...
Definition: field_codec_default.h:227
dccl::TypedFieldCodec
Base class for static-typed (no boost::any) field encoders/decoders. Most single-valued user defined ...
Definition: field_codec_typed.h:82
dccl::v2::DefaultBytesCodec
Provides an fixed length byte string encoder.
Definition: field_codec_default.h:213
dccl::FieldCodecBase::use_required
bool use_required()
Whether to use the required or optional encoding.
Definition: field_codec.h:353
dccl::v2::DefaultNumericFieldCodec::decode
virtual WireType decode(Bitset *bits)
Decode a field. If the field is empty (i.e. was encoded using the zero-argument encode()),...
Definition: field_codec_default.h:136
dccl::v2::DefaultNumericFieldCodec
Provides a basic bounded arbitrary length numeric (double, float, uint32, uint64, int32,...
Definition: field_codec_default.h:54
dccl::FieldCodecBase::dccl_field_options
dccl::DCCLFieldOptions dccl_field_options() const
Get the DCCL field option extension value for the current field.
Definition: field_codec.h:326
dccl::v2::DefaultNumericFieldCodec::encode
Bitset encode()
Encode an empty field.
Definition: field_codec_default.h:94
dccl::v2::DefaultNumericFieldCodec::size
unsigned size()
The size of the encoded message in bits. Use TypedFieldCodec if the size depends on the data.
Definition: field_codec_default.h:169
dccl::v2::DefaultEnumCodec::post_decode
const google::protobuf::EnumValueDescriptor * post_decode(const int32 &wire_value)
Convert from the WireType representation (used with encode() and decode(), i.e. "on the wire") to the...
Definition: field_codec_default.cpp:273