DCCL v4
Loading...
Searching...
No Matches
field_codec_arithmetic.h
1// Copyright 2012-2023:
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// This code is adapted from the reference code provided by Witten et al. "Arithmetic Coding for Data Compression," Communications of the ACM, June 1987, Vol 30, Number 6
25
26#ifndef DCCLFIELDCODECARITHMETIC20120726H
27#define DCCLFIELDCODECARITHMETIC20120726H
28
29#include <algorithm>
30#include <limits>
31#include <utility>
32
33#include "../field_codec_typed.h"
34
35#include "dccl/arithmetic/protobuf/arithmetic.pb.h"
36#include "dccl/arithmetic/protobuf/arithmetic_extensions.pb.h"
37
38#include "../logger.h"
39
40#include "../binary.h"
41#include "../thread_safety.h"
42
43extern "C"
44{
45 void dccl3_load(dccl::Codec* dccl);
46 void dccl3_unload(dccl::Codec* dccl);
47 void dccl_arithmetic_load(dccl::Codec* dccl);
48 void dccl_arithmetic_unload(dccl::Codec* dccl);
49}
50
51namespace dccl
52{
54namespace arith
55{
56class ModelManager;
57
58ModelManager& model_manager(FieldCodecManagerLocal& manager);
59
60class Model
61{
62 public:
63 typedef uint32 freq_type;
64 using symbol_type = int; // google protobuf RepeatedField size type
65 using value_type = double;
66
67 static constexpr symbol_type OUT_OF_RANGE_SYMBOL = -1;
68 static constexpr symbol_type EOF_SYMBOL = -2;
69 static constexpr symbol_type MIN_SYMBOL = EOF_SYMBOL;
70
71 static constexpr int CODE_VALUE_BITS = 32;
72 static constexpr int FREQUENCY_BITS = CODE_VALUE_BITS - 2;
73
74 static constexpr freq_type MAX_FREQUENCY = (1 << FREQUENCY_BITS) - 1;
75
76#if DCCL_THREAD_SUPPORT
77 static std::recursive_mutex last_bits_map_mutex;
78#define LOCK_LAST_BITS_MAP_MUTEX \
79 std::lock_guard<std::recursive_mutex> l(dccl::arith::Model::last_bits_map_mutex);
80#else
81#define LOCK_LAST_BITS_MAP_MUTEX
82#endif
83 // maps message name -> map of field name -> last size (bits)
84 static std::map<std::string, std::map<std::string, Bitset>> last_bits_map;
85
86 Model(protobuf::ArithmeticModel user) : user_model_(std::move(user)) {}
87
88 enum ModelState
89 {
90 ENCODER,
91 DECODER
92 };
93
94 symbol_type value_to_symbol(value_type value) const;
95 value_type symbol_to_value(symbol_type symbol) const;
96 symbol_type total_symbols() // EOF and OUT_OF_RANGE plus all user defined
97 {
98 return encoder_cumulative_freqs_.size();
99 }
100
101 const protobuf::ArithmeticModel& user_model() const { return user_model_; }
102
103 symbol_type max_symbol() const { return user_model_.frequency_size() - 1; }
104
105 freq_type total_freq(ModelState state) const
106 {
107 const auto& c_freqs =
108 (state == ENCODER) ? encoder_cumulative_freqs_ : decoder_cumulative_freqs_;
109
110 return c_freqs.at(max_symbol());
111 }
112
113 void update_model(symbol_type symbol, ModelState state);
114
115 std::pair<freq_type, freq_type> symbol_to_cumulative_freq(symbol_type symbol,
116 ModelState state) const;
117 std::pair<symbol_type, symbol_type>
118 cumulative_freq_to_symbol(std::pair<freq_type, freq_type> c_freq_pair, ModelState state) const;
119
120 friend class ModelManager;
121
122 private:
123 protobuf::ArithmeticModel user_model_;
124 std::map<symbol_type, freq_type> encoder_cumulative_freqs_;
125 std::map<symbol_type, freq_type> decoder_cumulative_freqs_;
126};
127
129{
130 public:
131 static void set_model(dccl::Codec& codec, const protobuf::ArithmeticModel& model);
132
133 Model& find(const std::string& name)
134 {
135 auto it = arithmetic_models_.find(name);
136 if (it == arithmetic_models_.end())
137 throw(Exception("Cannot find model called: " + name));
138 else
139 return it->second;
140 }
141
142 private:
143 void _set_model(const protobuf::ArithmeticModel& model)
144 {
145 Model new_model(model);
146 _create_and_validate_model(&new_model);
147 if (arithmetic_models_.count(model.name()))
148 arithmetic_models_.erase(model.name());
149 arithmetic_models_.insert(std::make_pair(model.name(), new_model));
150 }
151
152 void _create_and_validate_model(Model* model)
153 {
154 if (!model->user_model_.IsInitialized())
155 {
156 throw(Exception("Invalid model: " + model->user_model_.DebugString() +
157 "Missing fields: " + model->user_model_.InitializationErrorString()));
158 }
159
160 Model::freq_type cumulative_freq = 0;
161 for (Model::symbol_type symbol = Model::MIN_SYMBOL, n = model->user_model_.frequency_size();
162 symbol < n; ++symbol)
163 {
164 Model::freq_type freq;
165 if (symbol == Model::EOF_SYMBOL)
166 freq = model->user_model_.eof_frequency();
167 else if (symbol == Model::OUT_OF_RANGE_SYMBOL)
168 freq = model->user_model_.out_of_range_frequency();
169 else
170 freq = model->user_model_.frequency(symbol);
171
172 if (freq == 0 && symbol != Model::OUT_OF_RANGE_SYMBOL && symbol != Model::EOF_SYMBOL)
173 {
174 throw(Exception("Invalid model: " + model->user_model_.DebugString() +
175 "All frequencies must be nonzero."));
176 }
177 cumulative_freq += freq;
178 model->encoder_cumulative_freqs_.insert(std::make_pair(symbol, cumulative_freq));
179 }
180
181 // must have separate models for adaptive encoding.
182 model->decoder_cumulative_freqs_ = model->encoder_cumulative_freqs_;
183
184 if (model->total_freq(Model::ENCODER) > Model::MAX_FREQUENCY)
185 {
186 throw(Exception("Invalid model: " + model->user_model_.DebugString() +
187 "Sum of all frequencies must be less than " +
188 std::to_string(Model::MAX_FREQUENCY) +
189 " in order to use 64 bit arithmetic"));
190 }
191
192 if (model->user_model_.value_bound_size() != model->user_model_.frequency_size() + 1)
193 {
194 throw(Exception("Invalid model: " + model->user_model_.DebugString() +
195 "`value_bound` size must be exactly 1 more than number of symbols (= "
196 "size of `frequency`)."));
197 }
198
199 // is `value_bound` repeated field strictly monotonically increasing?
200 if (std::adjacent_find(
201 model->user_model_.value_bound().begin(), model->user_model_.value_bound().end(),
202 std::greater_equal<Model::value_type>()) != model->user_model_.value_bound().end())
203 {
204 throw(Exception("Invalid model: " + model->user_model_.DebugString() +
205 "`value_bound` must be monotonically increasing."));
206 }
207 }
208
209 private:
210 std::map<std::string, Model> arithmetic_models_;
211};
212
213template <typename FieldType = Model::value_type>
214class ArithmeticFieldCodecBase : public RepeatedTypedFieldCodec<Model::value_type, FieldType>
215{
216 public:
217 static constexpr uint64 TOP_VALUE =
218 (static_cast<uint64>(1) << Model::CODE_VALUE_BITS) - 1; // 11111111...
219 static constexpr uint64 HALF =
220 (static_cast<uint64>(1) << (Model::CODE_VALUE_BITS - 1)); // 10000000...
221 static constexpr uint64 FIRST_QTR = HALF >> 1; // 01000000...
222 static constexpr uint64 THIRD_QTR = HALF + FIRST_QTR; // 11000000...
223
224 Bitset encode_repeated(const std::vector<Model::value_type>& wire_value) override
225 {
226 return encode_repeated(wire_value, true);
227 }
228
229 Bitset encode_repeated(const std::vector<Model::value_type>& wire_value, bool update_model)
230 {
231 using dccl::dlog;
232 using namespace dccl::logger;
233 Model& model = current_model();
234
235 uint64 low = 0; // lowest code value (0.0 in decimal version)
236 uint64 high = TOP_VALUE; // highest code value (1.0 in decimal version)
237 int bits_to_follow = 0; // bits to follow with after expanding around half
238 Bitset bits;
239
240 for (unsigned value_index = 0, n = max_repeat(); value_index < n; ++value_index)
241 {
242 Model::symbol_type symbol = Model::EOF_SYMBOL;
243
244 if (wire_value.size() > value_index)
245 {
246 Model::value_type value = wire_value[value_index];
247 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) value is : " << value
248 << std::endl;
249
250 symbol = model.value_to_symbol(value);
251 }
252
253 // if out-of-range is given no frequency, end encoding
254 if (symbol == Model::OUT_OF_RANGE_SYMBOL &&
255 model.user_model().out_of_range_frequency() == 0)
256 {
257 dlog.is(DEBUG2) && dlog << "(ArithmeticFieldCodec) out of range symbol, but no "
258 "frequency given; ending encoding"
259 << std::endl;
260
261 symbol = Model::EOF_SYMBOL;
262 }
263
264 // if EOF_SYMBOL is given no frequency, use most probable symbol and give a warning
265 if (symbol == Model::EOF_SYMBOL && model.user_model().eof_frequency() == 0)
266 {
267 dlog.is(DEBUG2) && dlog << "(ArithmeticFieldCodec) end of file, but no frequency "
268 "given; filling with most probable symbol"
269 << std::endl;
270 symbol = *std::max_element(model.user_model().frequency().begin(),
271 model.user_model().frequency().end());
272 }
273
274 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) symbol is : " << symbol << std::endl;
275
276 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) current interval: ["
277 << (double)low / TOP_VALUE << "," << (double)high / TOP_VALUE
278 << ")" << std::endl;
279
280 uint64 range = (high - low) + 1;
281
282 std::pair<Model::freq_type, Model::freq_type> c_freq_range =
283 model.symbol_to_cumulative_freq(symbol, Model::ENCODER);
284
285 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) input symbol (" << symbol
286 << ") cumulative freq: [" << c_freq_range.first << ","
287 << c_freq_range.second << ")" << std::endl;
288
289 high = low + (range * c_freq_range.second) / model.total_freq(Model::ENCODER) - 1;
290 low += (range * c_freq_range.first) / model.total_freq(Model::ENCODER);
291
292 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) input symbol (" << symbol
293 << ") interval: [" << (double)low / TOP_VALUE << ","
294 << (double)high / TOP_VALUE << ")" << std::endl;
295
296 dlog.is(DEBUG3) &&
297 dlog << "(ArithmeticFieldCodec) Q1: " << Bitset(Model::CODE_VALUE_BITS, FIRST_QTR)
298 << ", Q2: " << Bitset(Model::CODE_VALUE_BITS, HALF)
299 << ", Q3 : " << Bitset(Model::CODE_VALUE_BITS, THIRD_QTR)
300 << ", top: " << Bitset(Model::CODE_VALUE_BITS, TOP_VALUE) << std::endl;
301
302 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) low: "
303 << Bitset(Model::CODE_VALUE_BITS, low).to_string() << std::endl;
304 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) high: "
305 << Bitset(Model::CODE_VALUE_BITS, high).to_string()
306 << std::endl;
307
308 if (update_model)
309 model.update_model(symbol, Model::ENCODER);
310
311 for (;;)
312 {
313 if (high < HALF)
314 {
315 bit_plus_follow(&bits, &bits_to_follow, 0);
316 dlog.is(DEBUG3) &&
317 dlog << "(ArithmeticFieldCodec): completely in [0, 0.5): EXPAND"
318 << std::endl;
319 }
320 else if (low >= HALF)
321 {
322 bit_plus_follow(&bits, &bits_to_follow, 1);
323 low -= HALF;
324 high -= HALF;
325 dlog.is(DEBUG3) &&
326 dlog << "(ArithmeticFieldCodec): completely in [0.5, 1): EXPAND"
327 << std::endl;
328 }
329 else if (low >= FIRST_QTR && high < THIRD_QTR)
330 {
331 dlog.is(DEBUG3) &&
332 dlog << "(ArithmeticFieldCodec): straddle middle [0.25, 0.75): EXPAND"
333 << std::endl;
334
335 bits_to_follow += 1;
336 low -= FIRST_QTR;
337 high -= FIRST_QTR;
338 }
339 else
340 break;
341
342 low <<= 1;
343 high <<= 1;
344 high += 1;
345
346 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) low: "
347 << Bitset(Model::CODE_VALUE_BITS, low).to_string()
348 << std::endl;
349 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) high: "
350 << Bitset(Model::CODE_VALUE_BITS, high).to_string()
351 << std::endl;
352
353 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) current interval: ["
354 << (double)low / TOP_VALUE << ","
355 << (double)high / TOP_VALUE << ")" << std::endl;
356 }
357
358 // nothing more to do, we're encoding all the data and an EOF
359 if (value_index == wire_value.size())
360 break;
361 }
362
363 // output exactly the number of bits required to unambiguously
364 // store the final range's state
365 // 0 . . . 1
366 // | | -- output nothing, unless we have follow bits
367 // | | -- output a single 0
368 if (low == 0) // high must be greater than half
369 {
370 if (high != TOP_VALUE || bits_to_follow > 0)
371 bit_plus_follow(&bits, &bits_to_follow, 0);
372 }
373 // 0 . . . 1
374 // | | -- output a single 1
375 else if (high == TOP_VALUE) // 0 < low < half
376 {
377 bit_plus_follow(&bits, &bits_to_follow, 1);
378 }
379 // 0 . . . 1
380 // | | -- output 01
381 // | | -- output 10
382 else
383 {
384 bits_to_follow += 1;
385 bit_plus_follow(&bits, &bits_to_follow, (low < FIRST_QTR) ? 0 : 1);
386 }
387
388 if (FieldCodecBase::dccl_field_options().GetExtension(arithmetic).debug_assert())
389 {
390 LOCK_LAST_BITS_MAP_MUTEX
391 // bit of a hack so I can get at the exact bit field sizes
392 Model::last_bits_map[FieldCodecBase::this_descriptor()->full_name()]
393 [FieldCodecBase::this_field()->name()] = bits;
394 }
395
396 return bits;
397 }
398
399 void bit_plus_follow(Bitset* bits, int* bits_to_follow, bool bit)
400 {
401 bits->push_back(bit);
402 dccl::dlog.is(dccl::logger::DEBUG3) &&
403 dccl::dlog << "(ArithmeticFieldCodec): emitted bit: " << bit << std::endl;
404
405 while (*bits_to_follow)
406 {
407 dccl::dlog.is(dccl::logger::DEBUG3) &&
408 dccl::dlog << "(ArithmeticFieldCodec): emitted bit (from follow): " << !bit
409 << std::endl;
410
411 bits->push_back(!bit);
412 (*bits_to_follow) -= 1;
413 }
414 }
415
416 std::vector<Model::value_type> decode_repeated(Bitset* bits) override
417 {
418 using dccl::dlog;
419 using namespace dccl::logger;
420
421 std::vector<Model::value_type> values;
422 Model& model = current_model();
423
424 uint64 value = 0;
425 uint64 low = 0;
426 uint64 high = TOP_VALUE;
427
428 // offset from `bits` to currently examined `value`
429 // there are `bit_stream_offset` zeros in the lower bits of `value`
430 int bit_stream_offset = Model::CODE_VALUE_BITS - bits->size();
431
432 for (int i = 0, n = Model::CODE_VALUE_BITS; i < n; ++i)
433 {
434 if (i >= bit_stream_offset)
435 value |=
436 (static_cast<uint64>((*bits)[bits->size() - (i - bit_stream_offset) - 1]) << i);
437 }
438
439 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec): starting value: "
440 << Bitset(Model::CODE_VALUE_BITS, value).to_string() << std::endl;
441
442 for (unsigned value_index = 0, n = max_repeat(); value_index < n; ++value_index)
443 {
444 uint64 range = (high - low) + 1;
445
446 Model::symbol_type symbol = bits_to_symbol(bits, value, bit_stream_offset, low, range);
447
448 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) symbol is: " << symbol << std::endl;
449
450 std::pair<Model::freq_type, Model::freq_type> c_freq_range =
451 model.symbol_to_cumulative_freq(symbol, Model::DECODER);
452
453 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) input symbol (" << symbol
454 << ") cumulative freq: [" << c_freq_range.first << ","
455 << c_freq_range.second << ")" << std::endl;
456
457 high = low + (range * c_freq_range.second) / model.total_freq(Model::DECODER) - 1;
458 low += (range * c_freq_range.first) / model.total_freq(Model::DECODER);
459
460 model.update_model(symbol, Model::DECODER);
461
462 if (symbol == Model::EOF_SYMBOL)
463 break;
464
465 values.push_back(model.symbol_to_value(symbol));
466
467 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) value is: " << values.back()
468 << std::endl;
469
470 for (;;)
471 {
472 if (high < HALF)
473 {
474 // nothing
475 }
476 else if (low >= HALF)
477 {
478 value -= HALF;
479 low -= HALF;
480 high -= HALF;
481 }
482 else if (low >= FIRST_QTR && high < THIRD_QTR)
483 {
484 value -= FIRST_QTR;
485 low -= FIRST_QTR;
486 high -= FIRST_QTR;
487 }
488 else
489 break;
490
491 low <<= 1;
492 high <<= 1;
493 high += 1;
494 value <<= 1;
495 bit_stream_offset += 1;
496 }
497 }
498
499 // for debugging / testing
500 if (FieldCodecBase::dccl_field_options().GetExtension(arithmetic).debug_assert())
501 {
502 LOCK_LAST_BITS_MAP_MUTEX
503 // must consume same bits as encoded makes
504 Bitset in = Model::last_bits_map[FieldCodecBase::this_descriptor()->full_name()]
505 [FieldCodecBase::this_field()->name()];
506
507 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) bits used is (" << bits->size()
508 << "): " << *bits << std::endl;
509 dlog.is(DEBUG3) && dlog << "(ArithmeticFieldCodec) bits original is (" << in.size()
510 << "): " << in << std::endl;
511
512 assert(in == *bits);
513 }
514
515 return values;
516 }
517
518 unsigned size_repeated(const std::vector<Model::value_type>& wire_values) override
519 {
520 // we should really cache this for efficiency
521 return encode_repeated(wire_values, false).size();
522 }
523
524 // this maximum size will be upper bounded by: ceil(log_2(1/P)) + 1 where P is the
525 // probability of this least probable set of symbols
526 unsigned max_size_repeated() override
527 {
528 using dccl::log2;
529 Model& model = current_model();
530
531 // if user doesn't provide out_of_range frequency, set it to max to force this
532 // calculation to return the lowest probability symbol in use
533 Model::freq_type out_of_range_freq = model.user_model().out_of_range_frequency();
534 if (out_of_range_freq == 0)
535 out_of_range_freq = Model::MAX_FREQUENCY;
536
537 Model::value_type lowest_frequency =
538 std::min(out_of_range_freq, *std::min_element(model.user_model().frequency().begin(),
539 model.user_model().frequency().end()));
540
541 // full of least probable symbols
542 auto size_least_probable = (unsigned)(std::ceil(
543 max_repeat() * (log2(model.total_freq(Model::ENCODER)) - log2(lowest_frequency))));
544
545 dccl::dlog.is(dccl::logger::DEBUG3) &&
546 dccl::dlog << "(ArithmeticFieldCodec) size_least_probable: " << size_least_probable
547 << std::endl;
548
549 Model::freq_type eof_freq = model.user_model().eof_frequency();
550 // almost full of least probable symbols plus EOF
551 auto size_least_probable_plus_eof =
552 (unsigned)((eof_freq != 0)
553 ? std::ceil(max_repeat() * log2(model.total_freq(Model::ENCODER)) -
554 (max_repeat() - 1) * log2(lowest_frequency) - log2(eof_freq))
555 : 0);
556
557 dccl::dlog.is(dccl::logger::DEBUG3) &&
558 dccl::dlog << "(ArithmeticFieldCodec) size_least_probable_plus_eof: "
559 << size_least_probable_plus_eof << std::endl;
560
561 return std::max(size_least_probable_plus_eof, size_least_probable) + 1;
562 }
563
564 unsigned min_size_repeated() override
565 {
566 using dccl::log2;
567 const Model& model = current_model();
568
569 if (model.user_model().is_adaptive())
570 return 0; // force examining bits from the beginning on decode
571
572 // if user doesn't provide out_of_range frequency, set it to 1 (minimum) to force this
573 // calculation to return the highest probability symbol in use
574 Model::freq_type out_of_range_freq = model.user_model().out_of_range_frequency();
575 if (out_of_range_freq == 0)
576 out_of_range_freq = 1;
577
578 Model::freq_type eof_freq = model.user_model().eof_frequency();
579 // just EOF
580 auto size_empty =
581 (unsigned)((eof_freq != 0)
582 ? std::ceil(log2(model.total_freq(Model::ENCODER)) - log2(eof_freq))
583 : std::numeric_limits<unsigned>::max());
584
585 dccl::dlog.is(dccl::logger::DEBUG3) &&
586 dccl::dlog << "(ArithmeticFieldCodec) size_empty: " << size_empty << std::endl;
587
588 // full with most probable symbol
589 Model::value_type highest_frequency =
590 std::max(out_of_range_freq, *std::max_element(model.user_model().frequency().begin(),
591 model.user_model().frequency().end()));
592
593 auto size_most_probable = (unsigned)(std::ceil(
594 max_repeat() * (log2(model.total_freq(Model::ENCODER)) - log2(highest_frequency))));
595
596 dccl::dlog.is(dccl::logger::DEBUG3) &&
597 dccl::dlog << "(ArithmeticFieldCodec) size_most_probable: " << size_most_probable
598 << std::endl;
599
600 return std::min(size_empty, size_most_probable);
601 }
602
603 void validate() override
604 {
606 "missing (dccl.field).arithmetic");
607
608 std::string model_name =
609 FieldCodecBase::dccl_field_options().GetExtension(arithmetic).model();
610 try
611 {
612 model_manager().find(model_name);
613 }
614 catch (Exception& e)
615 {
616 FieldCodecBase::require(false, "no such (dccl.field).arithmetic.model called \"" +
617 model_name + "\" loaded.");
618 }
619 }
620
621 // end inherited methods
622
623 Model::symbol_type bits_to_symbol(Bitset* bits, uint64& value, int& bit_stream_offset,
624 uint64 low, uint64 range)
625 {
626 Model& model = current_model();
627
628 for (;;)
629 {
630 uint64 value_high = (bit_stream_offset > 0)
631 ? value + ((static_cast<uint64>(1) << bit_stream_offset) - 1)
632 : value;
633
634 dccl::dlog.is(dccl::logger::DEBUG3) &&
635 dccl::dlog << "(ArithmeticFieldCodec): value range: ["
636 << Bitset(Model::CODE_VALUE_BITS, value) << ","
637 << Bitset(Model::CODE_VALUE_BITS, value_high) << ")" << std::endl;
638
639 Model::freq_type cumulative_freq =
640 ((value - low + 1) * model.total_freq(Model::DECODER) - 1) / range;
641 Model::freq_type cumulative_freq_high =
642 ((value_high - low + 1) * model.total_freq(Model::DECODER) - 1) / range;
643
644 dccl::dlog.is(dccl::logger::DEBUG3) &&
645 dccl::dlog << "(ArithmeticFieldCodec): c_freq: " << cumulative_freq
646 << ", c_freq_high: " << cumulative_freq_high << std::endl;
647
648 std::pair<Model::symbol_type, Model::symbol_type> symbol_pair =
649 model.cumulative_freq_to_symbol(
650 std::make_pair(cumulative_freq, cumulative_freq_high), Model::DECODER);
651
652 dccl::dlog.is(dccl::logger::DEBUG3) &&
653 dccl::dlog << "(ArithmeticFieldCodec): symbol: " << symbol_pair.first << ", "
654 << symbol_pair.second << std::endl;
655
656 if (symbol_pair.first == symbol_pair.second)
657 return symbol_pair.first;
658
659 // add another bit to disambiguate
660 bits->get_more_bits(1);
661
662 dccl::dlog.is(dccl::logger::DEBUG3) &&
663 dccl::dlog << "(ArithmeticFieldCodec): bits: " << *bits << std::endl;
664
665 --bit_stream_offset;
666 value |= static_cast<uint64>(bits->back()) << bit_stream_offset;
667
668 dccl::dlog.is(dccl::logger::DEBUG3) &&
669 dccl::dlog << "(ArithmeticFieldCodec): ambiguous (symbol could be "
670 << symbol_pair.first << " or " << symbol_pair.second << ")" << std::endl;
671 }
672
673 return 0;
674 }
675
676 dccl::int32 max_repeat()
677 {
678 return FieldCodecBase::this_field()->is_repeated()
680 : 1;
681 }
682
683 Model& current_model()
684 {
685 std::string name = FieldCodecBase::dccl_field_options().GetExtension(arithmetic).model();
686 return model_manager().find(name);
687 }
688
689 ModelManager& model_manager() { return dccl::arith::model_manager(this->manager()); }
690};
691
692// constant integer definitions
693template <typename FieldType> const uint64 ArithmeticFieldCodecBase<FieldType>::TOP_VALUE;
694template <typename FieldType> const uint64 ArithmeticFieldCodecBase<FieldType>::FIRST_QTR;
695template <typename FieldType> const uint64 ArithmeticFieldCodecBase<FieldType>::HALF;
696template <typename FieldType> const uint64 ArithmeticFieldCodecBase<FieldType>::THIRD_QTR;
697
698template <typename FieldType>
700{
701 Model::value_type pre_encode(const FieldType& field_value) override
702 {
703 return static_cast<Model::value_type>(field_value);
704 }
705
706 FieldType post_decode(const Model::value_type& wire_value) override
707 {
708 return static_cast<FieldType>(wire_value);
709 }
710};
711
712template <>
713class ArithmeticFieldCodec<const google::protobuf::EnumValueDescriptor*>
714 : public ArithmeticFieldCodecBase<const google::protobuf::EnumValueDescriptor*>
715{
716 public:
717 Model::value_type
718 pre_encode(const google::protobuf::EnumValueDescriptor* const& field_value) override
719 {
720 return field_value->number();
721 }
722
723 const google::protobuf::EnumValueDescriptor*
724 post_decode(const Model::value_type& wire_value) override
725 {
726 const google::protobuf::EnumDescriptor* e = FieldCodecBase::this_field()->enum_type();
727 const google::protobuf::EnumValueDescriptor* return_value =
728 e->FindValueByNumber((int)wire_value);
729
730 if (return_value)
731 return return_value;
732 else
733 throw NullValueException();
734 }
735};
736
737} // namespace arith
738} // namespace dccl
739
740#endif
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy....
Definition bitset.h:43
std::string to_string() const
Returns the value of the Bitset as a printable string, where each bit is represented by '1' or '0'....
Definition bitset.h:278
void get_more_bits(size_type num_bits)
Retrieve more bits from the parent Bitset.
Definition bitset.h:420
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Definition codec.h:63
Exception class for DCCL.
Definition exception.h:47
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.
std::string name() const
the name of the codec used to identifier it in the .proto custom option extension
Definition field_codec.h:66
dccl::DCCLFieldOptions dccl_field_options() const
Get the DCCL field option extension value for the current field.
A class for managing the various field codecs. Here you can add and remove field codecs....
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:192
Exception used to signal null (non-existent) value within field codecs during decode.
Definition exception.h:62
Base class for "repeated" (multiple value) static-typed (no dccl::any) field encoders/decoders....
unsigned min_size_repeated() override
Give the min size of a repeated field.
std::vector< Model::value_type > decode_repeated(Bitset *bits) override
Decode a repeated field.
void validate() override
Validate a field. Use require() inside your overloaded validate() to assert requirements or throw Exc...
unsigned max_size_repeated() override
Give the max size of a repeated field.
Dynamic Compact Control Language namespace.
Definition any.h:47
google::protobuf::uint64 uint64
an unsigned 64 bit integer
Definition common.h:60
google::protobuf::int32 int32
a signed 32 bit integer
Definition common.h:58
google::protobuf::uint32 uint32
an unsigned 32 bit integer
Definition common.h:56