| 34 | # This number always increases, even when diverging into two |
| 35 | # timelines, so it's useful for conservative garbage collection. |
| 36 | class SegmentNumber(collections.namedtuple('SegmentNumber', |
| 37 | ['log', 'seg'])): |
| 38 | |
| 39 | @property |
| 40 | def as_an_integer(self): |
| 41 | assert len(self.log) == 8 |
| 42 | assert len(self.seg) == 8 |
| 43 | return int(self.log + self.seg, 16) |
| 44 | |
| 45 | @staticmethod |
| 46 | def _integer_to_name(integral): |
| 47 | h = hex(integral) |
| 48 | assert h.startswith('0x') |
| 49 | assert len(h) <= 10 |
| 50 | |
| 51 | name = hex(integral)[2:].upper().zfill(8) |
| 52 | assert len(name) == 8 |
| 53 | |
| 54 | return name |
| 55 | |
| 56 | def next_smaller(self): |
| 57 | int_seg = int(self.seg, 16) |
| 58 | assert int_seg <= 0xFF |
| 59 | |
| 60 | if int_seg == 0: |
| 61 | # Seg is at the minimum value, carry the decrement from |
| 62 | # 'log'. |
| 63 | int_log = int(self.log, 16) |
| 64 | assert int_log <= 0xFFFFFFFF |
| 65 | |
| 66 | # 0 is a valid int_log, but not when decrementing from a |
| 67 | # 0-valued segment: negative such values are not valid. |
| 68 | assert int_seg == 0 and int_log > 0 |
| 69 | |
| 70 | # NB: older versions of Postgres won't generate a seg |
| 71 | # value of 0xFF, so a gap has to be treated |
| 72 | # conservatively. |
| 73 | s_log = SegmentNumber._integer_to_name(int_log - 1) |
| 74 | s_seg = SegmentNumber._integer_to_name(0xFF) |
| 75 | |
| 76 | return self.__class__(log=s_log, seg=s_seg) |
| 77 | else: |
| 78 | assert int_seg > 0 |
| 79 | |
| 80 | s_log = self.log |
| 81 | s_seg = SegmentNumber._integer_to_name(int_seg - 1) |
| 82 | |
| 83 | return self.__class__(log=s_log, seg=s_seg) |
| 84 | |
| 85 | assert False |
| 86 | |
| 87 | def next_larger(self): |
| 88 | int_seg = int(self.seg, 16) |
| 89 | assert int_seg <= 0xFF |
| 90 | |
| 91 | if int_seg == 0xFF: |
| 92 | # Seg is at the maximum value, carry the increment into |
| 93 | # 'log'. |