{-# LANGUAGE RecordWildCards #-}

module Network.Control.Flow (
    -- * Constants for flow control.
    defaultMaxStreams,
    defaultMaxStreamData,
    defaultMaxData,

    -- * Flow control for sending
    TxFlow (..),
    newTxFlow,
    txWindowSize,
    WindowSize,

    -- * Flow control for receiving
    RxFlow (..),
    newRxFlow,
    FlowControlType (..),
    maybeOpenRxWindow,
    checkRxLimit,
) where

import Data.Bits

-- | Default max streams. (64)
defaultMaxStreams :: Int
defaultMaxStreams :: Int
defaultMaxStreams = Int
64

-- | Default max data of a stream. (256K bytes)
defaultMaxStreamData :: Int
defaultMaxStreamData :: Int
defaultMaxStreamData = Int
262144

-- | Default max data of a connection. (1M bytes)
defaultMaxData :: Int
defaultMaxData :: Int
defaultMaxData = Int
1048576

-- | Window size.
type WindowSize = Int

-- | Flow for sending
data TxFlow = TxFlow
    { TxFlow -> Int
txfSent :: Int
    , TxFlow -> Int
txfLimit :: Int
    }
    deriving (Int -> TxFlow -> ShowS
[TxFlow] -> ShowS
TxFlow -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [TxFlow] -> ShowS
$cshowList :: [TxFlow] -> ShowS
show :: TxFlow -> String
$cshow :: TxFlow -> String
showsPrec :: Int -> TxFlow -> ShowS
$cshowsPrec :: Int -> TxFlow -> ShowS
Show)

-- | Creating TX flow with an initial window size.
newTxFlow :: WindowSize -> TxFlow
newTxFlow :: Int -> TxFlow
newTxFlow Int
win = Int -> Int -> TxFlow
TxFlow Int
0 Int
win

-- | 'txfLimit' - 'txfSent'.
txWindowSize :: TxFlow -> WindowSize
txWindowSize :: TxFlow -> Int
txWindowSize TxFlow{Int
txfLimit :: Int
txfSent :: Int
txfLimit :: TxFlow -> Int
txfSent :: TxFlow -> Int
..} = Int
txfLimit forall a. Num a => a -> a -> a
- Int
txfSent

-- | Flow for receiving
data RxFlow = RxFlow
    { RxFlow -> Int
rxfWindow :: WindowSize
    , RxFlow -> Int
rxfConsumed :: Int
    , RxFlow -> Int
rxfReceived :: Int
    , RxFlow -> Int
rxfLimit :: Int
    }
    deriving (Int -> RxFlow -> ShowS
[RxFlow] -> ShowS
RxFlow -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RxFlow] -> ShowS
$cshowList :: [RxFlow] -> ShowS
show :: RxFlow -> String
$cshow :: RxFlow -> String
showsPrec :: Int -> RxFlow -> ShowS
$cshowsPrec :: Int -> RxFlow -> ShowS
Show)

-- | Creating RX flow with an initial window size.
newRxFlow :: WindowSize -> RxFlow
newRxFlow :: Int -> RxFlow
newRxFlow Int
win = Int -> Int -> Int -> Int -> RxFlow
RxFlow Int
win Int
0 Int
0 Int
win

-- | The representation of window size update.
data FlowControlType
    = -- | HTTP\/2 style
      FCTWindowUpdate
    | -- | QUIC style
      FCTMaxData

-- | When an application consumed received data,
--   this function should be called to update 'rxfConsumed'.
--   If the window size is less than the half of the initial window.
--   the representation of window size update is returned.
maybeOpenRxWindow
    :: Int
    -- ^ The consumed size.
    -> FlowControlType
    -> RxFlow
    -> (RxFlow, Maybe Int)
    -- ^ 'Just' if the size should be informed to the peer.
maybeOpenRxWindow :: Int -> FlowControlType -> RxFlow -> (RxFlow, Maybe Int)
maybeOpenRxWindow Int
consumed FlowControlType
fct flow :: RxFlow
flow@RxFlow{Int
rxfLimit :: Int
rxfReceived :: Int
rxfConsumed :: Int
rxfWindow :: Int
rxfLimit :: RxFlow -> Int
rxfReceived :: RxFlow -> Int
rxfConsumed :: RxFlow -> Int
rxfWindow :: RxFlow -> Int
..}
    | Int
available forall a. Ord a => a -> a -> Bool
< Int
threshold =
        let limit :: Int
limit = Int
consumed' forall a. Num a => a -> a -> a
+ Int
rxfWindow
            flow' :: RxFlow
flow' =
                RxFlow
flow
                    { rxfConsumed :: Int
rxfConsumed = Int
consumed'
                    , rxfLimit :: Int
rxfLimit = Int
limit
                    }
            update :: Int
update = case FlowControlType
fct of
                FlowControlType
FCTWindowUpdate -> Int
limit forall a. Num a => a -> a -> a
- Int
rxfLimit
                FlowControlType
FCTMaxData -> Int
limit
         in (RxFlow
flow', forall a. a -> Maybe a
Just Int
update)
    | Bool
otherwise =
        let flow' :: RxFlow
flow' = RxFlow
flow{rxfConsumed :: Int
rxfConsumed = Int
consumed'}
         in (RxFlow
flow', forall a. Maybe a
Nothing)
  where
    available :: Int
available = Int
rxfLimit forall a. Num a => a -> a -> a
- Int
rxfReceived
    threshold :: Int
threshold = Int
rxfWindow forall a. Bits a => a -> Int -> a
`unsafeShiftR` Int
1
    consumed' :: Int
consumed' = Int
rxfConsumed forall a. Num a => a -> a -> a
+ Int
consumed

-- | Checking if received data is acceptable against the
--   current window.
checkRxLimit
    :: Int
    -- ^ The size of received data.
    -> RxFlow
    -> (RxFlow, Bool)
    -- ^ Acceptable if 'True'.
checkRxLimit :: Int -> RxFlow -> (RxFlow, Bool)
checkRxLimit Int
received flow :: RxFlow
flow@RxFlow{Int
rxfLimit :: Int
rxfReceived :: Int
rxfConsumed :: Int
rxfWindow :: Int
rxfLimit :: RxFlow -> Int
rxfReceived :: RxFlow -> Int
rxfConsumed :: RxFlow -> Int
rxfWindow :: RxFlow -> Int
..}
    | Int
received' forall a. Ord a => a -> a -> Bool
<= Int
rxfLimit =
        let flow' :: RxFlow
flow' = RxFlow
flow{rxfReceived :: Int
rxfReceived = Int
received'}
         in (RxFlow
flow', Bool
True)
    | Bool
otherwise = (RxFlow
flow, Bool
False)
  where
    received' :: Int
received' = Int
rxfReceived forall a. Num a => a -> a -> a
+ Int
received