-- | Common utility functions, used both in the client and the server.
module Parry.Util(
  -- * Hexadecimal encoders and decoders.
  encode16,decode16,encode16l,decode16l
  ) where

import qualified Data.ByteString.Lazy.Internal as ILB
import qualified Data.ByteString.Internal as IB
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as UB
import qualified Data.ByteString.Char8 as B8
import Foreign.Ptr
import Foreign.Storable
import Data.Bits
import Data.Word

-- | Encodes a strict bytestring to its hexadecimal representation. See 'decode16'.
encode16 :: B.ByteString -> B.ByteString
encode16 bytes=
  let { len=B.length bytes } in
  IB.unsafeCreate (len*2) $ \pp->
  let { fill p0 i
        | i>=len=return ()
        | otherwise=do {
          let { c=UB.unsafeIndex bytes i;
                c0=c`shiftR`4;
                c1=c.&.0xf };
          poke p0 $ if c0<=9 then 48+c0 else 97+c0-10;
          poke (p0`plusPtr`1) $ if c1<=9 then 48+c1 else 97+c1-10;
          fill (p0`plusPtr`2) $ i+1
          }}
  in
   fill pp 0

atomHex::Word8->Word8
atomHex c=
  if c<=(fromIntegral $ fromEnum '9') then
    c-(fromIntegral $ fromEnum '0')
  else
    c-(fromIntegral $ fromEnum 'a')+10

-- | Decodes a strict bytestring from its hexadecimal representation. See 'encode16'.
decode16::B8.ByteString->B8.ByteString
decode16 bytes=
  let { len=B.length bytes } in
  IB.unsafeCreate (len`quot`2) $ \pp->
  let { fill p0 i
        | i>=len=return ()
        | otherwise=do {
          let { c0=atomHex $ UB.unsafeIndex bytes i; c1=atomHex $ UB.unsafeIndex bytes (i+1) };
          poke p0 $ (c0`shiftL`4) .|. c1;
          fill (p0`plusPtr`1) $ i+2
          }}
  in
   fill pp 0

-- | Encodes a lazy bytestring to its hexadecimal representation.
encode16l::ILB.ByteString->ILB.ByteString
encode16l ILB.Empty=ILB.Empty
encode16l (ILB.Chunk x xs)=ILB.Chunk (encode16 x) (encode16l xs)

-- | Decodes a lazy bytestring from its hexadecimal representation.
decode16l::ILB.ByteString->ILB.ByteString
decode16l ILB.Empty=ILB.Empty
decode16l (ILB.Chunk x xs)=ILB.Chunk (decode16 x) (decode16l xs)