packages feed

battleships-1.0.0: Logic/Binary.hs

{-# LANGUAGE CPP #-}
----------------------------------------------------------------------------
-- |
-- Module      :  Logic.Binary
-- Stability   :  experimental
-- Portability :  portable
--
-- Import/export for binary serializable data to text URLs.

module Logic.Binary
  ( -- * Import/Export
    impBinary
  , expBinary
    -- * Misc
  , fromStrict
  , toStrict
    -- * General Serialization Helpers
  , getEnum8
  , getIntegral8
  , getList8
  , putEnum8
  , putIntegral8
  , putList8
  ) where

import           Prelude
import           Control.Applicative
import           Control.Monad
import           Data.Serialize.Get
import           Data.Serialize.Put
import           Data.Text (Text)
import qualified Data.Text.Encoding          as TE
import qualified Data.ByteString             as BS
import qualified Data.ByteString.Lazy        as BL
import qualified Data.ByteString.Base64.Lazy as B64

--------------------------------------------------------------------------------
-- * Import/Export
--------------------------------------------------------------------------------

-- | Imports a byte string from text.
impBinary :: Text -> Maybe BL.ByteString
impBinary
  = eitherToMaybe
  . B64.decode
  . fromStrict
  . fromBase64Url
  . TE.encodeUtf8

-- | Exports a byte string to text.
expBinary :: BL.ByteString -> Text
expBinary
  = TE.decodeUtf8
  . toBase64Url
  . toStrict
  . B64.encode

--------------------------------------------------------------------------------
-- * URL
--
-- Some characters used in base 64 encoding are problematic when used in URLs.
-- These functions convert from and into a representation in which these
-- characters are replaced with non problematic ones, using "base64url" 
-- according to RFC 4648 (http://tools.ietf.org/html/rfc4648).
--------------------------------------------------------------------------------

toBase64Url :: BS.ByteString -> BS.ByteString
toBase64Url = BS.map convert where
  convert 43 = 45 -- '+' --> '-'
  convert 47 = 95 -- '/' --> '_'
  convert  x =  x

fromBase64Url :: BS.ByteString -> BS.ByteString
fromBase64Url = BS.map convert where
  convert 45 = 43 -- '+' <-- '-'
  convert 95 = 47 -- '/' <-- '_'
  convert  x =  x

--------------------------------------------------------------------------------
-- * Misc
--------------------------------------------------------------------------------

eitherToMaybe :: Either a b -> Maybe b
eitherToMaybe e = case e of
  Right x -> Just x
  _       -> Nothing

#if MIN_VERSION_bytestring(0,10,0)
toStrict :: BL.ByteString -> BS.ByteString
toStrict = BL.toStrict

fromStrict :: BS.ByteString -> BL.ByteString
fromStrict = BL.fromStrict
#else
toStrict :: BL.ByteString -> BS.ByteString
toStrict = BS.concat . BL.toChunks

fromStrict :: BS.ByteString -> BL.ByteString
fromStrict = BL.fromChunks . (:[])
#endif

-------------------------------------------------------------------------------
-- * General Serialization Helpers
--
-- These functions serialize Enums with up to 255 values, integrals in the
-- range from 0 to 255 and lists with at most 255 items. These constraints
-- are not enforced, so usage of these functions may be unsafe.
-------------------------------------------------------------------------------

putEnum8 :: Enum a => Putter a
putEnum8 = putWord8 . fromIntegral . fromEnum

getEnum8 :: Enum a => Get a
getEnum8 = toEnum . fromIntegral <$> getWord8

putIntegral8 :: Integral a => Putter a
putIntegral8 = putWord8 . fromIntegral

getIntegral8 :: Integral a => Get a
getIntegral8 = fromIntegral <$> getWord8

putList8 :: Putter a -> Putter [a]
putList8 pel xs = do
  putIntegral8 $ length xs
  forM_ xs pel

getList8 :: Get a -> Get [a]
getList8 gel = getIntegral8 >>= getList where
  getList len = replicateM len gel