content: module Network.QUIC.Packet.Number (
      encodePacketNumber,
      decodePacketNumber,
  ) where
  
  import Network.QUIC.Imports
  import Network.QUIC.Types
  
  ----------------------------------------------------------------
  
  -- |
  --
  -- >>> encodePacketNumber 0xabe8bc 0xac5c02 == (0x5c02,2)
  -- True
  -- >>> encodePacketNumber 0xa82f30ea 0xa82f9b32 == (0x9b32,2)
  -- True
  -- >>> encodePacketNumber 0xabe8bc 0xace8fe == (0xace8fe, 3)
  -- True
  {- FOURMOLU_DISABLE -}
  encodePacketNumber :: PacketNumber -> PacketNumber -> (EncodedPacketNumber, Int)
  encodePacketNumber largestPN pn = (diff, bytes)
    where
      enoughRange = (pn - largestPN) * 2
      (pnMask, bytes)
  --      | enoughRange <      256 = (0x000000ff, 1)
        | enoughRange <    65536 = (0x0000ffff, 2)
        | enoughRange < 16777216 = (0x00ffffff, 3)
        | otherwise              = (0xffffffff, 4)
      diff = fromIntegral (pn .&. pnMask)
  {- FOURMOLU_ENABLE -}
  
  ----------------------------------------------------------------
  
  -- |
  --
  -- >>> decodePacketNumber 0xabe8bc 0x5c02 2 == 0xac5c02
  -- True
  -- >>> decodePacketNumber 0xa82f30ea 0x9b32 2 == 0xa82f9b32
  -- True
  -- >>> decodePacketNumber 0xabe8bc 0xace8fe 3 == 0xace8fe
  -- True
  {- FOURMOLU_DISABLE -}
  decodePacketNumber :: PacketNumber -> EncodedPacketNumber -> Int -> PacketNumber
  decodePacketNumber largestPN truncatedPN bytes
    | candidatePN <= expectedPN - pnHwin
   && candidatePN <  mx - pnWin          = candidatePN + pnWin
    | candidatePN >  expectedPN + pnHwin
   && candidatePN >= pnWin               = candidatePN - pnWin
    | otherwise                          = candidatePN
    where
      mx = 1 !<<. 62
      pnNbits = bytes * 8
      expectedPN = largestPN + 1
      pnWin = 1 !<<. pnNbits
      pnHwin = pnWin !>>. 1
      pnMask = pnWin - 1
      candidatePN = (expectedPN .&. complement pnMask)
                .|. fromIntegral truncatedPN
  {- FOURMOLU_ENABLE -}
  