{-# 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 (ShortByteString -> ByteArray)
-> (ByteString -> ShortByteString) -> ByteString -> ByteArray
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 (SlicedByteArray -> Builder)
-> (ByteArray -> SlicedByteArray) -> ByteArray -> Builder
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
_ = SlicedByteArray -> ShowS
forall a. Show a => a -> ShowS
shows (SlicedByteArray -> ShowS)
-> (ByteArray -> SlicedByteArray) -> ByteArray -> 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 SlicedByteArray -> SlicedByteArray -> Bool
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 SlicedByteArray -> SlicedByteArray -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` ByteArray -> SlicedByteArray
toSliced ByteArray
ba2

instance IsString ByteArray where
  fromString :: String -> ByteArray
fromString = [Word8] -> ByteArray
forall l. IsList l => [Item l] -> l
fromList ([Word8] -> ByteArray)
-> (String -> [Word8]) -> String -> ByteArray
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Word8) -> String -> [Word8]
forall a b. (a -> b) -> [a] -> [b]
map Char -> Word8
forall p. Num p => Char -> p
checkedOrd
    where
      checkedOrd :: Char -> p
checkedOrd Char
c
        | Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
> Char
'\xff' = String -> p
forall a. HasCallStack => String -> a
error String
"IsString(Codec.CBOR.ByteArray): Non-ASCII character"
        | Bool
otherwise  = Int -> p
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> p) -> Int -> p
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 = Int -> [Item ByteArray] -> ByteArray
forall l. IsList l => Int -> [Item l] -> l
fromListN ([Word8] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
Prelude.length [Word8]
[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 [Word8]
[Item ByteArray]
xs
      in ByteArray -> ByteArray
BA ByteArray
arr
  toList :: ByteArray -> [Item ByteArray]
toList ba :: ByteArray
ba@(BA ByteArray
arr) =
      (Word8 -> [Word8] -> [Word8])
-> [Word8] -> Int -> Int -> ByteArray -> [Word8]
forall a. (Word8 -> a -> a) -> a -> Int -> Int -> ByteArray -> a
foldrByteArray (:) [] Int
0 (ByteArray -> Int
sizeofByteArray ByteArray
ba) ByteArray
arr