{-# OPTIONS_HADDOCK prune #-}
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module: Data.ByteString.Base16
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Pure base16 encoding and decoding of strict bytestrings.

module Data.ByteString.Base16 (
    encode
  , decode
  ) where

import qualified Data.Bits as B
import Data.Bits ((.&.), (.|.))
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Builder.Extra as BE
import qualified Data.ByteString.Internal as BI
import qualified Data.ByteString.Unsafe as BU
import Data.Word (Word8, Word16)

to_strict :: BSB.Builder -> BS.ByteString
to_strict :: Builder -> ByteString
to_strict = ByteString -> ByteString
BS.toStrict (ByteString -> ByteString)
-> (Builder -> ByteString) -> Builder -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> ByteString
BSB.toLazyByteString
{-# INLINE to_strict #-}

to_strict_small :: BSB.Builder -> BS.ByteString
to_strict_small :: Builder -> ByteString
to_strict_small = ByteString -> ByteString
BS.toStrict
  (ByteString -> ByteString)
-> (Builder -> ByteString) -> Builder -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AllocationStrategy -> ByteString -> Builder -> ByteString
BE.toLazyByteStringWith (Int -> Int -> AllocationStrategy
BE.safeStrategy Int
128 Int
BE.smallChunkSize) ByteString
forall a. Monoid a => a
mempty
{-# INLINE to_strict_small #-}

fi :: (Num a, Integral b) => b -> a
fi :: forall a b. (Num a, Integral b) => b -> a
fi = b -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral
{-# INLINE fi #-}

hex_charset :: BS.ByteString
hex_charset :: ByteString
hex_charset = ByteString
"0123456789abcdef"

expand_w8 :: Word8 -> Word16
expand_w8 :: Word8 -> Word16
expand_w8 Word8
b =
  let !hi :: Word8
hi = ByteString -> Int -> Word8
BU.unsafeIndex ByteString
hex_charset (Word8 -> Int
forall a b. (Num a, Integral b) => b -> a
fi Word8
b Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`B.shiftR` Int
4)
      !lo :: Word8
lo = ByteString -> Int -> Word8
BU.unsafeIndex ByteString
hex_charset (Word8 -> Int
forall a b. (Num a, Integral b) => b -> a
fi Word8
b Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
0b00001111)
  in      Word8 -> Word16
forall a b. (Num a, Integral b) => b -> a
fi Word8
hi Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
8
      Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Word8 -> Word16
forall a b. (Num a, Integral b) => b -> a
fi Word8
lo
{-# INLINE expand_w8 #-}

-- | Encode a base256 'ByteString' as base16.
--
--   >>> encode "hello world"
--   "68656c6c6f20776f726c64"
encode :: BS.ByteString -> BS.ByteString
encode :: ByteString -> ByteString
encode bs :: ByteString
bs@(BI.PS ForeignPtr Word8
_ Int
_ Int
l)
    | Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
64    = Builder -> ByteString
to_strict_small Builder
loop
    | Bool
otherwise = Builder -> ByteString
to_strict Builder
loop
  where
    -- writing as few words as possible requires performing some length
    -- checks up front
    loop :: Builder
loop
      | Int
l Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
4 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 =
               ByteString -> Builder
go64 ByteString
bs
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
4 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3) ByteString
bs of
          (ByteString
chunk, ByteString
etc) ->
               ByteString -> Builder
go64 ByteString
chunk
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
go32 (Int -> ByteString -> ByteString
BU.unsafeTake Int
2 ByteString
etc)
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
go16 (Int -> ByteString -> ByteString
BU.unsafeDrop Int
2 ByteString
etc)
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
4 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) ByteString
bs of
          (ByteString
chunk, ByteString
etc) ->
               ByteString -> Builder
go64 ByteString
chunk
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
go32 ByteString
etc
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
4 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ByteString
bs of
          (ByteString
chunk, ByteString
etc) ->
               ByteString -> Builder
go64 ByteString
chunk
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
go16 ByteString
etc

      | Int
l Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
2 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 =
               ByteString -> Builder
go32 ByteString
bs
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
2 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ByteString
bs of
          (ByteString
chunk, ByteString
etc) ->
               ByteString -> Builder
go32 ByteString
chunk
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
go16 ByteString
etc

      | Bool
otherwise =
               ByteString -> Builder
go16 ByteString
bs

    go64 :: ByteString -> Builder
go64 ByteString
b = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt Int
4 ByteString
b of
      (ByteString
chunk, ByteString
etc)
        | ByteString -> Bool
BS.null ByteString
chunk -> Builder
forall a. Monoid a => a
mempty
        | Bool
otherwise ->
            let !w16_0 :: Word16
w16_0 = Word8 -> Word16
expand_w8 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
0)
                !w16_1 :: Word16
w16_1 = Word8 -> Word16
expand_w8 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
1)
                !w16_2 :: Word16
w16_2 = Word8 -> Word16
expand_w8 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
2)
                !w16_3 :: Word16
