packages feed

quic-0.3.5: Network/QUIC/Types/Ack.hs

module Network.QUIC.Types.Ack where

import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet

type PacketNumber = Int

type Range = Int
type Gap = Int

data AckInfo = AckInfo PacketNumber Range [(Gap, Range)]
    deriving (Eq, Show)

ackInfo0 :: AckInfo
ackInfo0 = AckInfo (-1) 0 []

-- | Whether the ranges name packet numbers that could exist.
--
-- RFC 9000 section 19.3.1 walks the ranges downward from the largest
-- acknowledged.  Each gap gives the largest of the next range as
-- @previous_smallest - gap - 2@, and "if the value of the Gap field or the
-- value calculated is negative, an endpoint MUST generate a connection error
-- of type FRAME_ENCODING_ERROR".
--
-- Nothing checked this.  The ranges were turned into a predicate and asked
-- about packets we had sent; ones reaching below zero simply matched nothing.
validAckInfo :: AckInfo -> Bool
validAckInfo (AckInfo lpn fr grs) = lpn >= 0 && fr >= 0 && stt >= 0 && go stt grs
  where
    stt = lpn - fr
    go _ [] = True
    go s ((g, r) : xs)
        | g < 0 || r < 0 = False
        | z < 0 || lo < 0 = False
        | otherwise = go lo xs
      where
        z = s - g - 2
        lo = z - r

-- |
-- >>> toAckInfo [9]
-- AckInfo 9 0 []
-- >>> toAckInfo [9,8,7]
-- AckInfo 9 2 []
-- >>> toAckInfo [8,7,3,2]
-- AckInfo 8 1 [(2,1)]
-- >>> toAckInfo [9,8,7,5,4]
-- AckInfo 9 2 [(0,1)]
toAckInfo :: [PacketNumber] -> AckInfo
toAckInfo [] = error "toAckInfo"
toAckInfo [l] = AckInfo l 0 []
toAckInfo (l : ls) = ack l ls 0
  where
    ack _ [] fr = AckInfo l fr []
    ack p (x : xs) fr
        | p - 1 == x = ack x xs (fr + 1)
        | otherwise = AckInfo l fr $ ranges x xs (fromIntegral (p - x) - 2) 0
    ranges _ [] g r = [(g, r)]
    ranges p (x : xs) g r
        | p - 1 == x = ranges x xs g (r + 1)
        | otherwise = (g, r) : ranges x xs (fromIntegral (p - x) - 2) 0

-- |
-- >>> fromAckInfo $ AckInfo 9 0 []
-- [9]
-- >>> fromAckInfo $ AckInfo 9 2 []
-- [7,8,9]
-- >>> fromAckInfo $ AckInfo 8 1 [(2,1)]
-- [2,3,7,8]
-- >>> fromAckInfo $ AckInfo 9 2 [(0,1)]
-- [4,5,7,8,9]
fromAckInfo :: AckInfo -> [PacketNumber]
fromAckInfo (AckInfo lpn fr grs) = loop grs stt [stt .. lpn]
  where
    stt = lpn - fromIntegral fr
    -- Carrying the smallest of the range just built, rather than reading it
    -- back off the front of the accumulator.  Taking it off the front needs a
    -- clause for the accumulator being empty, which it never is -- and that
    -- clause was an error call sitting on a path the peer's ACK ranges reach.
    loop [] _ acc = acc
    loop ((g, r) : xs) s acc = loop xs lo ([lo .. z] ++ acc)
      where
        z = s - fromIntegral g - 2
        lo = z - fromIntegral r

fromAckInfoToPred :: AckInfo -> (PacketNumber -> Bool)
fromAckInfoToPred (AckInfo lpn fr grs) =
    \x -> any (f x) $ loop grs stt [(stt, lpn)]
  where
    f x (l, u) = l <= x && x <= u
    stt = lpn - fromIntegral fr
    -- As in 'fromAckInfo': carry the smallest of the range just built instead
    -- of reading it back off the accumulator, so there is no empty case to
    -- answer for.  The peer chooses these ranges.
    loop [] _ acc = acc
    loop ((g, r) : xs) s acc = loop xs lo ((lo, z) : acc)
      where
        z = s - fromIntegral g - 2
        lo = z - fromIntegral r

----------------------------------------------------------------

newtype PeerPacketNumbers = PeerPacketNumbers IntSet
    deriving (Eq, Show)

emptyPeerPacketNumbers :: PeerPacketNumbers
emptyPeerPacketNumbers = PeerPacketNumbers IntSet.empty