BSD-3-Clause licensed by Emily Pillmore
Maintained by Emily Pillmore
This version can be pinned in stack with:base16-2.0@sha256:3cfa8002b7fd079be0552a116f27073e895b62a56d7a1cb7dd4f7a5ea3c8f6ae,3089

Module documentation for 2.0

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.

Changes

Revision history for base16

2.0

This major release removes two public functions. See MIGRATION-2.0.md for migration examples.

  • Replace isBase16 and isValidBase16 with parseBase16 in all six ByteString and Text modules. Parsing checks even length and the mixed-case hexadecimal alphabet, then preserves the original encoded representation. For a Boolean check a la isBase16, just use isRight . For parseBase16, odd-length alphabet-only inputs previously accepted by isValidBase16 are now rejected.
  • Add an optional manual simd flag, which is off by default, using the bundled cbits which give runtime-detected x86-64 AVX2/SSSE3 encoding and decoding. Unsupported targets and inputs under the simd threshold use the scalar fallbacks; lenient decoding remains scalar.
  • Accelerate ShortByteString/ShortText encoding and checked/typed decoding with direct byte-array SIMD bindings, without pinning or copying through strict ByteString. Short parsers now validate through the short decoder.
  • Preserve scalar decoding errors and input-relative offsets with SIMD.
  • Fix lazy typed decoding when hex pairs cross chunk boundaries. Lazy checked decoding now materializes the complete input for consistent total-length checks and global error offsets. Lazy parsing retains the original chunks.
  • Fix #32: lenient Text decoding no longer throws on malformed UTF-8. Valid UTF-8 is preserved; invalid bytes fall back to their Latin-1 characters. Apply the same policy to lazy Text and ShortText, avoiding invalid UTF-8 inside the short-text result.
  • Require GHC 9.6 or later, support GHC 9.6 through 9.14, refresh CI, and retire the old i386 32 bit support.
  • Guard encoded output size calculations against integer overflow.

1.0

  • Revamp api (see: MIGRATION-1.0.md in the source repo)

    • Add new Base16 opaque wrapper for encoded strings
    • Move decode* to decode*Typed for untyped bytestring values
    • Make typed decoding the default
    • Optimize loops in the typed case
  • Bump lower bound on bytestring to bytestring-0.11

  • Bump lower bound on text to text-2.0

  • Support for all recent GHCs

  • Removed support for SafeHaskell

0.3.2.1

  • Add support for GHC 9.4
  • Added decodeBase16' to Data.ByteString.Base16* (thanks to @shlevy #20)

0.3.2.0

  • Fix incorrect behavior in Data.ByteString.Short.Base16#encodeBase16
  • Drop base GHC <8.10

0.3.0.1

  • Allow for mixed-case lenient decoding + validation
  • Bump testing for mixed-case hex

0.3.0.0

  • API for decodeBase16With has changed to require ByteString instead of Text. This is in alignment with work done on base64, which reflects the same API. This will be the final design for these types of conversions.
  • Test coverage now at 94%
  • Add NFData, Exception, and Generic instances for Base16Error + @since annotations for new instances. (#5)
  • Doc improvements and add -XTrustworty and -XSafe annotations where needed. (#5)
  • Optimized inner loop for short text and bytestrings (#4)
  • Changed encodeBase16 in ByteString.Short to produce ShortText, instead of Text.

0.2.1

  • Added support for Text.Short and ByteString.Short values

0.2.0.1

  • Improved performance. Decode and encode are now 3.5x-5x the next best lib.

0.2.0

  • Add lenient decoders
  • Fix bug in Text decodeBase16 which failed on invalid UTF-8 values as a result of decoding
  • Add decodeBase16With combinators

0.1.3

  • Add lazy variants for Text and ByteString values

0.1.2.1 – 2020-02-17

  • Documentation now references correct RFC section

0.1.2 – 2020-02-17

  • Unmask loops - now correct.

0.1.1 – 2020-02-17

  • Mask Word32 and Word64 loops (flaky)

0.1.0.0 – 2020-02-16

  • First version. Released on an unsuspecting world.