w16_3 = Word8 -> Word16
expand_w8 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
3)

                !w64 :: Word64
w64 = Word16 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word16
w16_0 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
48
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word16 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word16
w16_1 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
32
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word16 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word16
w16_2 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
16
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word16 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word16
w16_3

            in  Word64 -> Builder
BSB.word64BE Word64
w64 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
go64 ByteString
etc

    go32 :: ByteString -> Builder
go32 ByteString
b = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt Int
2 ByteString
b of
      (ByteString
chunk, ByteString
etc)
        | ByteString -> Bool
BS.null ByteString
chunk -> Builder
forall a. Monoid a => a
mempty
        | Bool
otherwise ->
            let !w16_0 :: Word16
w16_0 = Word8 -> Word16
expand_w8 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
0)
                !w16_1 :: Word16
w16_1 = Word8 -> Word16
expand_w8 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
1)

                !w32 :: Word32
w32 = Word16 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word16
w16_0 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
16
                   Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word16 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word16
w16_1

            in  Word32 -> Builder
BSB.word32BE Word32
w32 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
go32 ByteString
etc

    go16 :: ByteString -> Builder
go16 ByteString
b = case ByteString -> Maybe (Word8, ByteString)
BS.uncons ByteString
b of
      Maybe (Word8, ByteString)
Nothing -> Builder
forall a. Monoid a => a
mempty
      Just (Word8
h, ByteString
t) ->
        let !w16 :: Word16
w16 = Word8 -> Word16
expand_w8 Word8
h
        in  Word16 -> Builder
BSB.word16BE Word16
w16 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
go16 ByteString
t

-- word8 hex character to word4
word4 :: Word8 -> Maybe Word8
word4 :: Word8 -> Maybe Word8
word4 Word8
c
  | Word8
c Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
> Word8
47 Bool -> Bool -> Bool
&& Word8
c Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
< Word8
58  = Word8 -> Maybe Word8
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word8 -> Maybe Word8) -> Word8 -> Maybe Word8
forall a b. (a -> b) -> a -> b
$! Word8
c Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
48 -- 0-9
  | Word8
c Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
> Word8
64 Bool -> Bool -> Bool
&& Word8
c Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
< Word8
71  = Word8 -> Maybe Word8
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word8 -> Maybe Word8) -> Word8 -> Maybe Word8
forall a b. (a -> b) -> a -> b
$! Word8
c Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
55 -- A-F
  | Word8
c Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
> Word8
96 Bool -> Bool -> Bool
&& Word8
c Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
< Word8
103 = Word8 -> Maybe Word8
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Word8 -> Maybe Word8) -> Word8 -> Maybe Word8
forall a b. (a -> b) -> a -> b
$! Word8
c Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
- Word8
87 -- a-f
  | Bool
otherwise         = Maybe Word8
forall a. Maybe a
Nothing
{-# INLINE word4 #-}

-- | Decode a base16 'ByteString' to base256.
--
--   Invalid inputs (including odd-length inputs) will produce
--   'Nothing'.
--
--   >>> decode "68656c6c6f20776f726c64"
--   Just "hello world"
--   >>> decode "068656c6c6f20776f726c64" -- odd-length
--   Nothing
decode :: BS.ByteString -> Maybe BS.ByteString
decode :: ByteString -> Maybe ByteString
decode bs :: ByteString
bs@(BI.PS ForeignPtr Word8
_ Int
_ Int
l)
    | Int -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
B.testBit Int
l Int
0    = Maybe ByteString
forall a. Maybe a
Nothing
    | Int
l Int -> Int -> Int
forall a. Integral a => a -> a -> a
`quot` Int
2 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
128 = (Builder -> ByteString) -> Maybe Builder -> Maybe ByteString
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Builder -> ByteString
to_strict_small Maybe Builder
loop
    | Bool
otherwise        = (Builder -> ByteString) -> Maybe Builder -> Maybe ByteString
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Builder -> ByteString
to_strict Maybe Builder
loop
  where
    -- same story, but we need more checks
    loop :: Maybe Builder
loop
      | Int
l Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 =
            Builder -> ByteString -> Maybe Builder
go64 Builder
forall a. Monoid a => a
mempty ByteString
bs
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go64 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder -> ByteString -> Maybe Builder
go8 Builder
b0 ByteString
etc
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
4) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
4) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go64 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder -> ByteString -> Maybe Builder
go16 Builder
b0 ByteString
etc
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
6) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
6) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go64 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder
b1 <- Builder -> ByteString -> Maybe Builder
go16 Builder
b0 (Int -> ByteString -> ByteString
BU.unsafeTake Int
4 ByteString
etc)
            Builder -> ByteString -> Maybe Builder
