{-# 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 {ByteArray -> ByteArray
unBA :: Prim.ByteArray}

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

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

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

fromByteString :: BS.ByteString -> ByteArray
fromByteString :: ByteString -> ByteArray
fromByteString = ShortByteString -> ByteArray
fromShortByteString forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ShortByteString
BSS.toShort

toBuilder :: ByteArray -> BSB.Builder
toBuilder :: ByteArray -> Builder
toBuilder = SlicedByteArray -> Builder
Sliced.toBuilder forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteArray -> SlicedByteArray
toSliced

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

instance Show ByteArray where
  showsPrec :: Int -> ByteArray -> ShowS
showsPrec Int
_ = forall a. Show a => a -> ShowS
shows forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteArray -> SlicedByteArray
toSliced

instance Eq ByteArray where
  ByteArray
ba1 == :: ByteArray -> ByteArray -> Bool
== ByteArray
ba2 = ByteArray -> SlicedByteArray
toSliced ByteArray
ba1 forall a. Eq a => a -> a -> Bool
== ByteArray -> SlicedByteArray
toSliced ByteArray
ba2

instance Ord ByteArray where
  ByteArray
ba1 compare :: ByteArray -> ByteArray -> Ordering
`compare` ByteArray
ba2 = ByteArray -> SlicedByteArray
toSliced ByteArray
ba1 forall a. Ord a => a -> a -> Ordering
`compare` ByteArray -> SlicedByteArray
toSliced ByteArray
ba2

instance IsString ByteArray where
  fromString :: String -> ByteArray
fromString = forall l. IsList l => [Item l] -> l
fromList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall {a}. Num a => Char -> a
checkedOrd
    where
      checkedOrd :: Char -> a
checkedOrd Char
c
        | Char
c forall a. Ord a => a -> a -> Bool
> Char
'\xff' = forall a. HasCallStack => String -> a
error String
"IsString(Codec.CBOR.ByteArray): Non-ASCII character"
        | Bool
otherwise  = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
c

instance IsList ByteArray where
  type Item ByteArray = Word8
  fromList :: [Item ByteArray] -> ByteArray
fromList [Item ByteArray]
xs = forall l. IsList l => Int -> [Item l] -> l
fromListN (forall (t :: * -> *) a. Foldable t => t a -> Int
Prelude.length [Item ByteArray]
xs) [Item ByteArray]
xs
  fromListN :: Int -> [Item ByteArray] -> ByteArray
fromListN Int
n [Item ByteArray]
xs =
      let arr :: ByteArray
arr = Int -> [Word8] -> ByteArray
mkByteArray Int
n [Item ByteArray]
xs
      in ByteArray -> ByteArray
BA ByteArray
arr
  toList :: ByteArray -> [Item ByteArray]
toList ba :: ByteArray
ba@(BA ByteArray
arr) =
      forall a. (Word8 -> a -> a) -> a -> Int -> Int -> ByteArray -> a
foldrByteArray (:) [] Int
0 (ByteArray -> Int
sizeofByteArray ByteArray
ba) ByteArray
arr