DCCL v5
Loading...
Searching...
No Matches
field_codec_default.h
1// Copyright 2009-2024:
2// GobySoft, LLC (2013-)
3// Massachusetts Institute of Technology (2007-2014)
4// Community contributors (see AUTHORS file)
5// File authors:
6// Toby Schneider <toby@gobysoft.org>
7//
8//
9// This file is part of the Dynamic Compact Control Language Library
10// ("DCCL").
11//
12// DCCL is free software: you can redistribute it and/or modify
13// it under the terms of the GNU Lesser General Public License as published by
14// the Free Software Foundation, either version 2.1 of the License, or
15// (at your option) any later version.
16//
17// DCCL is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU Lesser General Public License for more details.
21//
22// You should have received a copy of the GNU Lesser General Public License
23// along with DCCL. If not, see <http://www.gnu.org/licenses/>.
24// implements FieldCodecBase for all the basic DCCL types for version 5
25
26#ifndef DCCLV5FIELDCODECDEFAULT20240101H
27#define DCCLV5FIELDCODECDEFAULT20240101H
28
29#include "../codecs4/field_codec_default.h"
30
31#include <type_traits>
32#include "../numeric.h"
33
34namespace dccl
35{
37namespace v5
38{
39
40// v5 DefaultNumericFieldCodec is the same fundamental algorithm as v2-v4 but with fixed numeric precision at edge cases: https://github.com/GobySoft/dccl/pull/152
41
45template <typename WireType, typename FieldType = WireType>
46class DefaultNumericFieldCodec : public TypedFixedFieldCodec<WireType, FieldType>
47{
48 public:
49 virtual double max()
50 {
51 DynamicConditions& dc = this->dynamic_conditions(this->this_field());
52
53 double static_max = this->dccl_field_options().max();
54 if (dc.has_max())
55 {
56 dc.regenerate(this->this_message(), this->root_message());
57 // don't let dynamic conditions breach static bounds
58 return std::max(this->dccl_field_options().min(), std::min(dc.max(), static_max));
59 }
60 else
61 {
62 return static_max;
63 }
64 }
65
66 virtual double min()
67 {
68 DynamicConditions& dc = this->dynamic_conditions(this->this_field());
69 double static_min = this->dccl_field_options().min();
70 if (dc.has_min())
71 {
72 dc.regenerate(this->this_message(), this->root_message());
73
74 // don't let dynamic conditions breach static bounds
75 return std::min(this->dccl_field_options().max(), std::max(dc.min(), static_min));
76 }
77 else
78 {
79 return static_min;
80 }
81 }
82
83 virtual double precision() { return FieldCodecBase::dccl_field_options().precision(); }
84
85 virtual double resolution()
86 {
87 if (FieldCodecBase::dccl_field_options().has_precision())
88 return std::pow(10.0, -precision());
89 // If none is set returns the default resolution (=1)
90 return FieldCodecBase::dccl_field_options().resolution();
91 }
92
93 void validate() override
94 {
96 "missing (dccl.field).min");
98 "missing (dccl.field).max");
99
101 "(dccl.field).resolution must be greater than 0");
103 !(FieldCodecBase::dccl_field_options().has_precision() &&
104 FieldCodecBase::dccl_field_options().has_resolution()),
105 "at most one of either (dccl.field).precision or (dccl.field).resolution can be set");
106
107 validate_numeric_bounds();
108 }
109
110 void validate_numeric_bounds()
111 {
112 // ensure given max and min fit within WireType ranges
113 FieldCodecBase::require(static_cast<WireType>(min()) >=
114 std::numeric_limits<WireType>::lowest(),
115 "(dccl.field).min must be >= minimum of this field type.");
116 FieldCodecBase::require(static_cast<WireType>(max()) <=
117 std::numeric_limits<WireType>::max(),
118 "(dccl.field).max must be <= maximum of this field type.");
119
120 // allowable epsilon for min / max to diverge from nearest quantile
121 const double min_max_eps = 1e-10;
122 bool min_multiple_of_res = std::abs(quantize(min(), resolution()) - min()) < min_max_eps;
123 bool max_multiple_of_res = std::abs(quantize(max(), resolution()) - max()) < min_max_eps;
124 if (FieldCodecBase::dccl_field_options().has_resolution())
125 {
126 // ensure that max and min are multiples of the resolution chosen
128 min_multiple_of_res,
129 "(dccl.field).min must be an exact multiple of (dccl.field).resolution");
131 max_multiple_of_res,
132 "(dccl.field).max must be an exact multiple of (dccl.field).resolution");
133 }
134 else
135 {
136 auto res = resolution();
137 // this was previously allowed so we will only give a warning not throw an exception
138 if (!min_multiple_of_res)
139 dccl::dlog.is(dccl::logger::WARN, dccl::logger::GENERAL) &&
140 dccl::dlog << "Warning: (dccl.field).min should be an exact multiple of "
141 "10^(-(dccl.field).precision), i.e. "
142 << res << ": " << this->this_field()->DebugString() << std::endl;
143 if (!max_multiple_of_res)
144 dccl::dlog.is(dccl::logger::WARN, dccl::logger::GENERAL) &&
145 dccl::dlog << "Warning: (dccl.field).max should be an exact multiple of "
146 "10^(-(dccl.field).precision), i.e. "
147 << res << ": " << this->this_field()->DebugString() << std::endl;
148 }
149
150 // ensure value fits into double
151 FieldCodecBase::require(std::log2(max() - min()) - std::log2(resolution()) <=
152 std::numeric_limits<double>::digits,
153 "[(dccl.field).max-(dccl.field).min]/(dccl.field).resolution must "
154 "fit in a double-precision floating point value. Please increase "
155 "min, decrease max, or decrease precision.");
156 }
157
158 Bitset encode() override { return Bitset(size()); }
159
160 Bitset encode(const WireType& value) override
161 {
162 dccl::dlog.is(dccl::logger::DEBUG2, dccl::logger::ENCODE) &&
163 dlog << "Encode " << value << " with bounds: [" << min() << "," << max() << "]"
164 << std::endl;
165
166 double res = resolution();
167 double tol = res / 2;
168 // check bounds in the input space (NaN-correct)
169 if (!(min() - tol <= value && value < max() + tol))
170 {
171 // strict mode
172 if (this->strict())
174 std::string("Value exceeds min/max bounds for field: ") +
175 FieldCodecBase::this_field()->DebugString(),
176 this->this_field(), this->this_descriptor()));
177 // non-strict (default): if out-of-bounds, send as zeros
178 else
179 return Bitset(size());
180 }
181
182 dccl::uint64 uint_value = dccl::encode(value, min(), res);
183
184 // "presence" value (0)
186 uint_value += 1;
187
188 Bitset encoded;
189 encoded.from(uint_value, size());
190 return encoded;
191 }
192
193 WireType decode(Bitset* bits) override
194 {
195 dccl::dlog.is(dccl::logger::DEBUG2, dccl::logger::DECODE) &&
196 dlog << "Decode with bounds: [" << min() << "," << max() << "]" << std::endl;
197
198 // The line below SHOULD BE:
199 // dccl::uint64 t = bits->to<dccl::uint64>();
200 // But GCC3.3 requires an explicit template modifier on the method.
201 // See, e.g., http://gcc.gnu.org/bugzilla/show_bug.cgi?id=10959
202 dccl::uint64 uint_value = (bits->template to<dccl::uint64>)();
203
205 {
206 if (!uint_value)
207 throw NullValueException();
208 --uint_value;
209 }
210
211 return dccl::decode<WireType>(uint_value, min(), resolution());
212 }
213
214 // bring size(const WireType&) into scope so callers can access it
215 using TypedFixedFieldCodec<WireType, FieldType>::size;
216
217 unsigned size() override
218 {
219 // if not required field, leave one value for unspecified (always encoded as 0)
220 unsigned NULL_VALUE = FieldCodecBase::use_required() ? 0 : 1;
221
222 return dccl::ceil_log2((max() - min()) / resolution() + 1 + NULL_VALUE);
223 }
224};
225
226// all these are the same as version 4
227using DefaultBoolCodec = v4::DefaultBoolCodec;
228using DefaultEnumCodec = v4::DefaultEnumCodec;
229
230using DefaultBytesCodec = v4::DefaultBytesCodec;
231using DefaultStringCodec = v4::DefaultStringCodec;
232
233template <typename TimeType> using TimeCodec = v4::TimeCodec<TimeType>;
234template <typename T> using StaticCodec = v4::StaticCodec<T>;
235
236} // namespace v5
237} // namespace dccl
238
239#endif
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy....
Definition bitset.h:43
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:236
const google::protobuf::Descriptor * this_descriptor() const
Returns the Descriptor (message schema meta-data) for the immediate parent Message.
const google::protobuf::FieldDescriptor * this_field() const
Returns the FieldDescriptor (field schema meta-data) for this field.
void require(bool b, const std::string &description)
Essentially an assertion to be used in the validate() virtual method.
dccl::DCCLFieldOptions dccl_field_options() const
Get the DCCL field option extension value for the current field.
bool use_required()
Whether to use the required or optional encoding.
bool is(logger::Verbosity verbosity, logger::Group group=logger::GENERAL)
Indicates the verbosity of the Logger until the next std::flush or std::endl. The boolean return is u...
Definition logger.h:191
Exception used to signal null (non-existent) value within field codecs during decode.
Definition exception.h:62
Base class for static-typed field encoders/decoders that use a fixed number of bits on the wire regar...
Provides a bool encoder. Uses 1 bit if field is required, 2 bits if optional
Placeholder codec that takes no space on the wire (0 bits).
Provides an enum encoder. This converts the enumeration to an integer and uses DefaultNumericFieldCod...
Provides a basic bounded arbitrary length numeric (double, float, uint32, uint64, int32,...
WireType decode(Bitset *bits) override
Decode a field. If the field is empty (i.e. was encoded using the zero-argument encode()),...
Bitset encode(const WireType &value) override
Encode a non-empty field.
Bitset encode() override
Encode an empty field.
void validate() override
Validate a field. Use require() inside your overloaded validate() to assert requirements or throw Exc...
unsigned size() override
The size of the encoded message in bits. Use TypedFieldCodec if the size depends on the data.
Dynamic Compact Control Language namespace.
Definition any.h:28
google::protobuf::uint64 uint64
an unsigned 64 bit integer
Definition common.h:60
std::enable_if< std::is_floating_point< Float >::value, Float >::type quantize(Float value, double interval)
Definition common.h:90
unsigned ceil_log2(dccl::uint64 v)
Definition binary.h:163