go8 Builder
b1 (Int -> ByteString -> ByteString
BU.unsafeDrop Int
4 ByteString
etc)
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
8) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
8) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go64 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder -> ByteString -> Maybe Builder
go32 Builder
b0 ByteString
etc
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
10) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
10) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go64 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder
b1 <- Builder -> ByteString -> Maybe Builder
go32 Builder
b0 (Int -> ByteString -> ByteString
BU.unsafeTake Int
8 ByteString
etc)
            Builder -> ByteString -> Maybe Builder
go8 Builder
b1 (Int -> ByteString -> ByteString
BU.unsafeDrop Int
8 ByteString
etc)
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
12) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
12) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go64 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder
b1 <- Builder -> ByteString -> Maybe Builder
go32 Builder
b0 (Int -> ByteString -> ByteString
BU.unsafeTake Int
8 ByteString
etc)
            Builder -> ByteString -> Maybe Builder
go16 Builder
b1 (Int -> ByteString -> ByteString
BU.unsafeDrop Int
8 ByteString
etc)
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
14) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
16 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
14) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go64 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder
b1 <- Builder -> ByteString -> Maybe Builder
go32 Builder
b0 (Int -> ByteString -> ByteString
BU.unsafeTake Int
8 ByteString
etc)
            Builder
b2 <- Builder -> ByteString -> Maybe Builder
go16 Builder
b1 (Int -> ByteString -> ByteString
BU.unsafeTake Int
4 (Int -> ByteString -> ByteString
BU.unsafeDrop Int
8 ByteString
etc))
            Builder -> ByteString -> Maybe Builder
go8 Builder
b2 (Int -> ByteString -> ByteString
BU.unsafeDrop Int
12 ByteString
etc)

      | Int
l Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
8 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 =
            Builder -> ByteString -> Maybe Builder
go32 Builder
forall a. Monoid a => a
mempty ByteString
bs
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
8 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go32 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder -> ByteString -> Maybe Builder
go8 Builder
b0 ByteString
etc
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
4) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
8 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
4) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go32 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder -> ByteString -> Maybe Builder
go16 Builder
b0 ByteString
etc
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
6) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
8 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
6) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go32 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder
b1 <- Builder -> ByteString -> Maybe Builder
go16 Builder
b0 (Int -> ByteString -> ByteString
BU.unsafeTake Int
4 ByteString
etc)
            Builder -> ByteString -> Maybe Builder
go8 Builder
b1  (Int -> ByteString -> ByteString
BU.unsafeDrop Int
4 ByteString
etc)

      | Int
l Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
4 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 =
            Builder -> ByteString -> Maybe Builder
go16 Builder
forall a. Monoid a => a
mempty ByteString
bs
      | (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
4 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) ByteString
bs of
          (ByteString
chunk, ByteString
etc) -> do
            Builder
b0 <- Builder -> ByteString -> Maybe Builder
go16 Builder
forall a. Monoid a => a
mempty ByteString
chunk
            Builder -> ByteString -> Maybe Builder
go8 Builder
b0 ByteString
etc

      | Bool
otherwise =
            Builder -> ByteString -> Maybe Builder
go8 Builder
forall a. Monoid a => a
mempty ByteString
bs

    go64 :: Builder -> ByteString -> Maybe Builder
go64 Builder
acc ByteString
b = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt Int
16 ByteString
b of
      (ByteString
chunk, ByteString
etc)
        | ByteString -> Bool
