bech32

Pure Haskell bech32, bech32m encodings (docs.ppad.tech/bech32).
git clone git://git.ppad.tech/bech32.git
Log | Files | Refs | README | LICENSE

commit 4da05856fb5f37f37170fa594f6b28ec2d07bdd4
parent ba6d5429cdbcfe1b5da2933ddd3b390313013b79
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri,  3 Jan 2025 15:39:11 -0330

lib: separate word5

Diffstat:
Mlib/Data/ByteString/Base32.hs | 157+++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 77 insertions(+), 80 deletions(-)

diff --git a/lib/Data/ByteString/Base32.hs b/lib/Data/ByteString/Base32.hs @@ -53,6 +53,9 @@ toStrict = BS.toStrict bech32_charset :: BS.ByteString bech32_charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" +word5 :: Word8 -> Maybe Word8 +word5 w8 = fmap fi (BS.elemIndex w8 bech32_charset) + arrange :: Word32 -> Word8 -> BSB.Builder arrange w32 w8 = let mask = 0b00011111 -- low 5-bit mask @@ -203,90 +206,84 @@ decode = handle . go mempty where finalize :: BS.ByteString -> Maybe BSB.Builder finalize bs@(BI.PS _ _ l) - | l == 0 = Just mempty - | otherwise = do - guard (l >= 2) - w5_0 <- word5 0 - w5_1 <- word5 1 - let w8_0 = w5_0 `B.shiftL` 3 - .|. w5_1 `B.shiftR` 2 - - -- https://datatracker.ietf.org/doc/html/rfc4648#section-6 - if | l == 2 -> do -- 2 w5's, need 1 w8 (XX final 2 bits?) - guard (w5_1 `B.shiftL` 6 == 0) -- lowest 2 bits -- XX - pure (BSB.word8 w8_0) - - | l == 4 -> do -- 4 w5's, need 2 w8's - w5_2 <- word5 2 - w5_3 <- word5 3 - let w8_1 = w5_1 `B.shiftL` 6 - .|. w5_2 `B.shiftL` 1 - .|. w5_3 `B.shiftR` 4 - - w16 = fi w8_1 - .|. fi w8_0 `B.shiftL` 8 - - guard (w5_3 `B.shiftL` 4 == 0) -- lowest 4 bits - pure (BSB.word16LE w16) - - | l == 5 -> do -- 5 w5's, need 3 w8's - w5_2 <- word5 2 - w5_3 <- word5 3 - w5_4 <- word5 4 - let w8_1 = w5_1 `B.shiftL` 6 - .|. w5_2 `B.shiftL` 1 - .|. w5_3 `B.shiftR` 4 - w8_2 = w5_3 `B.shiftL` 4 - .|. w5_4 `B.shiftR` 1 - - w16 = fi w8_1 - .|. fi w8_0 `B.shiftL` 8 - - guard (w5_4 `B.shiftL` 7 == 0) -- lowest bit - pure (BSB.word16LE w16 <> BSB.word8 w8_2) - - | l == 7 -> do -- 7 w5's, need 4 w8's - w5_2 <- word5 2 - w5_3 <- word5 3 - w5_4 <- word5 4 - w5_5 <- word5 5 - w5_6 <- word5 6 - let w8_1 = w5_1 `B.shiftL` 6 - .|. w5_2 `B.shiftL` 1 - .|. w5_3 `B.shiftR` 4 - w8_2 = w5_3 `B.shiftL` 4 - .|. w5_4 `B.shiftR` 1 - w8_3 = w5_4 `B.shiftL` 7 - .|. w5_5 `B.shiftL` 2 - .|. w5_6 `B.shiftR` 3 - - w32 = fi w8_3 - .|. fi w8_2 `B.shiftL` 8 - .|. fi w8_2 `B.shiftL` 16 - .|. fi w8_1 `B.shiftL` 24 - - guard (w5_6 `B.shiftL` 5 == 0) -- lowest 3 bits - pure (BSB.word32LE w32) - - | otherwise -> Nothing - - where - word5 :: Int -> Maybe Word8 - word5 i = fmap fi (BS.elemIndex (fi (BU.unsafeIndex bs i)) bech32_charset) + | l == 0 = Just mempty + | otherwise = do + guard (l >= 2) + w5_0 <- word5 (BU.unsafeIndex bs 0) + w5_1 <- word5 (BU.unsafeIndex bs 1) + let w8_0 = w5_0 `B.shiftL` 3 + .|. w5_1 `B.shiftR` 2 + + -- https://datatracker.ietf.org/doc/html/rfc4648#section-6 + if | l == 2 -> do -- 2 w5's, need 1 w8 (XX final 2 bits?) + guard (w5_1 `B.shiftL` 6 == 0) -- lowest 2 bits -- XX + pure (BSB.word8 w8_0) + + | l == 4 -> do -- 4 w5's, need 2 w8's + w5_2 <- word5 (BU.unsafeIndex bs 2) + w5_3 <- word5 (BU.unsafeIndex bs 3) + let w8_1 = w5_1 `B.shiftL` 6 + .|. w5_2 `B.shiftL` 1 + .|. w5_3 `B.shiftR` 4 + + w16 = fi w8_1 + .|. fi w8_0 `B.shiftL` 8 + + guard (w5_3 `B.shiftL` 4 == 0) -- lowest 4 bits + pure (BSB.word16LE w16) + + | l == 5 -> do -- 5 w5's, need 3 w8's + w5_2 <- word5 (BU.unsafeIndex bs 2) + w5_3 <- word5 (BU.unsafeIndex bs 3) + w5_4 <- word5 (BU.unsafeIndex bs 4) + let w8_1 = w5_1 `B.shiftL` 6 + .|. w5_2 `B.shiftL` 1 + .|. w5_3 `B.shiftR` 4 + w8_2 = w5_3 `B.shiftL` 4 + .|. w5_4 `B.shiftR` 1 + + w16 = fi w8_1 + .|. fi w8_0 `B.shiftL` 8 + + guard (w5_4 `B.shiftL` 7 == 0) -- lowest bit + pure (BSB.word16LE w16 <> BSB.word8 w8_2) + + | l == 7 -> do -- 7 w5's, need 4 w8's + w5_2 <- word5 (BU.unsafeIndex bs 2) + w5_3 <- word5 (BU.unsafeIndex bs 3) + w5_4 <- word5 (BU.unsafeIndex bs 4) + w5_5 <- word5 (BU.unsafeIndex bs 5) + w5_6 <- word5 (BU.unsafeIndex bs 6) + let w8_1 = w5_1 `B.shiftL` 6 + .|. w5_2 `B.shiftL` 1 + .|. w5_3 `B.shiftR` 4 + w8_2 = w5_3 `B.shiftL` 4 + .|. w5_4 `B.shiftR` 1 + w8_3 = w5_4 `B.shiftL` 7 + .|. w5_5 `B.shiftL` 2 + .|. w5_6 `B.shiftR` 3 + + w32 = fi w8_3 + .|. fi w8_2 `B.shiftL` 8 + .|. fi w8_2 `B.shiftL` 16 + .|. fi w8_1 `B.shiftL` 24 + + guard (w5_6 `B.shiftL` 5 == 0) -- lowest 3 bits + pure (BSB.word32LE w32) + + | otherwise -> Nothing -- assumes length 8 input decode_chunk :: BS.ByteString -> Maybe BSB.Builder decode_chunk bs = do - let word5 i = BS.elemIndex (fi (BU.unsafeIndex bs i)) bech32_charset - - w5_0 <- word5 0 - w5_1 <- word5 1 - w5_2 <- word5 2 - w5_3 <- word5 3 - w5_4 <- word5 4 - w5_5 <- word5 5 - w5_6 <- word5 6 - w5_7 <- word5 7 + w5_0 <- word5 (BU.unsafeIndex bs 0) + w5_1 <- word5 (BU.unsafeIndex bs 1) + w5_2 <- word5 (BU.unsafeIndex bs 2) + w5_3 <- word5 (BU.unsafeIndex bs 3) + w5_4 <- word5 (BU.unsafeIndex bs 4) + w5_5 <- word5 (BU.unsafeIndex bs 5) + w5_6 <- word5 (BU.unsafeIndex bs 6) + w5_7 <- word5 (BU.unsafeIndex bs 7) let w40 :: Word64 w40 = fi w5_0 `B.shiftL` 35