    {-# LANGUAGE CPP                 #-}
    {-# LANGUAGE MagicHash           #-}
    {-# LANGUAGE RankNTypes          #-}
    {-# LANGUAGE TypeFamilies        #-}
    {-# LANGUAGE ScopedTypeVariables #-}

    -- |
    -- Module      : Codec.CBOR.ByteArray
    -- Copyright   : (c) Ben Gamari 2017-2018
    -- License     : BSD3-style (see LICENSE.txt)
    --
    -- Maintainer  : duncan@community.haskell.org
    -- Stability   : experimental
    -- Portability : non-portable (GHC extensions)
    --
    -- A ByteArray with more instances than 'Data.Primitive.ByteArray.ByteArray'.
    -- Some day when these instances are reliably available from @primitive@ we can
    -- likely replace this with 'Data.Primitive.ByteArray.ByteArray'.
    --
    module Codec.CBOR.ByteArray
      ( -- * Simple byte arrays
        ByteArray(..)
      , sizeofByteArray
        -- * Conversions
      , fromShortByteString
      , toShortByteString
      , fromByteString
      , toBuilder
      , toSliced
      ) where

    import Data.Char (ord)
    import Data.Word
    import GHC.Exts (IsList(..), IsString(..))

    import qualified Data.Primitive.ByteArray as Prim
    import qualified Data.ByteString as BS
    import qualified Data.ByteString.Short as BSS
    import qualified Data.ByteString.Short.Internal as BSS
    import qualified Data.ByteString.Builder as BSB

    import qualified Codec.CBOR.ByteArray.Sliced as Sliced
    import           Codec.CBOR.ByteArray.Internal

    newtype ByteArray = BA {unBA :: Prim.ByteArray}

    sizeofByteArray :: ByteArray -> Int
    {-# INLINE sizeofByteArray #-}
    sizeofByteArray (BA ba) = Prim.sizeofByteArray ba

    fromShortByteString :: BSS.ShortByteString -> ByteArray
    fromShortByteString (BSS.SBS ba) = BA (Prim.ByteArray ba)

    toShortByteString :: ByteArray -> BSS.ShortByteString
    toShortByteString (BA (Prim.ByteArray ba)) = BSS.SBS ba

    fromByteString :: BS.ByteString -> ByteArray
    fromByteString = fromShortByteString . BSS.toShort

    toBuilder :: ByteArray -> BSB.Builder
    toBuilder = Sliced.toBuilder . toSliced

    toSliced :: ByteArray -> Sliced.SlicedByteArray
    toSliced ba@(BA arr) = Sliced.SBA arr 0 (sizeofByteArray ba)

    instance Show ByteArray where
      showsPrec _ = shows . toSliced

    instance Eq ByteArray where
      ba1 == ba2 = toSliced ba1 == toSliced ba2

    instance Ord ByteArray where
      ba1 `compare` ba2 = toSliced ba1 `compare` toSliced ba2

    instance IsString ByteArray where
      fromString = fromList . map checkedOrd
        where
          checkedOrd c
            | c > '\xff' = error "IsString(Codec.CBOR.ByteArray): Non-ASCII character"
            | otherwise  = fromIntegral $ ord c

    instance IsList ByteArray where
      type Item ByteArray = Word8
      fromList xs = fromListN (Prelude.length xs) xs
      fromListN n xs =
          let arr = mkByteArray n xs
          in BA arr
      toList ba@(BA arr) =
          foldrByteArray (:) [] 0 (sizeofByteArray ba) arr