P4C
The P4 Compiler
 
Loading...
Searching...
No Matches
bitrange.h
1/*
2Copyright 2013-present Barefoot Networks, Inc.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#ifndef LIB_BITRANGE_H_
18#define LIB_BITRANGE_H_
19
20#include <absl/numeric/bits.h>
21
22#include <algorithm>
23#include <iosfwd>
24#include <limits>
25#include <optional>
26#include <utility>
27
28#include "bitvec.h"
29#include "exceptions.h"
30#include "hash.h"
31
32/* iterate over ranges of contiguous bits in a bitvector */
33class bitranges {
34 bitvec tmp;
35 const bitvec &bits;
36 struct iter {
37 bool valid = true;
39 std::pair<int, int> range;
40
41 iter &operator++() {
42 if (ptr) {
43 range.first = range.second = ptr.index();
44 while (++ptr && range.second + 1 == ptr.index()) ++range.second;
45 } else {
46 valid = false;
47 }
48 return *this;
49 }
50 std::pair<int, int> operator*() { return range; }
51 bool operator==(iter &a) const { return valid == a.valid && ptr == a.ptr; }
52 bool operator!=(iter &a) const { return !(*this == a); }
53 explicit iter(bitvec::const_bitref p) : ptr(p) { ++*this; }
54 };
55
56 public:
57 explicit bitranges(const bitvec &b) : bits(b) {}
58 explicit bitranges(bitvec &&b) : tmp(b), bits(tmp) {}
59 explicit bitranges(uintptr_t b) : tmp(b), bits(tmp) {}
60 iter begin() const { return iter(bits.begin()); }
61 iter end() const { return iter(bits.end()); }
62};
63
64class JSONGenerator;
65class JSONLoader;
66
67namespace BitRange {
68
69namespace Detail {
76constexpr inline int divideFloor(int dividend, int divisor) {
77#if defined(__GNUC__) || defined(__clang__)
78 // Code to enable compiler folding when the divisor is a power-of-two compile-time constant
79 // In this case, compiler should fold to a right-shift
80 // FIXME: Replace absl with std after moving to C++20
81 unsigned u_divisor = static_cast<unsigned>(divisor);
82 if (__builtin_constant_p(u_divisor) && absl::has_single_bit(u_divisor))
83 return dividend >> (absl::bit_width(u_divisor) - 1);
84#endif // defined(__GNUC__) || defined(__clang__)
85
86 const int quotient = dividend / divisor;
87 const int remainder = dividend % divisor;
88 if ((remainder != 0) && ((remainder < 0) != (divisor < 0))) return quotient - 1;
89 return quotient;
90}
91
99constexpr int modulo(int dividend, int divisor) {
100 return (dividend % divisor) * ((dividend < 0) != (divisor < 0) ? -1 : 1);
101}
102
116constexpr inline int moduloFloor(const int dividend, const int divisor) {
117#if defined(__GNUC__) || defined(__clang__)
118 // Code to enable compiler folding when the divisor is a power-of-two compile-time constant
119 // In this case, compiler should fold to a bitwise-and
120 // FIXME: Replace absl with std after moving to C++20
121 if (__builtin_constant_p(divisor) && absl::has_single_bit(static_cast<unsigned>(divisor)))
122 return dividend & (divisor - 1);
123#endif // defined(__GNUC__) || defined(__clang__)
124
125 const int remainder = modulo(dividend, divisor);
126 if (remainder == 0 || dividend >= 0) return remainder;
127 return divisor - remainder;
128}
129} // namespace Detail
130
140struct FromTo {
141 FromTo(int from, int to) : from(from), to(to) {}
142 FromTo(const FromTo &) = delete;
143 FromTo &operator=(const FromTo &) = delete;
144 const int from;
145 const int to;
146};
147
157struct StartLen {
158 StartLen(int start, int len) : start(start), len(len) {}
159 StartLen(const StartLen &) = delete;
160 StartLen &operator=(const StartLen &) = delete;
161 const int start;
162 const int len;
163};
164
189struct ZeroToMax {};
190
208struct MinToMax {};
209
211void rangeToJSON(JSONGenerator &json, int lo, int hi);
212std::pair<int, int> rangeFromJSON(JSONLoader &json);
213
214} // namespace BitRange
215
217enum class RangeUnit : uint8_t {
218 Bit = 0,
219 Byte = 1
220};
221
223enum class Endian : uint8_t {
224 Network = 0,
225 Little = 1,
226};
227
251template <RangeUnit Unit, Endian Order>
253 static constexpr RangeUnit unit = Unit;
254 static constexpr Endian order = Order;
255
256 using FromTo = BitRange::FromTo;
260
261 HalfOpenRange() : lo(0), hi(0) {}
262 HalfOpenRange(int lo, int hi) : lo(lo), hi(hi) {}
263 HalfOpenRange(FromTo &&fromTo) // NOLINT(runtime/explicit)
264 : lo(fromTo.from), hi(fromTo.to + 1) {}
265 HalfOpenRange(StartLen &&startLen) // NOLINT(runtime/explicit)
266 : lo(startLen.start), hi(startLen.start + startLen.len) {}
267 HalfOpenRange(ZeroToMax &&) // NOLINT(runtime/explicit)
268 : lo(0), hi(std::numeric_limits<int>::max()) {}
269 HalfOpenRange(MinToMax &&) // NOLINT(runtime/explicit)
270 : lo(std::numeric_limits<int>::min()), hi(std::numeric_limits<int>::max()) {}
271 explicit HalfOpenRange(std::pair<int, int> range) : lo(range.first), hi(range.second) {}
272
274 ssize_t size() const { return ssize_t(hi) - ssize_t(lo); }
275
279 if (empty()) return HalfOpenRange(0, 0);
280 return *this;
281 }
282
288 auto asBits = toUnit<RangeUnit::Bit>();
289 return {asBits.lo, asBits.lo + size};
290 }
291
295 const int resizedLo = empty() ? 0 : lo;
296 if (Unit == RangeUnit::Byte) return {resizedLo, resizedLo + size};
297 return {resizedLo, resizedLo + size * 8};
298 }
299
305 auto asBits = toUnit<RangeUnit::Bit>();
306 return {asBits.lo + offset, asBits.hi + offset};
307 }
308
313 if (empty()) return HalfOpenRange();
314 if (Unit == RangeUnit::Byte) return {lo + offset, hi + offset};
315 return {lo + offset * 8, hi + offset * 8};
316 }
317
319 int loByte() const {
320 if (empty()) return 0;
321 return Unit == RangeUnit::Byte ? lo : BitRange::Detail::divideFloor(lo, 8);
322 }
323
325 int hiByte() const {
326 if (empty()) return 0;
327 return Unit == RangeUnit::Byte ? hi - 1 : BitRange::Detail::divideFloor(hi - 1, 8);
328 }
329
333 int nextByte() const { return empty() ? 0 : hiByte() + 1; }
334
337 bool isLoAligned() const {
338 return (empty() || Unit == RangeUnit::Byte) ? true
339 : BitRange::Detail::moduloFloor(lo, 8) == 0;
340 }
341
345 bool isHiAligned() const {
346 return (empty() || Unit == RangeUnit::Byte) ? true
347 : BitRange::Detail::moduloFloor(hi, 8) == 0;
348 }
349
350 bool operator==(HalfOpenRange other) const {
351 if (empty()) return other.empty();
352 return other.lo == lo && other.hi == hi;
353 }
354 bool operator!=(HalfOpenRange other) const { return !(*this == other); }
355
357 bool empty() const { return lo == hi; }
358
360 bool contains(int index) const { return index >= lo && index < hi; }
361
364 bool contains(HalfOpenRange other) const { return intersectWith(other) == other; }
365
369 bool overlaps(HalfOpenRange a) const { return !intersectWith(a).empty(); }
370 bool overlaps(int l, int h) const { return !intersectWith(l, h).empty(); }
371
376 HalfOpenRange intersectWith(int l, int h) const {
377 HalfOpenRange rv = {std::max(lo, l), std::min(hi, h)};
378 if (rv.hi <= rv.lo) return {0, 0};
379 return rv;
380 }
381 HalfOpenRange operator&(HalfOpenRange a) const { return intersectWith(a); }
382 HalfOpenRange operator&=(HalfOpenRange a) {
383 *this = intersectWith(a);
384 return *this;
385 }
386
392 HalfOpenRange unionWith(int l, int h) const {
393 if (empty()) return {l, h};
394 if (l == h) return *this;
395 return HalfOpenRange(std::min(lo, l), std::max(hi, h));
396 }
397 HalfOpenRange operator|(HalfOpenRange a) const { return unionWith(a); }
398 HalfOpenRange operator|=(HalfOpenRange a) {
399 *this = unionWith(a);
400 return *this;
401 }
402
425 template <Endian DestOrder>
427 if (DestOrder == Order) return HalfOpenRange<Unit, DestOrder>(lo, hi);
428 switch (DestOrder) {
429 case Endian::Network:
430 case Endian::Little:
431 return HalfOpenRange<Unit, DestOrder>(spaceSize - hi, spaceSize - lo);
432 }
433 BUG("Unexpected ordering");
434 }
435
451 template <RangeUnit DestUnit>
453 if (DestUnit == Unit) return HalfOpenRange<DestUnit, Order>(lo, hi);
455 switch (DestUnit) {
456 case RangeUnit::Bit:
457 return HalfOpenRange<DestUnit, Order>(lo * 8, hi * 8);
458 case RangeUnit::Byte:
460 }
461 BUG("Unexpected unit");
462 }
463
465 void toJSON(JSONGenerator &json) const { BitRange::rangeToJSON(json, lo, hi); }
466 static HalfOpenRange fromJSON(JSONLoader &json) {
467 return HalfOpenRange(BitRange::rangeFromJSON(json));
468 }
469
471 bool operator<(const HalfOpenRange &other) const {
472 if (lo != other.lo) return lo < other.lo;
473 return hi < other.hi;
474 }
475
476 friend size_t hash_value(const HalfOpenRange &r) { return Util::Hash{}(r.lo, r.hi); }
477
481 int lo;
482
487 int hi;
488};
489
511template <RangeUnit Unit, Endian Order>
513 static constexpr RangeUnit unit = Unit;
514 static constexpr Endian order = Order;
515
516 using FromTo = BitRange::FromTo;
520
521 ClosedRange() : lo(0), hi(0) {} // FIXME: default is [0,0]? This is just wrong ...
522 constexpr ClosedRange(int lo, int hi) : lo(lo), hi(hi) {}
523 ClosedRange(FromTo &&fromTo) // NOLINT(runtime/explicit)
524 : lo(fromTo.from), hi(fromTo.to) {}
525 ClosedRange(StartLen &&startLen) // NOLINT(runtime/explicit)
526 : lo(startLen.start), hi(startLen.start + startLen.len - 1) {}
527 ClosedRange(ZeroToMax &&) // NOLINT(runtime/explicit)
528 : lo(0), hi(std::numeric_limits<int>::max() - 1) {}
529 ClosedRange(MinToMax &&) // NOLINT(runtime/explicit)
530 : lo(std::numeric_limits<int>::min()), hi(std::numeric_limits<int>::max() - 1) {}
531 explicit ClosedRange(std::pair<int, int> range) : lo(range.first), hi(range.second) {}
532 ClosedRange(const HalfOpenRange<Unit, Order> &r) // NOLINT(runtime/explicit)
533 : lo(r.lo), hi(r.hi - 1) {
534 BUG_CHECK(!r.empty(), "can't convert empty range to Closed");
535 }
536
538 ssize_t size() const { return ssize_t(hi) - ssize_t(lo) + 1; }
539
542 BUG_CHECK(size != 0, "Resizing ClosedRange to zero size");
543 auto asBits = toUnit<RangeUnit::Bit>();
544 return {asBits.lo, asBits.lo + size - 1};
545 }
546
549 BUG_CHECK(size != 0, "Resizing ClosedRange to zero size");
550 if (Unit == RangeUnit::Byte) return {lo, lo + size - 1};
551 return {lo, lo + size * 8 - 1};
552 }
553
556 auto asBits = toUnit<RangeUnit::Bit>();
557 return {asBits.lo + offset, asBits.hi + offset};
558 }
559
561 ClosedRange shiftedByBytes(int offset) const {
562 if (Unit == RangeUnit::Byte) return {lo + offset, hi + offset};
563 return {lo + offset * 8, hi + offset * 8};
564 }
565
567 int loByte() const {
568 return Unit == RangeUnit::Byte ? lo : BitRange::Detail::divideFloor(lo, 8);
569 }
570
572 int hiByte() const {
573 return Unit == RangeUnit::Byte ? hi : BitRange::Detail::divideFloor(hi, 8);
574 }
575
577 int nextByte() const { return hiByte() + 1; }
578
580 bool isLoAligned() const {
581 return Unit == RangeUnit::Byte ? true : BitRange::Detail::moduloFloor(lo, 8) == 0;
582 }
583
585 bool isHiAligned() const {
586 return Unit == RangeUnit::Byte ? true : BitRange::Detail::moduloFloor(hi + 1, 8) == 0;
587 }
588
589 bool operator==(ClosedRange other) const { return (other.lo == lo) && (other.hi == hi); }
590 bool operator!=(ClosedRange other) const { return !(*this == other); }
591
593 bool contains(int index) const { return (index >= lo) && (index <= hi); }
594
596 bool contains(ClosedRange other) const {
597 auto intersection = intersectWith(other);
598 return intersection.lo == other.lo && intersection.size() == other.size();
599 }
600
602 bool overlaps(ClosedRange a) const { return !intersectWith(a).empty(); }
603 bool overlaps(int l, int h) const { return !intersectWith(l, h).empty(); }
604
612 HalfOpenRange<Unit, Order> intersectWith(int l, int h) const {
613 return HalfOpenRange<Unit, Order>(lo, hi + 1)
615 }
616 HalfOpenRange<Unit, Order> operator&(ClosedRange a) const { return intersectWith(a); }
618 *this = intersectWith(a);
619 return *this;
620 }
621
623 ClosedRange unionWith(ClosedRange a) const { return unionWith(a.lo, a.hi); }
624 ClosedRange unionWith(int l, int h) const {
625 return ClosedRange(std::min(lo, l), std::max(hi, h));
626 }
627 ClosedRange operator|(ClosedRange a) const { return unionWith(a); }
628 ClosedRange operator|=(ClosedRange a) {
629 *this = unionWith(a);
630 return *this;
631 }
632
634 template <Endian DestOrder>
636 BUG_CHECK(spaceSize > 0, "Can't represent an empty range");
637 if (DestOrder == Order) return ClosedRange<Unit, DestOrder>(lo, hi);
638 switch (DestOrder) {
639 case Endian::Network:
640 case Endian::Little:
641 return ClosedRange<Unit, DestOrder>((spaceSize - 1) - hi, (spaceSize - 1) - lo);
642 }
643 BUG("Unexpected ordering");
644 }
645
647 template <RangeUnit DestUnit>
649 if (DestUnit == Unit) return ClosedRange<DestUnit, Order>(lo, hi);
650 switch (DestUnit) {
651 case RangeUnit::Bit:
652 return ClosedRange<DestUnit, Order>(lo * 8, hi * 8 + 7);
653 case RangeUnit::Byte:
655 }
656 BUG("Unexpected unit");
657 }
658
660 void toJSON(JSONGenerator &json) const { BitRange::rangeToJSON(json, lo, hi); }
661 static ClosedRange fromJSON(JSONLoader &json) {
662 return ClosedRange(BitRange::rangeFromJSON(json));
663 }
664
666 bool operator<(const ClosedRange &other) const {
667 if (lo != other.lo) return lo < other.lo;
668 return hi < other.hi;
669 }
670
671 friend size_t hash_value(const ClosedRange &r) { return Util::Hash{}(r.lo, r.hi); }
672
675 cstring formatAsSlice(int spaceSize) const {
676 auto r = toOrder<Endian::Little>(spaceSize);
677 auto hi = r.hi;
678 auto lo = r.lo;
679 if (Unit == RangeUnit::Byte) {
680 lo = lo * 8;
681 hi = hi * 8 + 7;
682 } else {
683 BUG_CHECK(Unit == RangeUnit::Bit, "mismatch range units");
684 }
685 std::stringstream out;
686 out << "[" << hi << ":" << lo << "]";
687 return cstring(out.str());
688 }
689
693 int lo;
694
699 int hi;
700};
701
714template <RangeUnit Unit, Endian Order>
715std::pair<HalfOpenRange<Unit, Order>, HalfOpenRange<Unit, Order>> operator-(
717 HalfOpenRange<Unit, Order> empty = {0, 0};
718 HalfOpenRange<Unit, Order> intersect = left.intersectWith(right);
719 if (intersect.empty())
720 return left.lo < right.lo ? std::make_pair(left, empty) : std::make_pair(empty, left);
721
723 left.lo == intersect.lo ? empty : BitRange::FromTo(left.lo, intersect.lo - 1);
725 left.hi == intersect.hi ? empty : BitRange::FromTo(intersect.hi, left.hi - 1);
726
727 return {lower, upper};
728}
729
732template <RangeUnit Unit, Endian Order>
733std::pair<HalfOpenRange<Unit, Order>, HalfOpenRange<Unit, Order>> operator-(
735 return toHalfOpenRange(left) - toHalfOpenRange(right);
736}
737
740template <RangeUnit Unit, Endian Order>
741HalfOpenRange<Unit, Order> toHalfOpenRange(ClosedRange<Unit, Order> closedRange) {
742 return HalfOpenRange<Unit, Order>(closedRange.lo, closedRange.hi + 1);
743}
744
747template <RangeUnit Unit, Endian Order>
748std::optional<ClosedRange<Unit, Order>> toClosedRange(HalfOpenRange<Unit, Order> halfOpenRange) {
749 if (halfOpenRange.empty()) return std::nullopt;
750 return ClosedRange<Unit, Order>(halfOpenRange.lo, halfOpenRange.hi - 1);
751}
752
756
760
764
768
769std::ostream &toStream(std::ostream &out, RangeUnit unit, Endian order, int lo, int hi,
770 bool closed);
771
772template <RangeUnit Unit, Endian Order>
773std::ostream &operator<<(std::ostream &out, const HalfOpenRange<Unit, Order> &range) {
774 return toStream(out, Unit, Order, range.lo, range.hi, false);
775}
776
777template <RangeUnit Unit, Endian Order>
778std::ostream &operator<<(std::ostream &out, const ClosedRange<Unit, Order> &range) {
779 return toStream(out, Unit, Order, range.lo, range.hi, true);
780}
781
782// Hashing specializations
783namespace std {
784template <RangeUnit Unit, Endian Order>
785struct hash<HalfOpenRange<Unit, Order>> {
786 std::size_t operator()(const HalfOpenRange<Unit, Order> &r) const {
787 return Util::Hash{}(r.lo, r.hi);
788 }
789};
790
791template <RangeUnit Unit, Endian Order>
792struct hash<ClosedRange<Unit, Order>> {
793 std::size_t operator()(const ClosedRange<Unit, Order> &r) const {
794 return Util::Hash{}(r.lo, r.hi);
795 }
796};
797} // namespace std
798
799namespace Util {
800template <RangeUnit Unit, Endian Order>
801struct Hasher<HalfOpenRange<Unit, Order>> {
802 size_t operator()(const HalfOpenRange<Unit, Order> &r) const {
803 return Util::Hash{}(r.lo, r.hi);
804 }
805};
806
807template <RangeUnit Unit, Endian Order>
808struct Hasher<ClosedRange<Unit, Order>> {
809 size_t operator()(const ClosedRange<Unit, Order> &r) const { return Util::Hash{}(r.lo, r.hi); }
810};
811} // namespace Util
812
813#endif /* LIB_BITRANGE_H_ */
Definition bitrange.h:33
Definition bitvec.h:200
Definition bitvec.h:119
Definition cstring.h:72
STL namespace.
Definition bitrange.h:140
Definition bitrange.h:208
Definition bitrange.h:157
Definition bitrange.h:189
Definition bitrange.h:512
HalfOpenRange< Unit, Order > intersectWith(ClosedRange a) const
Definition bitrange.h:609
ClosedRange resizedToBytes(int size) const
Definition bitrange.h:548
ClosedRange< Unit, DestOrder > toOrder(int spaceSize) const
Definition bitrange.h:635
int nextByte() const
Definition bitrange.h:577
bool operator<(const ClosedRange &other) const
Definition bitrange.h:666
int lo
Definition bitrange.h:693
bool isLoAligned() const
Definition bitrange.h:580
ClosedRange shiftedByBytes(int offset) const
Definition bitrange.h:561
bool contains(int index) const
Definition bitrange.h:593
bool isHiAligned() const
Definition bitrange.h:585
ssize_t size() const
Definition bitrange.h:538
ClosedRange< RangeUnit::Bit, Order > shiftedByBits(int offset) const
Definition bitrange.h:555
ClosedRange unionWith(ClosedRange a) const
Definition bitrange.h:623
int loByte() const
Definition bitrange.h:567
bool contains(ClosedRange other) const
Definition bitrange.h:596
cstring formatAsSlice(int spaceSize) const
Definition bitrange.h:675
ClosedRange< DestUnit, Order > toUnit() const
Definition bitrange.h:648
ClosedRange< RangeUnit::Bit, Order > resizedToBits(int size) const
Definition bitrange.h:541
bool overlaps(ClosedRange a) const
Definition bitrange.h:602
int hiByte() const
Definition bitrange.h:572
int hi
Definition bitrange.h:699
void toJSON(JSONGenerator &json) const
JSON serialization/deserialization.
Definition bitrange.h:660
Definition bitrange.h:252
ssize_t size() const
Definition bitrange.h:274
HalfOpenRange< Unit, Order > shiftedByBytes(int offset) const
Definition bitrange.h:312
bool contains(int index) const
Definition bitrange.h:360
HalfOpenRange< RangeUnit::Bit, Order > shiftedByBits(int offset) const
Definition bitrange.h:303
bool operator<(const HalfOpenRange &other) const
Total ordering, first by lo, then by hi.
Definition bitrange.h:471
HalfOpenRange< Unit, DestOrder > toOrder(int spaceSize) const
Definition bitrange.h:426
bool isLoAligned() const
Definition bitrange.h:337
HalfOpenRange resizedToBytes(int size) const
Definition bitrange.h:294
int lo
Definition bitrange.h:481
bool contains(HalfOpenRange other) const
Definition bitrange.h:364
HalfOpenRange< DestUnit, Order > toUnit() const
Definition bitrange.h:452
int hi
Definition bitrange.h:487
bool isHiAligned() const
Definition bitrange.h:345
HalfOpenRange< RangeUnit::Bit, Order > resizedToBits(int size) const
Definition bitrange.h:286
int hiByte() const
Definition bitrange.h:325
HalfOpenRange intersectWith(HalfOpenRange a) const
Definition bitrange.h:375
int nextByte() const
Definition bitrange.h:333
bool overlaps(HalfOpenRange a) const
Definition bitrange.h:369
HalfOpenRange canonicalize() const
Definition bitrange.h:278
HalfOpenRange unionWith(HalfOpenRange a) const
Definition bitrange.h:391
bool empty() const
Definition bitrange.h:357
void toJSON(JSONGenerator &json) const
JSON serialization/deserialization.
Definition bitrange.h:465
int loByte() const
Definition bitrange.h:319
Definition hash.h:125
Definition hash.h:123