LoginSignup
0
0

More than 1 year has passed since last update.

代入式を使って CRC32 を一つの lambda 式にする (Python 3.8 以降)

Last updated at Posted at 2021-08-23

zlib などで使われている CRC32 は以下のような計算になっています。

Python3
def crc32(data, P=0xedb88320, v=0):
    v = v ^ 0xffffffff
    for d in data:
        v = v ^ d
        for b in range(8):
            v = (v >> 1) ^ ((v & 1) and P)
    return v ^ 0xffffffff

これを、一つの lambda 式にしようとしたとき、data に応じた変数 v の更新が必要ですが、 Python 3.7 以前では v を更新する方法を見つけられませんでした。しかし、Python 3.8 から代入式 ":=" が使えるので、それが可能です。

Python3.8
crc32 = lambda data, P=0xedb88320, v=0xffffffff: 0xffffffff ^ [v := ((v ^ (0 if b else d)) >> 1) ^ (((v ^ (0 if b else d)) & 1) and P) for d in data for b in range(8)][-1]

この式は、入力ビット数(入力バイト数の 8 倍)の配列を一度作るので、CRC32 としての実用性はゼロです。

zlib の crc32 と一致するか確認します。

test.py
#!/usr/bin/env python3.8

import zlib

def crc32a(data, P=0xedb88320, v=0):
    v ^= 0xffffffff
    for d in data:
        v ^= d
        for b in range(8):
            v = (v >> 1) ^ ((v & 1) and P)
    return v ^ 0xffffffff


crc32b = lambda data, P=0xedb88320, v=0xffffffff: 0xffffffff ^ [v := ((v ^ (0 if b else d)) >> 1) ^ (((v ^ (0 if b else d)) & 1) and P) for d in data for b in range(8)][-1]


def test(data, encoding='utf-8'):
    data = bytearray(data) if type(data) != str else bytearray(data, encoding)
    a = crc32a(data)
    b = crc32b(data)
    z = zlib.crc32(data)
    if a != z:
        raise Exception('BUG(crc32a): %#010x != %#010x' % (a, z))
    if b != z:
        raise Exception('BUG(crc32b): %#010x != %#010x' % (b, z))
    print('OK: %#010x' % z)


test('abcd')
test(b'efgh')
実行結果
$ python3.8 test.py
OK: 0xed82cd11
OK: 0x08337bb5

参考: SSE4.2 拡張命令 CRC32 を使ってみる

0
0
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
0