bech32

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

commit 898757c3e9bde9698d51e9f34391eaa52d9b6030
parent 06d049b967a75f1d0dc8d96af22cbdbb2325c617
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 16 May 2026 19:34:44 -0230

lib: drop builder from bech32 final concat

Replaces the 'toStrict $ BSB.byteString hrp <> BSB.word8 49 <>
BSB.byteString dat <> BSB.byteString (as_base32 check)' pattern in
'Data.ByteString.Bech32.encode' and 'Data.ByteString.Bech32m.encode'
with a single 'BS.concat'.  No more builder, no more 'safeStrategy'
buffer.

Drops the now-unused 'Data.ByteString.Builder' /
'Data.ByteString.Builder.Extra' imports and the local 'toStrict'
helper in both modules.

Benchmarks (M4 Air, -fllvm):

  bech32 encode 120b   456.4 ns -> 235.4 ns   (1.94x)
  bech32 decode 120b   508.8 ns -> 264.5 ns   (1.92x)

Diffstat:
Mlib/Data/ByteString/Bech32.hs | 14+-------------
Mlib/Data/ByteString/Bech32m.hs | 14+-------------
2 files changed, 2 insertions(+), 26 deletions(-)

diff --git a/lib/Data/ByteString/Bech32.hs b/lib/Data/ByteString/Bech32.hs @@ -25,17 +25,9 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Base32 as B32 import qualified Data.ByteString.Bech32.Internal as BI -import qualified Data.ByteString.Builder as BSB -import qualified Data.ByteString.Builder.Extra as BE import qualified Data.ByteString.Internal as BSI import qualified Data.Char as C (toLower, isLower, isAlpha) --- realization for small builders -toStrict :: BSB.Builder -> BS.ByteString -toStrict = BS.toStrict - . BE.toLazyByteStringWith (BE.safeStrategy 128 BE.smallChunkSize) mempty -{-# INLINE toStrict #-} - create_checksum :: BS.ByteString -> BS.ByteString -> BS.ByteString create_checksum = BI.create_checksum BI.Bech32 @@ -52,11 +44,7 @@ encode (B8.map C.toLower -> hrp) (B32.encode -> dat) = do guard (BI.valid_hrp hrp) ws <- BI.as_word5 dat let check = create_checksum hrp ws - res = toStrict $ - BSB.byteString hrp - <> BSB.word8 49 -- 1 - <> BSB.byteString dat - <> BSB.byteString (BI.as_base32 check) + res = BS.concat [hrp, BS.singleton 49, dat, BI.as_base32 check] guard (BS.length res < 91) pure res diff --git a/lib/Data/ByteString/Bech32m.hs b/lib/Data/ByteString/Bech32m.hs @@ -26,17 +26,9 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Base32 as B32 import qualified Data.ByteString.Bech32.Internal as BI -import qualified Data.ByteString.Builder as BSB -import qualified Data.ByteString.Builder.Extra as BE import qualified Data.ByteString.Internal as BSI import qualified Data.Char as C (toLower) --- realization for small builders -toStrict :: BSB.Builder -> BS.ByteString -toStrict = BS.toStrict - . BE.toLazyByteStringWith (BE.safeStrategy 128 BE.smallChunkSize) mempty -{-# INLINE toStrict #-} - create_checksum :: BS.ByteString -> BS.ByteString -> BS.ByteString create_checksum = BI.create_checksum BI.Bech32m @@ -53,11 +45,7 @@ encode (B8.map C.toLower -> hrp) (B32.encode -> dat) = do guard (BI.valid_hrp hrp) ws <- BI.as_word5 dat let check = create_checksum hrp ws - res = toStrict $ - BSB.byteString hrp - <> BSB.word8 49 -- 1 - <> BSB.byteString dat - <> BSB.byteString (BI.as_base32 check) + res = BS.concat [hrp, BS.singleton 49, dat, BI.as_base32 check] guard (BS.length res < 91) pure res