The Sparta Modeling Framework
Loading...
Searching...
No Matches
RegisterBits.hpp
1// <RegisterBits> -*- C++ -*-
2#pragma once
3
4#include <cinttypes>
5#include <array>
6#include <functional>
7#include <limits>
8#include <string.h>
9#include <algorithm>
10
12
13namespace sparta
14{
30 {
31 template<typename SizeT, typename Op>
32 static RegisterBits bitMerge_(const RegisterBits & in, const RegisterBits & rh_bits) {
33 RegisterBits final_value(in.num_bytes_);
34 *reinterpret_cast<SizeT*>(final_value.local_data_) =
35 Op()(*reinterpret_cast<const SizeT*>(in.remote_data_),
36 *reinterpret_cast<const SizeT*>(rh_bits.remote_data_));
37 return final_value;
38 }
39
40 template<typename SizeT>
41 static RegisterBits bitNot_(const RegisterBits & in) {
42 RegisterBits final_value(in.num_bytes_);
43 *reinterpret_cast<SizeT*>(final_value.local_data_) =
44 std::bit_not<SizeT>()(*reinterpret_cast<const SizeT*>(in.remote_data_));
45 return final_value;
46 }
47
48 template<typename SizeT>
49 static RegisterBits bitShiftRight_(const RegisterBits & in, uint32_t amount) {
50 RegisterBits final_value(in.num_bytes_);
51 *reinterpret_cast<SizeT*>(final_value.local_data_) =
52 (*reinterpret_cast<const SizeT*>(in.remote_data_)) >> amount;
53 return final_value;
54 }
55
56 template<typename SizeT>
57 static RegisterBits bitShiftLeft_(const RegisterBits & in, uint32_t amount) {
58 RegisterBits final_value(in.num_bytes_);
59 *reinterpret_cast<SizeT*>(final_value.local_data_) =
60 (*reinterpret_cast<const SizeT*>(in.remote_data_)) << amount;
61 return final_value;
62 }
63
64 // Copy the remote register data locally.
65 void convert_() {
66 if(nullptr == local_data_)
67 {
68 if(num_bytes_ <= local_storage_.size()) {
69 local_data_ = local_storage_.data();
70 }
71 else {
72 local_storage_alt_.reset(new uint8_t[num_bytes_]);
73 local_data_ = local_storage_alt_.get();
74 }
75 ::memset(local_data_, 0, num_bytes_);
76 ::memcpy(local_data_, remote_data_, num_bytes_);
77 remote_data_ = local_data_;
78 }
79 }
80
81 public:
86 explicit RegisterBits(const uint64_t num_bytes) :
87 local_storage_(),
88 local_data_(local_storage_.data()),
89 remote_data_(local_data_),
90 num_bytes_(num_bytes)
91 {
92 if(num_bytes > local_storage_.size()) {
93 local_storage_alt_.reset(new uint8_t[num_bytes]);
94 local_data_ = local_storage_alt_.get();
95 remote_data_ = local_data_;
96 }
97 ::memset(local_data_, 0, num_bytes_);
98 }
99
107 template<class DataT>
108 RegisterBits(const uint64_t num_bytes, const DataT & data) :
109 local_storage_(),
110 local_data_(local_storage_.data()),
111 remote_data_(local_data_),
112 num_bytes_(num_bytes)
113 {
114 if(num_bytes > local_storage_.size()) {
115 local_storage_alt_.reset(new uint8_t[num_bytes]);
116 local_data_ = local_storage_alt_.get();
117 remote_data_ = local_data_;
118 }
119 ::memset(local_data_, 0, num_bytes_);
120 sparta_assert(sizeof(DataT) <= num_bytes);
121 set(data);
122 }
123
131 RegisterBits(uint8_t * data_ptr, const size_t num_bytes) :
132 local_data_(data_ptr),
133 remote_data_(local_data_),
134 num_bytes_(num_bytes)
135 {}
136
144 RegisterBits(const uint8_t * data, const size_t num_bytes) :
145 remote_data_(data),
146 num_bytes_(num_bytes)
147 {}
148
152 RegisterBits(std::nullptr_t) {}
153
161 num_bytes_(orig.num_bytes_)
162 {
163 if(num_bytes_ <= local_storage_.size()) {
164 local_storage_ = orig.local_storage_;
165 local_data_ = orig.local_data_ == nullptr ? nullptr : local_storage_.data();
166 }
167 else if (orig.local_data_ == nullptr) {
168 local_data_ = nullptr;
169 }
170 else {
171 local_storage_alt_.reset(new uint8_t[orig.getSize()]);
172 local_data_ = local_storage_alt_.get();
173 ::memcpy(local_data_, orig.local_data_, num_bytes_);
174 }
175 remote_data_ = orig.local_data_ == orig.remote_data_ ? local_data_ : orig.remote_data_;
176 }
177
186 num_bytes_(orig.num_bytes_)
187 {
188 if(num_bytes_ <= local_storage_.size()) {
189 local_storage_ = std::move(orig.local_storage_);
190 local_data_ = (orig.local_data_ == nullptr ? nullptr : local_storage_.data());
191 }
192 else if (orig.local_data_ == nullptr) {
193 local_data_ = nullptr;
194 }
195 else {
196 local_storage_alt_ = std::move(orig.local_storage_alt_);
197 local_data_ = local_storage_alt_.get();
198 }
199 remote_data_ = (orig.local_data_ == orig.remote_data_ ? local_data_ : orig.remote_data_);
200 orig.local_data_ = nullptr;
201 orig.remote_data_ = nullptr;
202 }
203
209 RegisterBits operator|(const RegisterBits & rh_bits) const
210 {
211 if(num_bytes_ == 8) {
212 return bitMerge_<uint64_t, std::bit_or<uint64_t>>(*this, rh_bits);
213 }
214 else if(num_bytes_ == 4) {
215 return bitMerge_<uint32_t, std::bit_or<uint32_t>>(*this, rh_bits);
216 }
217 else if(num_bytes_ > 8)
218 {
219 RegisterBits final_value(num_bytes_);
220 // 64-bit chunks
221 for(uint32_t idx = 0; idx < num_bytes_; idx += 8)
222 {
223 *reinterpret_cast<uint64_t*>(final_value.local_data_ + idx) =
224 *reinterpret_cast<const uint64_t*>(remote_data_ + idx) |
225 *reinterpret_cast<const uint64_t*>(rh_bits.remote_data_ + idx);
226 }
227 return final_value;
228 }
229 else if(num_bytes_ == 2) {
230 return bitMerge_<uint16_t, std::bit_or<uint16_t>>(*this, rh_bits);
231 }
232 else if(num_bytes_ == 1) {
233 return bitMerge_<uint8_t, std::bit_or<uint8_t>>(*this, rh_bits);
234 }
235
236 // undefined
237 return RegisterBits(nullptr);
238 }
239
245 RegisterBits operator&(const RegisterBits & rh_bits) const
246 {
247 if(num_bytes_ == 8) {
248 return bitMerge_<uint64_t, std::bit_and<uint64_t>>(*this, rh_bits);
249 }
250 else if(num_bytes_ == 4) {
251 return bitMerge_<uint32_t, std::bit_and<uint32_t>>(*this, rh_bits);
252 }
253 else if(num_bytes_ > 8)
254 {
255 RegisterBits final_value(num_bytes_);
256 // 64-bit chunks
257 for(uint32_t idx = 0; idx < num_bytes_; idx += 8)
258 {
259 *reinterpret_cast<uint64_t*>(final_value.local_data_ + idx) =
260 *reinterpret_cast<const uint64_t*>(remote_data_ + idx) &
261 *reinterpret_cast<const uint64_t*>(rh_bits.remote_data_ + idx);
262 }
263 return final_value;
264 }
265 else if(num_bytes_ == 2) {
266 return bitMerge_<uint16_t, std::bit_and<uint16_t>>(*this, rh_bits);
267 }
268 else if(num_bytes_ == 1) {
269 return bitMerge_<uint8_t, std::bit_and<uint8_t>>(*this, rh_bits);
270 }
271
272 // undefined
273 return RegisterBits(nullptr);
274 }
275
281 {
282 if(num_bytes_ == 8) {
283 return bitNot_<uint64_t>(*this);
284 }
285 else if(num_bytes_ == 4) {
286 return bitNot_<uint32_t>(*this);
287 }
288 else if(num_bytes_ > 8)
289 {
290 RegisterBits final_value(num_bytes_);
291 // 64-bit compares
292 for(uint32_t idx = 0; idx < num_bytes_; idx += 8)
293 {
294 *reinterpret_cast<uint64_t*>(final_value.local_data_ + idx) =
295 ~*reinterpret_cast<const uint64_t*>(remote_data_ + idx);
296 }
297 return final_value;
298 }
299 else if(num_bytes_ == 2) {
300 return bitNot_<uint16_t>(*this);
301 }
302 else if(num_bytes_ == 1) {
303 return bitNot_<uint8_t>(*this);
304 }
305 return RegisterBits(nullptr);
306 }
307
313 RegisterBits operator>>(uint32_t shift) const
314 {
315 if(num_bytes_ == 8) {
316 return bitShiftRight_<uint64_t>(*this, shift);
317 }
318 else if(num_bytes_ == 4) {
319 return bitShiftRight_<uint32_t>(*this, shift);
320 }
321 else if(num_bytes_ > 8)
322 {
323 RegisterBits final_value(num_bytes_);
324 const uint64_t * src_data = reinterpret_cast<const uint64_t*>(remote_data_);
325 uint64_t * final_data = reinterpret_cast<uint64_t*>(final_value.local_data_);
326 const uint32_t num_dbl_words = num_bytes_ / 8;
327
328 // Determine the number of double words that will be shifted
329 const uint32_t double_word_shift_count = shift / 64;
330
331 //
332 // Shift full double words first.
333 //
334 // Example:
335 //
336 // num_dbl_words = 4
337 //
338 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
339 // 1111111111111111
340 // double_word_shift_count = 3
341 // dest[0] = src[3]
342 //
343 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
344 // 1111111111111111 2222222222222222
345 // double_word_shift_count = 2
346 // dest[0] = src[2]
347 // dest[1] = src[3]
348 //
349 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
350 // 1111111111111111 2222222222222222 3333333333333333
351 // double_word_shift_count = 1
352 // dest[0] = src[1]
353 // dest[1] = src[2]
354 // dest[2] = src[3]
355 //
356 uint32_t src_idx = double_word_shift_count;
357 for(uint32_t dest_idx = 0; src_idx < num_dbl_words; ++dest_idx, ++src_idx) {
358 *(final_data + dest_idx) = *(src_data + src_idx);
359 }
360
361 // Micro-shift:
362 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
363 // 0011111111111111 1122222222222222 2233333333333333
364 //
365 const auto double_words_to_micro_shift = (num_dbl_words - double_word_shift_count);
366 if(double_words_to_micro_shift > 0)
367 {
368 const uint32_t remaining_bits_to_shift = shift % 64;
369 if(remaining_bits_to_shift)
370 {
371 const uint64_t prev_dbl_word_bits_dropped_mask =
372 (uint64_t)(-(remaining_bits_to_shift != 0) &
373 (-1 >> ((sizeof(uint64_t) * 8) - remaining_bits_to_shift)));
374 const uint32_t prev_dbl_word_bit_pos = 64 - remaining_bits_to_shift;
375 uint32_t idx =0;
376 while(true)
377 {
378 *(final_data + idx) >>= remaining_bits_to_shift;
379 if(++idx == double_words_to_micro_shift) {
380 break;
381 }
382 const uint64_t bits_pulled_in =
383 (*(final_data + idx) & prev_dbl_word_bits_dropped_mask) << prev_dbl_word_bit_pos;
384 *(final_data + (idx - 1)) |= bits_pulled_in;
385 }
386 }
387 }
388 return final_value;
389 }
390 else if(num_bytes_ == 2) {
391 return bitShiftRight_<uint16_t>(*this, shift);
392 }
393 else if(num_bytes_ == 1) {
394 return bitShiftRight_<uint8_t>(*this, shift);
395 }
396
397 return RegisterBits(nullptr);
398 }
399
405 RegisterBits operator<<(uint32_t shift) const
406 {
407 if(num_bytes_ == 8) {
408 return bitShiftLeft_<uint64_t>(*this, shift);
409 }
410 else if(num_bytes_ == 4) {
411 return bitShiftLeft_<uint32_t>(*this, shift);
412 }
413 else if(num_bytes_ > 8)
414 {
415 RegisterBits final_value(num_bytes_);
416 const uint64_t * src_data = reinterpret_cast<const uint64_t*>(remote_data_);
417 uint64_t * final_data = reinterpret_cast<uint64_t*>(final_value.local_data_);
418 const uint32_t num_dbl_words = num_bytes_ / 8;
419
420 // Determine the number of double words that will be shifted
421 const uint32_t double_word_shift_count = shift / 64;
422
423 //
424 // Shift full double words first.
425 //
426 // Example:
427 //
428 // num_dbl_words = 4
429 //
430 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
431 // 4444444444444444
432 // double_word_shift_count = 3
433 // dest[3] = src[0]
434 //
435 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
436 // 3333333333333333 4444444444444444
437 // double_word_shift_count = 2
438 // dest[2] = src[0]
439 // dest[3] = src[1]
440 //
441 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
442 // 2222222222222222 3333333333333333 4444444444444444
443 // double_word_shift_count = 1
444 // dest[1] = src[0]
445 // dest[2] = src[1]
446 // dest[3] = src[2]
447 //
448 uint32_t dest_idx = double_word_shift_count;
449 for(uint32_t src_idx = 0; dest_idx < num_dbl_words; ++dest_idx, ++src_idx) {
450 *(final_data + dest_idx) = *(src_data + src_idx);
451 }
452
453 // Micro-shift:
454 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
455 // 2222222222222233 3333333333333344 4444444444444400
456 //
457 const uint32_t remaining_bits_to_shift = shift % 64;
458 if(remaining_bits_to_shift)
459 {
460 const int32_t double_words_to_micro_shift = (num_dbl_words - double_word_shift_count);
461 if(double_words_to_micro_shift > 0)
462 {
463 const uint64_t prev_dbl_word_bit_pos = 64 - remaining_bits_to_shift;
464 const uint64_t prev_dbl_word_bits_dropped_mask =
465 (uint64_t)(-(prev_dbl_word_bit_pos != 0) &
466 (std::numeric_limits<uint64_t>::max() << prev_dbl_word_bit_pos));
467 int32_t idx = num_dbl_words - 1; // start at the top
468 while(true)
469 {
470 *(final_data + idx) <<= remaining_bits_to_shift;
471 --idx;
472 if(idx < 0) { break; }
473 const uint64_t bits_dropped_from_next_double_word =
474 (*(final_data + idx) & prev_dbl_word_bits_dropped_mask) >> prev_dbl_word_bit_pos;
475 *(final_data + (idx + 1)) |= bits_dropped_from_next_double_word;
476 }
477 }
478 }
479 return final_value;
480 }
481 else if(num_bytes_ == 2) {
482 return bitShiftLeft_<uint16_t>(*this, shift);
483 }
484 else if(num_bytes_ == 1) {
485 return bitShiftLeft_<uint8_t>(*this, shift);
486 }
487
488 return RegisterBits(nullptr);
489 }
490
491 void operator|=(const RegisterBits & rh_bits)
492 {
493 convert_();
494 if(num_bytes_ == 8) {
495 *reinterpret_cast<uint64_t*>(local_data_) |=
496 *reinterpret_cast<const uint64_t*>(rh_bits.remote_data_);
497 }
498 else if(num_bytes_ == 4) {
499 *reinterpret_cast<uint32_t*>(local_data_) |=
500 *reinterpret_cast<const uint32_t*>(rh_bits.remote_data_);
501 }
502 else if(num_bytes_ > 8)
503 {
504 // 64-bit chunks
505 for(uint32_t idx = 0; idx < num_bytes_; idx += 8)
506 {
507 *reinterpret_cast<uint64_t*>(local_data_ + idx) |=
508 *reinterpret_cast<const uint64_t*>(rh_bits.remote_data_ + idx);
509 }
510 }
511 else if(num_bytes_ == 2) {
512 *reinterpret_cast<uint16_t*>(local_data_) |=
513 *reinterpret_cast<const uint16_t*>(rh_bits.remote_data_);
514 }
515 else if(num_bytes_ == 1) {
516 *reinterpret_cast<uint8_t*>(local_data_) |=
517 *reinterpret_cast<const uint8_t*>(rh_bits.remote_data_);
518 }
519
520 }
521
526 void operator<<=(uint32_t shift)
527 {
528 convert_();
529 if(num_bytes_ == 8) {
530 *reinterpret_cast<uint64_t*>(local_data_) =
531 (*reinterpret_cast<const uint64_t*>(remote_data_)) << shift;
532 }
533 else if(num_bytes_ == 4) {
534 *reinterpret_cast<uint32_t*>(local_data_) =
535 (*reinterpret_cast<const uint32_t*>(remote_data_)) << shift;
536 }
537 else if(num_bytes_ > 8)
538 {
539 uint64_t * src_data = reinterpret_cast<uint64_t*>(local_data_);
540 uint64_t * final_data = reinterpret_cast<uint64_t*>(local_data_);
541 const uint32_t num_dbl_words = num_bytes_ / 8;
542
543 // Determine the number of double words that will be shifted
544 const uint32_t double_word_shift_count = shift / 64;
545
546 //
547 // Shift full double words first.
548 //
549 // Example:
550 //
551 // num_dbl_words = 4
552 //
553 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
554 // 4444444444444444
555 // double_word_shift_count = 3
556 // dest[3] = src[0]
557 //
558 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
559 // 3333333333333333 4444444444444444
560 // double_word_shift_count = 2
561 // dest[2] = src[0]
562 // dest[3] = src[1]
563 //
564 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
565 // 2222222222222222 3333333333333333 4444444444444444
566 // double_word_shift_count = 1
567 // dest[1] = src[0]
568 // dest[2] = src[1]
569 // dest[3] = src[2]
570 //
571 if(double_word_shift_count > 0)
572 {
573 uint32_t dest_idx = double_word_shift_count;
574 for(uint32_t src_idx = 0; dest_idx < num_dbl_words; ++dest_idx, ++src_idx) {
575 *(final_data + dest_idx) = *(src_data + src_idx);
576 *(src_data + src_idx) = 0;
577 }
578 }
579
580 // Micro-shift:
581 // 1111111111111111 2222222222222222 3333333333333333 4444444444444444
582 // 2222222222222233 3333333333333344 4444444444444400
583 //
584 const uint32_t remaining_bits_to_shift = shift % 64;
585 if(remaining_bits_to_shift)
586 {
587 const int32_t double_words_to_micro_shift = (num_dbl_words - double_word_shift_count);
588 if(double_words_to_micro_shift > 0)
589 {
590 const uint64_t prev_dbl_word_bit_pos = 64 - remaining_bits_to_shift;
591 const uint64_t prev_dbl_word_bits_dropped_mask =
592 (uint64_t)(-(prev_dbl_word_bit_pos != 0) &
593 (std::numeric_limits<uint64_t>::max() << prev_dbl_word_bit_pos));
594 int32_t idx = num_dbl_words - 1; // start at the top
595 while(true)
596 {
597 *(final_data + idx) <<= remaining_bits_to_shift;
598 --idx;
599 if(idx < 0) { break; }
600 const uint64_t bits_dropped_from_next_double_word =
601 (*(final_data + idx) & prev_dbl_word_bits_dropped_mask) >> prev_dbl_word_bit_pos;
602 *(final_data + (idx + 1)) |= bits_dropped_from_next_double_word;
603 }
604 }
605 }
606 }
607 else if(num_bytes_ == 2) {
608 *reinterpret_cast<uint16_t*>(local_data_) =
609 (*reinterpret_cast<const uint16_t*>(remote_data_)) << shift;
610 }
611 else if(num_bytes_ == 1) {
612 *reinterpret_cast<uint8_t*>(local_data_) =
613 (*reinterpret_cast<const uint8_t*>(remote_data_)) << shift;
614 }
615 }
616
622 template<class DataT>
623 std::enable_if_t<std::is_integral_v<DataT>, bool>
624 operator==(const DataT dat) const {
625 // sparta_assert(sizeof(DataT) <= num_bytes_);
626 return *(reinterpret_cast<const DataT*>(remote_data_)) == dat;
627 }
628
634 bool operator==(const RegisterBits & other) const {
635 return (num_bytes_ == other.num_bytes_) &&
636 (::memcmp(remote_data_, other.remote_data_, num_bytes_) == 0);
637 }
638
646 template<class DataT>
647 void set(const DataT & masked_bits) {
648 // sparta_assert(sizeof(DataT) <= num_bytes_);
649 convert_();
650 ::memcpy(local_data_, reinterpret_cast<const uint8_t*>(&masked_bits), sizeof(DataT));
651 }
652
660 void fill(const uint8_t fill_data) {
661 convert_();
662 ::memset(local_data_, fill_data, num_bytes_);
663 }
664
670 const uint8_t * operator[](const uint32_t idx) const {
671 return remote_data_ + idx;
672 }
673
678 const uint8_t *data() const {
679 return remote_data_;
680 }
681
686 uint8_t *data() {
687 convert_();
688 return local_data_;
689 }
690
695 template <typename T,
696 typename = typename std::enable_if<std::is_integral<T>::value>::type>
697 T dataAs() const {
698 T ret_data = 0;
699 ::memcpy(&ret_data, remote_data_, std::min(sizeof(T), (size_t)num_bytes_));
700 return ret_data;
701 }
702
707 uint32_t getSize() const { return num_bytes_; }
708
713 bool none() const {
714 sparta_assert(num_bytes_ > 0);
715 const auto mem_data_plus_one = remote_data_ + 1;
716 return (::memcmp(remote_data_, mem_data_plus_one, num_bytes_ - 1) == 0);
717 }
718
723 bool any() const {
724 return !none();
725 }
726
727 private:
728
729 std::array<uint8_t, 8> local_storage_;
730 std::unique_ptr<uint8_t[]> local_storage_alt_;
731 uint8_t * local_data_ = nullptr;
732 const uint8_t * remote_data_ = nullptr;
733 const uint64_t num_bytes_ = 0;
734 };
735}
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
uint32_t getSize() const
Get the number of bytes.
RegisterBits operator~() const
"not" this class
void set(const DataT &masked_bits)
Set the given masked_bits in this RegisterBits instance.
const uint8_t * operator[](const uint32_t idx) const
Get the data offset at the given index.
uint8_t * data()
Get the internal data pointer.
RegisterBits operator>>(uint32_t shift) const
Shift this instance right and return a copy.
RegisterBits(std::nullptr_t)
Create a nullptr version of the data. This would be an invalid class.
RegisterBits(const uint64_t num_bytes)
Create an empty class with the given number of bytes.
T dataAs() const
Get the internal data as the given dta type.
bool operator==(const RegisterBits &other) const
Compare the register bits to another.
bool none() const
Returns true if no bits are set.
std::enable_if_t< std::is_integral_v< DataT >, bool > operator==(const DataT dat) const
Compare the register bits to the given data.
RegisterBits operator<<(uint32_t shift) const
Shift this instance left and return a copy.
bool any() const
Returns true if any bit is set.
void fill(const uint8_t fill_data)
Fill the RegisterBits with the given fill_data.
RegisterBits(const RegisterBits &orig)
Make a copy.
RegisterBits(uint8_t *data_ptr, const size_t num_bytes)
Create a class pointing into the given data, of the given size.
void operator<<=(uint32_t shift)
Shift this instance left.
RegisterBits(RegisterBits &&orig)
Move.
RegisterBits(const uint8_t *data, const size_t num_bytes)
Create a class pointing into the given data constantly, of the given size.
RegisterBits(const uint64_t num_bytes, const DataT &data)
Create a class with the given number of bytes and initialize it to the given data.
const uint8_t * data() const
Get the internal data pointer.
RegisterBits operator&(const RegisterBits &rh_bits) const
"and" together two classes
RegisterBits operator|(const RegisterBits &rh_bits) const
"or" together two classes
Macros for handling exponential backoff.