-- | Flat instances for the bytestring library
{-# LANGUAGE NoMonomorphismRestriction #-}
module Flat.Instances.ByteString
  ()
where

import           Flat.Class
import           Flat.Decoder
import           Flat.Encoder
import qualified Data.ByteString               as B
import qualified Data.ByteString.Lazy          as L
import qualified Data.ByteString.Short         as SBS

-- $setup
-- >>> import Flat.Instances.Test
-- >>> import Flat.Instances.Base
-- >>> import qualified Data.ByteString               as B
-- >>> import qualified Data.ByteString.Lazy          as L
-- >>> import qualified Data.ByteString.Short         as SBS

{-|
ByteString, ByteString.Lazy and ByteString.Short are all encoded as Prealigned Arrays:

@
PreAligned a ≡ PreAligned {preFiller :: Filler, preValue :: a}

Filler ≡   FillerBit Filler
          | FillerEnd

Array v = A0
        | A1 v (Array v)
        | A2 v v (Array v)
        ...
        | A255 ... (Array v)
@

That's to say as a byte-aligned sequence of blocks of up to 255 elements, with every block preceded by the count of the elements in the block and a final 0-length block.

>>>  tst (B.pack [11,22,33])
(True,48,[1,3,11,22,33,0])

where:

1= PreAlignment (takes a byte if we are already on a byte boundary)

3= Number of bytes in ByteString

11,22,33= Bytes

0= End of Array

>>>  tst (B.pack [])
(True,16,[1,0])

Pre-alignment ensures that a ByteString always starts at a byte boundary:

>>>  tst ((False,True,False,B.pack [11,22,33]))
(True,51,[65,3,11,22,33,0])

All ByteStrings are encoded in the same way:

>>> all (tst (B.pack [55]) ==) [tst (L.pack [55]),tst (SBS.pack [55])]
True
-}
instance Flat B.ByteString where
  encode :: ByteString -> Encoding
encode = ByteString -> Encoding
eBytes
  size :: ByteString -> NumBits -> NumBits
size   = ByteString -> NumBits -> NumBits
sBytes
  decode :: Get ByteString
decode = Get ByteString
dByteString

{- |
>>>  tst ((False,True,False,L.pack [11,22,33]))
(True,51,[65,3,11,22,33,0])
-}
instance Flat L.ByteString where
  encode :: ByteString -> Encoding
encode = ByteString -> Encoding
eLazyBytes
  size :: ByteString -> NumBits -> NumBits
size   = ByteString -> NumBits -> NumBits
sLazyBytes
  decode :: Get ByteString
decode = Get ByteString
dLazyByteString

{- |
>>>  tst ((False,True,False,SBS.pack [11,22,33]))
(True,51,[65,3,11,22,33,0])
-}
instance Flat SBS.ShortByteString where
  encode :: ShortByteString -> Encoding
encode = ShortByteString -> Encoding
eShortBytes
  size :: ShortByteString -> NumBits -> NumBits
size   = ShortByteString -> NumBits -> NumBits
sShortBytes
  decode :: Get ShortByteString
decode = Get ShortByteString
dShortByteString