{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE UnboxedTuples #-}
module Data.ByteString.Base16.Internal.Utils
( aix
, reChunk
, runShortST
, runDecodeST
) where


import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.ByteString.Short.Internal
import Data.Primitive.ByteArray
import Data.Text (Text)

import GHC.Exts
import GHC.Word
import GHC.ST (ST(..))


-- | Read 'Word8' index off alphabet addr
--
aix :: Word8 -> Addr# -> Word8
aix (W8# i) alpha = W8# (indexWord8OffAddr# alpha (word2Int# i))
{-# INLINE aix #-}

-- | Form a list of chunks, and rechunk the list of bytestrings
-- into length multiples of 2
--
reChunk :: [ByteString] -> [ByteString]
reChunk [] = []
reChunk (c:cs) = case B.length c `divMod` 2 of
    (_, 0) -> c : reChunk cs
    (n, _) -> case B.splitAt (n * 2) c of
      ~(m, q) -> m : cont_ q cs
  where
    cont_ q [] = [q]
    cont_ q (a:as) = case B.splitAt 1 a of
      ~(x, y) -> let q' = B.append q x
        in if B.length q' == 2
          then
            let as' = if B.null y then as else y:as
            in q' : reChunk as'
          else cont_ q' as

-- | Write a byte array directly to Short bytestring
--
runShortST :: (forall s. ST s ByteArray) -> ShortByteString
runShortST enc = runRW# $ \s0 -> case enc of
  { ST g -> case g s0 of
    { (# _, ByteArray r #) -> SBS r
    }
  }
{-# INLINE runShortST #-}

-- | Used for writing 'ByteArray#'-based encodes
--
runDecodeST
    :: (forall s. ST s (Either Text ByteArray))
    -> Either Text ShortByteString
runDecodeST dec = runRW# $ \s0 -> case dec of
  { ST g -> case g s0 of
    { (# _, e #) -> case e of
      Left t -> Left t
      Right (ByteArray r) -> Right (SBS r)
    }
  }
{-# INLINE runDecodeST #-}