{-# Language CPP, OverloadedStrings, GeneralizedNewtypeDeriving #-} module Haste.Binary.Get ( Get, getWord8, getWord16le, getWord32le, getInt8, getInt16le, getInt32le, getFloat32le, getFloat64le, getBytes, getJSString, skip, runGet ) where import Data.Int import Data.Word import Haste.Prim import Haste.Binary.Types #if __GLASGOW_HASKELL__ < 710 import Control.Applicative #endif import Control.Monad import System.IO.Unsafe #ifdef __HASTE__ import Haste.Foreign hiding (get) #else import qualified Control.Exception as Ex import Data.Char (chr) import qualified Data.Binary as B import qualified Data.Binary.IEEE754 as BI import qualified Data.Binary.Get as BG #endif #ifdef __HASTE__ data Result a = Ok !Int !a | Fail !String data Get a = Get {unG :: JSAny -> Int -> Result a} instance Functor Get where fmap f (Get m) = Get $ \buf next -> case m buf next of Ok next' x -> Ok next' (f x) Fail s -> Fail s instance Applicative Get where (<*>) = ap pure = return instance Monad Get where return x = Get $ \_ next -> Ok next x (Get m) >>= f = Get $ \buf next -> case m buf next of Ok next' x -> unG (f x) buf next' Fail e -> Fail e fail s = Get $ \_ _ -> Fail s getW8 :: JSAny -> Int -> IO Word8 getW8 = ffi "(function(b,i){return b.getUint8(i);})" getWord8 :: Get Word8 getWord8 = Get $ \buf next -> Ok (next+1) (unsafePerformIO $ getW8 buf next) getW16le :: JSAny -> Int -> IO Word16 getW16le = ffi "(function(b,i){return b.getUint16(i,true);})" getWord16le :: Get Word16 getWord16le = Get $ \buf next -> Ok (next+2) (unsafePerformIO $ getW16le buf next) getW32le :: JSAny -> Int -> IO Word32 getW32le = ffi "(function(b,i){return b.getUint32(i,true);})" getWord32le :: Get Word32 getWord32le = Get $ \buf next -> Ok (next+4) (unsafePerformIO $ getW32le buf next) getI8 :: JSAny -> Int -> IO Int8 getI8 = ffi "(function(b,i){return b.getInt8(i);})" getInt8 :: Get Int8 getInt8 = Get $ \buf next -> Ok (next+1) (unsafePerformIO $ getI8 buf next) getI16le :: JSAny -> Int -> IO Int16 getI16le = ffi "(function(b,i){return b.getInt16(i,true);})" getInt16le :: Get Int16 getInt16le = Get $ \buf next -> Ok (next+2) (unsafePerformIO $ getI16le buf next) getI32le :: JSAny -> Int -> IO Int32 getI32le = ffi "(function(b,i){return b.getInt32(i,true);})" getInt32le :: Get Int32 getInt32le = Get $ \buf next -> Ok (next+4) (unsafePerformIO $ getI32le buf next) getF32le :: JSAny -> Int -> IO Float getF32le = ffi "(function(b,i){return b.getFloat32(i,true);})" getFloat32le :: Get Float getFloat32le = Get $ \buf next -> Ok (next+4) (unsafePerformIO $ getF32le buf next) getF64le :: JSAny -> Int -> IO Double getF64le = ffi "(function(b,i){return b.getFloat64(i,true);})" getFloat64le :: Get Double getFloat64le = Get $ \buf next -> Ok (next+8) (unsafePerformIO $ getF64le buf next) getBytes :: Int -> Get BlobData getBytes len = Get $ \buf next -> Ok (next+len) (BlobData next len buf) -- | Read a 'JSString' of @n@ characters. Encoding is assumed to be UTF-16. getJSString :: Word32 -> Get JSString getJSString len = Get $ \buf next -> Ok (next+fromIntegral (len+len)) (unsafePerformIO $ getJSS buf next len) getJSS :: JSAny -> Int -> Word32 -> IO JSString getJSS = ffi "(function(b,off,len){return String.fromCharCode.apply(null,new Uint16Array(b.buffer,off,len));})" -- | Skip n bytes of input. skip :: Int -> Get () skip len = Get $ \_buf next -> Ok (next+len) () -- | Run a Get computation. runGet :: Get a -> BlobData -> Either String a runGet (Get p) (BlobData off len bd) = do case p bd off of Ok consumed x | consumed <= len -> Right x | otherwise -> Left "Not enough data!" Fail s -> Left s #else newtype Get a = Get (BG.Get a) deriving (Functor, Applicative, Monad) runGet :: Get a -> BlobData -> Either String a runGet (Get g) (BlobData bd) = unsafePerformIO $ do Ex.catch (Right <$> (return $! BG.runGet g bd)) mEx mEx :: Ex.SomeException -> IO (Either String a) mEx ex = return . Left $ show ex getWord8 :: Get Word8 getWord8 = Get BG.getWord8 getWord16le :: Get Word16 getWord16le = Get BG.getWord16le getWord32le :: Get Word32 getWord32le = Get BG.getWord32le getInt8 :: Get Int8 getInt8 = Get B.get getInt16le :: Get Int16 getInt16le = fromIntegral <$> getWord16le getInt32le :: Get Int32 getInt32le = fromIntegral <$> getWord32le getFloat32le :: Get Float getFloat32le = Get BI.getFloat32le getFloat64le :: Get Double getFloat64le = Get BI.getFloat64le getBytes :: Int -> Get BlobData getBytes len = Get $ do bs <- BG.getLazyByteString (fromIntegral len) return (BlobData bs) getJSString :: Int -> Get JSString getJSString len = Get $ do toJSStr `fmap` forM [1..len] (\_ -> fmap (chr . fromIntegral) BG.getWord16le) -- | Skip n bytes of input. skip :: Int -> Get () skip = Get . BG.skip #endif