圧縮プログラミングのベースとなる指定ビットの埋め込み

setdataメソッドで、第1引数のデータを指定のビット幅で、バイト列に埋め込む能力のクラス

''' 1〜16bitの指定のデータを詰めてbyteごとにcallbackを実行する。'''
class Bit2Byte:
    def __init__(self, callback ):
        self.bit32=0        # bit設定の作業用変数
        self.bitcount=0     # 上記への記憶bit数。 8bit毎にcallback処理
        self.callback=callback
    #
    def setdata(self, data, bitsize): # dataをbyte列に変換 ビット列構築用で使う。
        '''bitsizeのビット数で、dataを記憶 1byte分に詰められたタイミングで callbackを呼ぶ'''
        bitmask = (1<<bitsize)-1
        if data > bitmask : raise Exception(f"{data} does not fit in {bitsize} bit")
        self.bit32 |= data << self.bitcount
        self.bitcount += bitsize
        while self.bitcount >= 8:
            callback(self.bit32 & 0x0ff)
            self.bit32 >>= 8
            self.bitcount-=8
    #
    def padding(self): # ビット列構築用で使う
        ''' bit_sizeサイズが、丁度byteに達するように0で埋める'''
        if self.bitcount != 0:
            self.setdata(0, 8-self.bitcount) # bit列をbyte列に変換

out=print
ba=bytearray()
def callback( bt ):
    out( f"{bt:08b}", end=" ") # 01001000 10100010 00110000 の表示
    ba.append(bt)

bb=Bit2Byte( callback )
for value in [ 8 , 9 , 10 , 12 ]: # 記憶対象のデータ群
    bb.setdata(value,6) # valueを6bitで記憶

bb.setdata(13,6) # valueを6bitで記憶
bb.padding()

print(ba) # bytearray(b'H\xa20') の3byteで表示

上記のBit2Byteののオブジェクトから、getdataメソッドでデータを復元するためのクラス

''' byteのデータ群から指定のビット長を取り出す '''
class Byte2Bit:
    def __init__(self, bytedata:bytes ): # 圧縮データを引数するコンストラクタ
        self.byte_buf = bytearray(bytedata)
        self.byte_buf_len=len(bytedata)     # 上記のbit記憶数
        self.bit32=0        # bit設定の作業用変数(0〜8)
        self.bitcount=0     # 上記へのbit記憶数。 
    #
    def getdata(self,bitsize): # ビット列復元用で使う
        '''bitsizeのビット数の値を取り出して返す '''
        rv = (self.byte_buf_len << 3) + self.bitcount - bitsize
        if rv < 0 : return rv  # 負の値(足りないビット数)
        bitmask = (1<<bitsize)-1
        while self.bitcount < bitsize and self.byte_buf_len>0:
            byte0 = self.byte_buf.pop(0)
            self.byte_buf_len-=1
            self.bit32 |= byte0 << self.bitcount
            self.bitcount += 8
        val = self.bit32 & bitmask
        self.bit32 >>= bitsize
        self.bitcount -= bitsize
        return val
    #
    def append(self, bytedata:bytes ): # 圧縮データを追加
        for i in range( len( bytedata ) ):
            self.byte_buf.append( bytedata[i] )
            self.byte_buf_len+=1     # 上記のbit記憶数


a=[0b01001000, 0b10100010, 0b00110000, 0b00001101]
byt=Byte2Bit(bytearray( a ))
while True:
    value=byt.getdata(6) # 6bit長データを取り出す。
    if value < 0: break # 取り出せなない? (終了)
    print( value ) # 取り出しデータの表示

intのリストから、指定ビット幅で詰め込んだバイナリ列を生成

# intのリストから、指定ビット幅で詰め込んだバイナリ列を生成
class ListToBytes:
   def __init__(self, data_list: list, bit_depth:int ):
      self.bit32=0        # bit設定の作業用変数
      self.bitCount=0     # 上記への記憶bit数。 8bit毎に変換処理
      self.bitSize=0      # データのビットサイズ
      self.bit_depth=bit_depth # 変換するビット長
      self.byte_buf = bytearray()
      for value in data_list: # 記憶対象のデータ群
         self.setdata(value,bit_depth) # valueを6bitで記憶
      self.padding()
   #
   def setdata(self, data, bitSize): # bit列をbyte列に変換 ビット列構築用で使う。
      '''bitsizeのビット数で、dataを記憶 1byte分に詰められたタイミングで byte_bufに追加'''
      bitmask = (1<<bitSize)-1
      if data > bitmask : raise Exception(f"{data} does not fit in {bitSize} bit")
      self.bit32 |= data << self.bitCount
      self.bitCount += bitSize
      #print(f"{self.bit32:032b}")
      while self.bitCount >= 8:
         self.byte_buf.append(self.bit32 & 0x0ff)
         self.bit32 >>= 8
         self.bitCount-=8
   #
   def padding(self): # ビット列構築用で使う
      ''' bit_sizeサイズが、丁度byteに達するように0で埋める'''
      if self.bitCount != 0:
         self.setdata(0, 8-self.bitCount) # bit列をbyte列に変換
   #
   def debug_string(self):# 下位ビットからビット列を並べた文字列を取得
      s=""
      for v in self.byte_buf:s+=''.join(list(reversed(f"{v:08b}")))
      return s

a=[1,3,7,15]
obj=ListToBytes(a, 8)
print(obj.byte_buf, obj.debug_string())
print(ListToBytes(a, 6).debug_string())
実行結果
bytearray(b'\x01\x03\x07\x0f') 10000000110000001110000011110000
100000110000111000111100