    -- | Optimised encode function for Osc packets.
    module Sound.Osc.Coding.Encode.Builder (
      build_packet,
      encodeMessage,
      encodeBundle,
      encodePacket,
      encodePacket_strict,
    ) where

    import Data.Word {- base -}

    import qualified Blaze.ByteString.Builder as B {- bytestring -}
    import qualified Blaze.ByteString.Builder.Char8 as B {- bytestring -}
    import qualified Data.ByteString as S {- bytestring -}
    import qualified Data.ByteString.Lazy as L {- bytestring -}

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

    -- | Generate a list of zero bytes for padding.
    padding :: Int -> [Word8]
    padding :: Int -> [Word8]
    padding Int
    n = Int -> Word8 -> [Word8]
    forall a. Int -> a -> [a]
    replicate Int
    n Word8
    0

    -- | Nul byte (0) and then zero padding.
    nul_and_padding :: Int -> B.Builder
    nul_and_padding :: Int -> Builder
    nul_and_padding Int
    n = [Word8] -> Builder
    B.fromWord8s (Word8
    0 Word8 -> [Word8] -> [Word8]
    forall a. a -> [a] -> [a]
    : Int -> [Word8]
    padding (Int -> Int
    forall i. (Num i, Bits i) => i -> i
    Byte.align Int
    n))

    -- Encode a string with zero padding.
    build_ascii :: Ascii -> B.Builder
    build_ascii :: Ascii -> Builder
    build_ascii Ascii
    s = Ascii -> Builder
    B.fromByteString Ascii
    s Builder -> Builder -> Builder
    forall a. Semigroup a => a -> a -> a
    <> Int -> Builder
    nul_and_padding (Ascii -> Int
    S.length Ascii
    s Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    1)

    -- Encode a string with zero padding.
    build_string :: String -> B.Builder
    build_string :: String -> Builder
    build_string String
    s = String -> Builder
    B.fromString String
    s Builder -> Builder -> Builder
    forall a. Semigroup a => a -> a -> a
    <> Int -> Builder
    nul_and_padding (String -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length String
    s Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    1)

    -- Encode a byte string with prepended length and zero padding.
    build_bytes :: L.ByteString -> B.Builder
    build_bytes :: ByteString -> Builder
    build_bytes ByteString
    s =
      Int32 -> Builder
    B.fromInt32be (Int64 -> Int32
    Convert.int64_to_int32 (ByteString -> Int64
    L.length ByteString
    s))
        Builder -> Builder -> Builder
    forall a. Semigroup a => a -> a -> a
    <> ByteString -> Builder
    B.fromLazyByteString ByteString
    s
        Builder -> Builder -> Builder
    forall a. Semigroup a => a -> a -> a
    <> [Word8] -> Builder
    B.fromWord8s (Int -> [Word8]
    padding (Int64 -> Int
    Convert.int64_to_int (Int64 -> Int64
    forall i. (Num i, Bits i) => i -> i
    Byte.align (ByteString -> Int64
    L.length ByteString
    s))))

    -- Encode an Osc datum.
    build_datum :: Datum -> B.Builder
    build_datum :: Datum -> Builder
    build_datum Datum
    d =
      case Datum
    d of
        Int32 Int32
    i -> Int32 -> Builder
    B.fromInt32be Int32
    i
        Int64 Int64
    i -> Int64 -> Builder
    B.fromInt64be Int64
    i
        Float Float
    n -> Word32 -> Builder
    B.fromWord32be (Float -> Word32
    Cast.f32_w32 Float
    n)
        Double Double
    n -> Word64 -> Builder
    B.fromWord64be (Double -> Word64
    Cast.f64_w64 Double
    n)
        TimeStamp Double
    t -> Word64 -> Builder
    B.fromWord64be (Double -> Word64
    ntpr_to_ntpi Double
    t)
        AsciiString Ascii
    s -> Ascii -> Builder
    build_ascii Ascii
    s
        Midi (MidiData Word8
    b0 Word8
    b1 Word8
    b2 Word8
    b3) -> [Word8] -> Builder
    B.fromWord8s [Word8
    b0, Word8
    b1, Word8
    b2, Word8
    b3]
        Blob ByteString
    b -> ByteString -> Builder
    build_bytes ByteString
    b

    -- Encode an Osc 'Message'.
    build_message :: Message -> B.Builder
    build_message :: Message -> Builder
    build_message (Message String
    c [Datum]
    l) =
      [Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat
        [ String -> Builder
    build_string String
    c
        , Ascii -> Builder
    build_ascii ([Datum] -> Ascii
    descriptor [Datum]
    l)
        , [Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat ((Datum -> Builder) -> [Datum] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    map Datum -> Builder
    build_datum [Datum]
    l)
        ]

    -- Encode an Osc 'Bundle'.
    build_bundle_ntpi :: Ntp64 -> [Message] -> B.Builder
    build_bundle_ntpi :: Word64 -> [Message] -> Builder
    build_bundle_ntpi Word64
    t [Message]
    l =
      [Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat
        [ ByteString -> Builder
    B.fromLazyByteString ByteString
    Byte.bundleHeader
        , Word64 -> Builder
    B.fromWord64be Word64
    t
        , [Builder] -> Builder
    forall a. Monoid a => [a] -> a
    mconcat ((Message -> Builder) -> [Message] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    map (ByteString -> Builder
    build_bytes (ByteString -> Builder)
    -> (Message -> ByteString) -> Message -> Builder
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Builder -> ByteString
    B.toLazyByteString (Builder -> ByteString)
    -> (Message -> Builder) -> Message -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Message -> Builder
    build_message) [Message]
    l)
        ]

    -- | Builder for an Osc 'Packet'.
    build_packet :: PacketOf Message -> B.Builder
    build_packet :: PacketOf Message -> Builder
    build_packet PacketOf Message
    o =
      case PacketOf Message
    o of
        Packet_Message Message
    m -> Message -> Builder
    build_message Message
    m
        Packet_Bundle (Bundle Double
    t [Message]
    m) -> Word64 -> [Message] -> Builder
    build_bundle_ntpi (Double -> Word64
    ntpr_to_ntpi Double
    t) [Message]
    m

    {-# INLINE encodePacket #-}
    {-# INLINE encodeMessage #-}
    {-# INLINE encodeBundle #-}
    {-# INLINE encodePacket_strict #-}

    -- | Encode an Osc 'Packet'.
    encodePacket :: PacketOf Message -> L.ByteString
    encodePacket :: PacketOf Message -> ByteString
    encodePacket = Builder -> ByteString
    B.toLazyByteString (Builder -> ByteString)
    -> (PacketOf Message -> Builder) -> PacketOf Message -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PacketOf Message -> Builder
    build_packet

    {- | Encode an Osc 'Message', ie. 'encodePacket' of 'Packet_Message'.

    >>> let m = [47,103,95,102,114,101,101,0,44,105,0,0,0,0,0,0]
    >>> encodeMessage (Message "/g_free" [Int32 0]) == L.pack m
    True
    -}
    encodeMessage :: Message -> L.ByteString
    encodeMessage :: Message -> ByteString
    encodeMessage = PacketOf Message -> ByteString
    encodePacket (PacketOf Message -> ByteString)
    -> (Message -> PacketOf Message) -> Message -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Message -> PacketOf Message
    forall t. Message -> PacketOf t
    Packet_Message

    {- | Encode an Osc 'Bundle', ie. 'encodePacket' of 'Packet_Bundle'.

    >>> let m = [47,103,95,102,114,101,101,0,44,105,0,0,0,0,0,0]
    >>> let b = [35,98,117,110,100,108,101,0,0,0,0,0,0,0,0,1,0,0,0,16] ++ m
    >>> encodeBundle (Bundle immediately [Message "/g_free" [Int32 0]]) == L.pack b
    True
    -}
    encodeBundle :: BundleOf Message -> L.ByteString
    encodeBundle :: BundleOf Message -> ByteString
    encodeBundle = PacketOf Message -> ByteString
    encodePacket (PacketOf Message -> ByteString)
    -> (BundleOf Message -> PacketOf Message)
    -> BundleOf Message
    -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . BundleOf Message -> PacketOf Message
    forall t. BundleOf t -> PacketOf t
    Packet_Bundle

    -- | Encode an Osc 'Packet' to a strict 'S.ByteString'.
    encodePacket_strict :: PacketOf Message -> S.ByteString
    encodePacket_strict :: PacketOf Message -> Ascii
    encodePacket_strict = Builder -> Ascii
    B.toByteString (Builder -> Ascii)
    -> (PacketOf Message -> Builder) -> PacketOf Message -> Ascii
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PacketOf Message -> Builder
    build_packet