BS.null ByteString
chunk -> Builder -> Maybe Builder
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Builder
acc
        | Bool
otherwise -> do
            !Word8
w4_00 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
00)
            !Word8
w4_01 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
01)
            !Word8
w4_02 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
02)
            !Word8
w4_03 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
03)
            !Word8
w4_04 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
04)
            !Word8
w4_05 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
05)
            !Word8
w4_06 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
06)
            !Word8
w4_07 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
07)
            !Word8
w4_08 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
08)
            !Word8
w4_09 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
09)
            !Word8
w4_10 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
10)
            !Word8
w4_11 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
11)
            !Word8
w4_12 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
12)
            !Word8
w4_13 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
13)
            !Word8
w4_14 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
14)
            !Word8
w4_15 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
15)

            let !w64 :: Word64
w64 = Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_00 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
60
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_01 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
56
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_02 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
52
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_03 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
48
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_04 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
44
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_05 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
40
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_06 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
36
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_07 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
32
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_08 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
28
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_09 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
24
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_10 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
20
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_11 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
16
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_12 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
12
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_13 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
08
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_14 Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
04
                   Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.|. Word8 -> Word64
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_15

            Builder -> ByteString -> Maybe Builder
go64 (Builder
acc Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word64 -> Builder
BSB.word64BE Word64
w64) ByteString
etc

    go32 :: Builder -> ByteString -> Maybe Builder
go32 Builder
acc ByteString
b = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt Int
8 ByteString
b of
      (ByteString
chunk, ByteString
etc)
        | ByteString -> Bool
BS.null ByteString
chunk -> Builder -> Maybe Builder
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Builder
acc
        | Bool
otherwise -> do
            !Word8
w4_00 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
00)
            !Word8
w4_01 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
01)
            !Word8
w4_02 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
02)
            !Word8
w4_03 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
03)
            !Word8
w4_04 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
04)
            !Word8
w4_05 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
05)
            !Word8
w4_06 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
06)
            !Word8
w4_07 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
07)

            let !w32 :: Word32
w32 = Word8 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_00 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
28
                   Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_01 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
24
                   Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_02 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
20
                   Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_03 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
16
                   Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_04 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
12
                   Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_05 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
08
                   Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_06 Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
04
                   Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_07

            Builder -> ByteString -> Maybe Builder
go32 (Builder
acc Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word32 -> Builder
BSB.word32BE Word32
w32) ByteString
etc

    go16 :: Builder -> ByteString -> Maybe Builder
go16 Builder
acc ByteString
b = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt Int
4 ByteString
b of
      (ByteString
chunk, ByteString
etc)
        | ByteString -> Bool
BS.null ByteString
chunk -> Builder -> Maybe Builder
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Builder
acc
        | Bool
otherwise -> do
            !Word8
w4_00 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
00)
            !Word8
w4_01 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
01)
            !Word8
w4_02 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
02)
            !Word8
w4_03 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
03)

            let !w16 :: Word16
w16 = Word8 -> Word16
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_00 Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
12
                   Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Word8 -> Word16
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_01 Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
08
                   Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Word8 -> Word16
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_02 Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
04
                   Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Word8 -> Word16
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_03

            Builder -> ByteString -> Maybe Builder
go16 (Builder
acc Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word16 -> Builder
BSB.word16BE Word16
w16) ByteString
etc

    go8 :: Builder -> ByteString -> Maybe Builder
go8 Builder
acc ByteString
b  = case Int -> ByteString -> (ByteString, ByteString)
BS.splitAt Int
2 ByteString
b of
      (ByteString
chunk, ByteString
etc)
        | ByteString -> Bool
BS.null ByteString
chunk -> Builder -> Maybe Builder
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Builder
acc
        | Bool
otherwise -> do
            !Word8
w4_00 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
00)
            !Word8
w4_01 <- Word8 -> Maybe Word8
word4 (ByteString -> Int -> Word8
BU.unsafeIndex ByteString
chunk Int
01)

            let !w8 :: Word8
w8 = Word8 -> Word8
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_00 Word8 -> Int -> Word8
forall a. Bits a => a -> Int -> a
`B.shiftL` Int
04
                  Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.|. Word8 -> Word8
forall a b. (Num a, Integral b) => b -> a
fi Word8
w4_01

            Builder -> ByteString -> Maybe Builder
go8 (Builder
acc Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
w8) ByteString
etc