base16

Fast RFC 4648-compliant Base16 encoding

Base16

Build Status Hackage

RFC 4648-compliant Base16 encoding and decoding for strict, lazy, and short ByteString and Text values. Encoding produces lowercase hex; decoding accepts lowercase, uppercase, and mixed case.

The API tracks encoded values with the Base16 wrapper. Use parseBase16 to validate external input and retain its original representation:

{-# LANGUAGE OverloadedStrings #-}
import Data.Base16.Types (extractBase16)
import qualified Data.ByteString.Base16 as B16

extractBase16 (B16.encodeBase16' "Sun") -- "53756e"
B16.parseBase16 "53756E"               -- Right "53756E"
B16.decodeBase16Untyped "53756E"       -- Right "Sun"
B16.parseBase16 "abc"                  -- Left "invalid bytestring size"

For a Boolean check, use isRight . parseBase16 from Data.Either. Version 2.0 removes isBase16 and isValidBase16; see MIGRATION-2.0.md for replacement examples, lazy decoding changes, and compiler requirements. For companion optics and pattern synonyms, see base16-lens.

Lenient Text decoding ignores non-hex characters and a final unpaired hex digit. It preserves valid UTF-8 sequences and interprets each invalid UTF-8 byte as Latin-1, so malformed input does not raise Unicode exceptions. This also applies to lazy Text and ShortText; see the migration guide for details.

SIMD

The optional, manual simd flag enables bundled ARM64 NEON and runtime-detected x86-64 AVX2/SSSE3 kernels for strict ByteString and ShortByteString encoding, checked decoding, and typed decoding, including the lazy and Text APIs built on those operations. ShortByteString and ShortText use direct byte-array bindings without pinning or copying through strict ByteString. Their parsers validate through the short decoder. Unsupported targets and inputs below the SIMD thresholds fall back to Haskell scalar loops. Lenient decoding remains scalar.

The flag is off by default. Enable it in your cabal.project:

package base16
  flags: +simd

No external native library is needed. SIMD uses the same alphabet checks and error offsets as the scalar implementation.

Development

GHC 9.6 or later is required. Test both build configurations:

cabal test --flags=-simd
cabal test --flags=simd
cabal bench --flags=-simd --benchmark-options='--iters 1'
cabal bench --flags=simd --benchmark-options='--iters 1'

The benchmark suite covers small inputs, vector/call boundaries, and buffers up to one million bytes. For timings, omit --iters 1 and use separate build directories (--builddir=dist-scalar and --builddir=dist-simd). Compare on the same machine and compiler; gains depend on input size and CPU.

Native differential tests check exact buffer bounds, alignment, invalid bytes, and scalar fallback. CI runs them with address/undefined-behavior sanitizers and guard pages, and builds the Haskell tests, benchmarks, and Haddock from the source distribution with both flag settings.