kopia lustrzana https://github.com/peterhinch/micropython-samples
138 wiersze
3.2 KiB
Python
138 wiersze
3.2 KiB
Python
![]() |
# bitmap.py
|
||
|
# Ideas for non-allocating classes based around a bitmap. Three classes are
|
||
|
# presented:
|
||
|
# IntSet: this is a set which is constrained to accept only integers in
|
||
|
# a range 0 <= i <= maxval. Provides a minimal set of methods which can readily
|
||
|
# be expanded.
|
||
|
# SetByte: an even more minimal version of IntSet where the range is
|
||
|
# 0 <= i <=255.
|
||
|
# BoolList: A list of booleans. The list index is constrained to lie in range
|
||
|
# 0 <= i <= maxval.
|
||
|
|
||
|
class BitMap:
|
||
|
def __init__(self, maxval):
|
||
|
d, m = divmod(maxval, 8)
|
||
|
self._ba = bytearray(d + int(m > 0))
|
||
|
self._size = maxval
|
||
|
|
||
|
def _check(self, i):
|
||
|
if i < 0 or i >= self._size:
|
||
|
raise ValueError('Index out of range')
|
||
|
|
||
|
def _val(self, i):
|
||
|
self._check(i)
|
||
|
return (self._ba[i >> 3] & 1 << (i & 7)) > 0
|
||
|
|
||
|
def _set(self, i):
|
||
|
self._check(i)
|
||
|
self._ba[i >> 3] |= 1 << (i & 7)
|
||
|
|
||
|
def _clear(self, i):
|
||
|
self._check(i)
|
||
|
self._ba[i >> 3] &= ~(1 << (i &7))
|
||
|
|
||
|
# Iterate through an IntSet returning members
|
||
|
# Iterate through an IntList returning index value of True members
|
||
|
def __iter__(self):
|
||
|
for i in range(self._size):
|
||
|
if self._val(i):
|
||
|
yield i
|
||
|
|
||
|
# if MyIntSet: True unless set is empty
|
||
|
# if MyBoolList: True if any element is True
|
||
|
def __bool__(self):
|
||
|
for x in self._ba:
|
||
|
if x:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
|
||
|
class IntSet(BitMap):
|
||
|
def __init__(self, maxval=256):
|
||
|
super().__init__(maxval)
|
||
|
|
||
|
# if n in MyIntSet:
|
||
|
def __contains__(self, i):
|
||
|
self._check(i)
|
||
|
return self._val(i)
|
||
|
|
||
|
# MyIntSet.discard(n)
|
||
|
def discard(self, i):
|
||
|
self._clear(i)
|
||
|
|
||
|
# MyIntSet.remove(n)
|
||
|
def remove(self, i):
|
||
|
if i in self:
|
||
|
self.discard(i)
|
||
|
else:
|
||
|
raise KeyError(i)
|
||
|
|
||
|
# MyIntSet.add(n)
|
||
|
def add(self, i):
|
||
|
self._set(i)
|
||
|
|
||
|
# Generator iterates through set intersection. Avoids allocation.
|
||
|
def intersec(self, other):
|
||
|
for i in other:
|
||
|
if i in self:
|
||
|
yield i
|
||
|
|
||
|
class BoolList(BitMap):
|
||
|
def __init__(self, maxval=256):
|
||
|
super().__init__(maxval)
|
||
|
|
||
|
# MyBoolList[n] = True
|
||
|
def __setitem__(self, bit, val):
|
||
|
self._set(bit) if val else self._clear(bit)
|
||
|
|
||
|
# if MyBoolList[n]:
|
||
|
def __getitem__(self, bit):
|
||
|
return self._val(bit)
|
||
|
|
||
|
# if False in MyBoolList:
|
||
|
def __contains__(self, i):
|
||
|
if i:
|
||
|
for b in self._ba:
|
||
|
if b:
|
||
|
return True
|
||
|
else:
|
||
|
for b in self._ba:
|
||
|
if not b:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
|
||
|
# Minimal implementation of set for 0-255
|
||
|
class SetByte:
|
||
|
def __init__(self):
|
||
|
self._ba = bytearray(32)
|
||
|
|
||
|
def __bool__(self):
|
||
|
for x in self._ba:
|
||
|
if x:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def __contains__(self, i):
|
||
|
return (self._ba[i >> 3] & 1 << (i & 7)) > 0
|
||
|
|
||
|
def discard(self, i):
|
||
|
self._ba[i >> 3] &= ~(1 << (i &7))
|
||
|
|
||
|
def add(self, i):
|
||
|
self._ba[i >> 3] |= 1 << (i & 7)
|
||
|
|
||
|
# Test for set intersection
|
||
|
bs = IntSet()
|
||
|
bs.add(1)
|
||
|
bs.add(2)
|
||
|
1 in bs
|
||
|
c = IntSet()
|
||
|
c.add(1)
|
||
|
c.add(4)
|
||
|
c.add(5)
|
||
|
g = bs.intersec(c)
|
||
|
next(g)
|
||
|
next(g)
|
||
|
|