-- | Optimised decode function for OSC packets.
module Sound.OSC.Coding.Decode.Binary
    (get_packet
    ,decodeMessage
    ,decodeBundle
    ,decodePacket
    ,decodePacket_strict) where

import Control.Applicative {- base -}
import Control.Monad {- base -}
import Data.Int {- base -}
import Data.Word {- base -}

import qualified Data.Binary.Get as G {- binary -}
import qualified Data.Binary.IEEE754 as I {- data-binary-ieee754 -}
import qualified Data.ByteString.Char8 as S.C {- bytestring -}
import qualified Data.ByteString.Lazy as B {- bytestring -}
import qualified Data.ByteString.Lazy.Char8 as C {- bytestring -}

import qualified Sound.OSC.Coding.Byte as Byte {- hosc -}
import Sound.OSC.Coding.Convert {- hosc -}
import Sound.OSC.Datum {- hosc -}
import Sound.OSC.Packet {- hosc -}
import qualified Sound.OSC.Time as Time {- hosc -}

-- | Get a 32 bit integer in big-endian byte order.
getInt32be :: G.Get Int32
getInt32be :: Get Int32
getInt32be = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word32 -> Int32
word32_to_int32 Get Word32
G.getWord32be

-- | Get a 64 bit integer in big-endian byte order.
getInt64be :: G.Get Int64
getInt64be :: Get Int64
getInt64be = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word64 -> Int64
word64_to_int64 Get Word64
G.getWord64be

-- | Get an aligned OSC string.
get_string :: G.Get String
get_string :: Get String
get_string = do
    ByteString
s <- Get ByteString
G.getLazyByteStringNul
    Int -> Get ()
G.skip (Int64 -> Int
int64_to_int (forall i. (Num i, Bits i) => i -> i
Byte.align (ByteString -> Int64
B.length ByteString
s forall a. Num a => a -> a -> a
+ Int64
1)))
    forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> String
C.unpack ByteString
s)

-- | Get an aligned OSC string.
get_ascii :: G.Get ASCII
get_ascii :: Get ByteString
get_ascii = do
    ByteString
s <- Get ByteString
G.getLazyByteStringNul
    Int -> Get ()
G.skip (Int64 -> Int
int64_to_int (forall i. (Num i, Bits i) => i -> i
Byte.align (ByteString -> Int64
B.length ByteString
s forall a. Num a => a -> a -> a
+ Int64
1)))
    forall (m :: * -> *) a. Monad m => a -> m a
return (String -> ByteString
S.C.pack (ByteString -> String
C.unpack ByteString
s))

-- | Get binary data prefixed by byte count.
get_bytes :: Word32 -> G.Get B.ByteString
get_bytes :: Word32 -> Get ByteString
get_bytes Word32
n = do
    ByteString
b <- Int64 -> Get ByteString
G.getLazyByteString (Word32 -> Int64
word32_to_int64 Word32
n)
    if Word32
n forall a. Eq a => a -> a -> Bool
/= Int64 -> Word32
int64_to_word32 (ByteString -> Int64
B.length ByteString
b)
        then forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"get_bytes: end of stream"
        else Int -> Get ()
G.skip (Word32 -> Int
word32_to_int (forall i. (Num i, Bits i) => i -> i
Byte.align Word32
n))
    forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
b

-- | Get an OSC datum.
get_datum :: Datum_Type -> G.Get Datum
get_datum :: Datum_Type -> Get Datum
get_datum Datum_Type
ty =
    case Datum_Type
ty of
      Datum_Type
'i' -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Int32 -> Datum
Int32 Get Int32
getInt32be
      Datum_Type
'h' -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Int64 -> Datum
Int64 Get Int64
getInt64be
      Datum_Type
'f' -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Float -> Datum
Float Get Float
I.getFloat32be
      Datum_Type
'd' -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Double -> Datum
Double Get Double
I.getFloat64be
      Datum_Type
's' -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Datum
ASCII_String Get ByteString
get_ascii
      Datum_Type
'b' -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Datum
Blob (Word32 -> Get ByteString
get_bytes forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Get Word32
G.getWord32be)
      Datum_Type
't' -> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Double -> Datum
TimeStamp forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> Double
Time.ntpi_to_ntpr) Get Word64
G.getWord64be
      Datum_Type
'm' -> do Word8
b0 <- Get Word8
G.getWord8
                Word8
b1 <- Get Word8
G.getWord8
                Word8
b2 <- Get Word8
G.getWord8
                Word8
