LoginSignup
0
1

More than 1 year has passed since last update.

【Python】bit 単位 でencode するために

Last updated at Posted at 2022-01-23

はじめに

今日はpython でbit 演算子の使い方を調べたのでメモします。バイナリメッセージを生成するときなどに使えると思います。

  • 12bit で値を書いて、続けてuint12 で別の値を書く
  • さらに続けて2bit + int38 を書く
  • それらをつなげる

ことができればミッション達成です。つなげるのは bytes 型を接続するだけなので問題ない。最初の2つ、できるかな。

調査内容

12 bits + 12 bits

bytes 型で8bit 単位で扱うのが基本作戦。なので、ここでは 24bit = 3bytes のbytes を作ることを目標にします。
最初に固定された ID (1005 = 0011 1110 1101)があり、続いて指定された数字を 12bit で続く、というものです。
作戦は、二つの整数値を作って、合体させるです。具体的には、それぞれの位置にシフトさせて、or(論理和)をとる、というものです。

2進数表記は下記で確認できます。

In [2]: bin(1005)
Out[2]: '0b1111101101'

In [3]: 0b1111101101
Out[3]: 1005

In [5]: hex(1005)
Out[5]: '0x3ed'

In [6]: (1005).to_bytes(2,'big')
Out[6]: b'\x03\xed'

このままだと、最初の6bit は0 で2bit + 8bit で1005 を実現しています。0b11 が0x3です。

In [10]: 0b11*256+0b11101101
Out[10]: 1005

なので4bit シフトさせると、8bit (上位は0)+ 4 bit にできます。今は3byteにしたいので 12bit シフトさせます。

In [13]: bin(1005 << 4)
Out[13]: '0b11111011010000'
In [14]: bin(1005 << 12)
Out[14]: '0b1111101101000000000000'

In [15]: (1005 << 4).to_bytes(2, 'big')
Out[15]: b'>\xd0'

In [16]: (1005 << 12).to_bytes(3, 'big')
Out[16]: b'>\xd0\x00'

In [63]: [ f'{x:02X}' for x in int(1005 << 12).to_bytes(3,'big')]
Out[63]: ['3E', 'D0', '00']

指定したい番号nが12だとします。このあと、論理和(or)をとればOKです。なお、bytes 型の文字列表示の読み方が最初分かりませんでしたが、アスキーコードと16進数表示が混じっています。最初の1 bytes はアスキーコードです。下記を試すと分かり易い。b'>'は0x3Eのことです。

In [80]: f'{b">"[0]:X}'
Out[80]: '3E'
In [18]: bin(12)
Out[18]: '0b1100'

In [19]: bin(1005 << (4+8+8) | 12)
Out[19]: '0b1111101101000000001100'

In [20]: int(1005 << 12 | 12).to_bytes(3,'big')
Out[20]: b'>\xd0\x0c'

In [57]: [ f'{x:02X}' for x in int(1005 << 12 | 12).to_bytes(3,'big')]
Out[57]: ['3E', 'D0', '0C']

後半の12bit が 12 (0x0c) になっています。良さそうだ。

2 bits + 38 bits

次のお題は、2bit の後に、指定した数字(int38)を書いた bytes 型を作成する、というものです。

作戦としては、40 / 8 = 5bytes の型を作ります。

最初の2bit を仮に0b01 として、38 bit シフトさせます。

In [84]: a = 1

In [85]: bin(a)
Out[85]: '0b1'

In [86]: bin(a << 38)
Out[86]: '0b100000000000000000000000000000000000000'

書きたい値Xは、38 bit の符号付整数値です。5 bytes で書くのですが、bytes にはunsigned int からしか変換できない。
ので、負の数は2進法38bit の補数であることを考えて

In [214]: bin((x0 + 2**38) % (2**38))
Out[214]: '0b11011011001001010110001110111101001001'

としてみました。38bit あるしあってそう。
'0b11 0110 1100 1001 0101 1000 1110 1111 0100 1001'

ということで、二つの論理和をとるには、これで良いのかな。

In [249]: y = (a << 38 | ((x0 + 2**38) % (2**38)))

In [250]: [ f'{x:08b}' for x in y.to_bytes(5, 'big')]
Out[250]: ['01110110', '11001001', '01011000', '11101111', '01001001']

これでとりあえず、やりたかったことはできたっぽい。

まとめ

bit 演算子を使って、メッセージをencode するためのpython 実装方法を調査。int38 で負の数を扱うために2進数で補数を考えるところを復習してしまった。

明日から一週間、しんどいなー。とりあえず生き延びられるかな。
(2021/01/23)

リンク

  • 素晴らしすぎる bit 演算子の解説

  • 昔書いた bytes 型のメモ

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1