b3 <- Get Word8
G.getWord8
                forall (m :: * -> *) a. Monad m => a -> m a
return (MIDI -> Datum
Midi (Word8 -> Word8 -> Word8 -> Word8 -> MIDI
MIDI Word8
b0 Word8
b1 Word8
b2 Word8
b3))
      Datum_Type
_ -> forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"get_datum: illegal type " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Datum_Type
ty)

-- | Get an OSC 'Message', fail if type descriptor is invalid.
get_message :: G.Get Message
get_message :: Get Message
get_message = do
    String
cmd <- Get String
get_string
    ByteString
dsc <- Get ByteString
get_ascii
    case ByteString -> String
S.C.unpack ByteString
dsc of
        Datum_Type
',':String
tags -> do
            [Datum]
arg <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Datum_Type -> Get Datum
get_datum String
tags
            forall (m :: * -> *) a. Monad m => a -> m a
return (String -> [Datum] -> Message
Message String
cmd [Datum]
arg)
        String
e -> forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String
"get_message: invalid type descriptor string: " forall a. [a] -> [a] -> [a]
++ String
e)

-- | Get a sequence of OSC 'Message's, each one headed by its length.
get_message_seq :: G.Get [Message]
get_message_seq :: Get [Message]
get_message_seq = do
    Bool
b <- Get Bool
G.isEmpty
    if Bool
b
        then forall (m :: * -> *) a. Monad m => a -> m a
return []
        else do
            Message
p <- forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Int -> Get a -> Get a
G.isolate Get Message
get_message forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> Int
word32_to_int forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Get Word32
G.getWord32be
            [Message]
ps <- Get [Message]
get_message_seq
            forall (m :: * -> *) a. Monad m => a -> m a
return (Message
pforall a. a -> [a] -> [a]
:[Message]
ps)

-- | Get a bundle. Fail if bundle header is not found in packet.
get_bundle :: G.Get Bundle
get_bundle :: Get Bundle
get_bundle = do
    ByteString
h <- Int -> Get ByteString
G.getByteString (ByteString -> Int
S.C.length ByteString
Byte.bundleHeader_strict)
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ByteString
h forall a. Eq a => a -> a -> Bool
/= ByteString
Byte.bundleHeader_strict) (forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"get_bundle: not a bundle")
    Double
t <- forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word64 -> Double
Time.ntpi_to_ntpr Get Word64
G.getWord64be
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Double -> [Message] -> Bundle
Bundle Double
t) Get [Message]
get_message_seq

-- | Get an OSC 'Packet'.
get_packet :: G.Get Packet
get_packet :: Get Packet
get_packet = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Bundle -> Packet
Packet_Bundle Get Bundle
get_bundle forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Message -> Packet
Packet_Message Get Message
get_message

{-# INLINE decodeMessage #-}
{-# INLINE decodeBundle #-}
{-# INLINE decodePacket #-}
{-# INLINE decodePacket_strict #-}

{- | Decode an OSC 'Message' from a lazy ByteString.

> let b = B.pack [47,103,95,102,114,101,101,0,44,105,0,0,0,0,0,0]
> decodeMessage b == Message "/g_free" [Int32 0]
-}
decodeMessage :: B.ByteString -> Message
decodeMessage :: ByteString -> Message
decodeMessage = forall a. Get a -> ByteString -> a
G.runGet Get Message
get_message

-- | Decode an OSC 'Bundle' from a lazy ByteString.
decodeBundle :: B.ByteString -> Bundle
decodeBundle :: ByteString -> Bundle
decodeBundle = forall a. Get a -> ByteString -> a
G.runGet Get Bundle
get_bundle

{- | Decode an OSC packet from a lazy ByteString.

> let b = B.pack [47,103,95,102,114,101,101,0,44,105,0,0,0,0,0,0]
> decodePacket b == Packet_Message (Message "/g_free" [Int32 0])
-}
decodePacket :: B.ByteString -> Packet
decodePacket :: ByteString -> Packet
decodePacket = forall a. Get a -> ByteString -> a
G.runGet Get Packet
get_packet

-- | Decode an OSC packet from a strict Char8 ByteString.
decodePacket_strict :: S.C.ByteString -> Packet
decodePacket_strict :: ByteString -> Packet
decodePacket_strict = forall a. Get a -> ByteString -> a
G.runGet Get Packet
get_packet forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> ByteString
B.fromChunks forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. a -> [a] -> [a]
:[])