{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-redundant-constraints #-} module Data.ByteString.Mp4.Boxes.Expandable where import Data.ByteString.IsoBaseFileFormat.Box import Data.ByteString.IsoBaseFileFormat.ReExports -- * Static Expandable data StaticExpandableContent :: BitRecord -> Extends BitRecord type StaticExpandableContentMaxBits = 32 type instance From (StaticExpandableContent record) = (ExpandableSize (ShiftR StaticExpandableContentMaxBits (SizeInBits record) 3)) :+: record type family ExpandableSize (s :: Nat) :: BitRecord where ExpandableSize 0 = 'EmptyBitRecord ExpandableSize s = If (s <=? 127) (ExpandableSizeLastChunk s) (ExpandableSizeNext (ShiftR StaticExpandableContentMaxBits s 7) :+: ExpandableSizeLastChunk s) type ExpandableSizeLastChunk (s :: Nat) = Field 1 := 0 .+. Field 7 := s type family ExpandableSizeNext (s :: Nat) :: BitRecord where ExpandableSizeNext 0 = 'EmptyBitRecord ExpandableSizeNext s = If (s <=? 127) (ExpandableSizeNextChunk s) (ExpandableSizeNext (ShiftR StaticExpandableContentMaxBits s 7) :+: ExpandableSizeNextChunk s) type ExpandableSizeNextChunk (s :: Nat) = Field 1 := 1 .+. Field 7 := s -- * Runtime-value Expandable -- TODO remove "runtime" Expandable? newtype Expandable t where Expandable ::t -> Expandable t instance IsBoxContent t => IsBoxContent (Expandable t) where boxSize (Expandable x) = expandableSizeSize (boxSize x) + boxSize x boxBuilder (Expandable x) = expandableSizeBuilder (boxSize x) <> boxBuilder x expandableSizeSize :: BoxSize -> BoxSize expandableSizeSize UnlimitedSize = error "Unlimited size not supported by expandable" expandableSizeSize (BoxSize s) | s >= 2 ^ (28 :: Int) = error "Expandable size >= 2^(28 :: Int)" | s >= 2 ^ (21 :: Int) = 4 | s >= 2 ^ (14 :: Int) = 3 | s >= 2 ^ (7 :: Int) = 2 | otherwise = 1 expandableSizeBuilder :: BoxSize -> Builder expandableSizeBuilder UnlimitedSize = error "Unlimited size not supported by expandable" expandableSizeBuilder (BoxSize s) | s >= 2 ^ (28 :: Int) = error "Expandable size >= 2^(28 :: Int)" | s >= 2 ^ (21 :: Int) = word8 (fromIntegral (0x80 .|. (s `unsafeShiftR` 21))) <> word8 (fromIntegral (0x80 .|. ((s `unsafeShiftR` 14) .&. 0x7F))) <> word8 (fromIntegral (0x80 .|. ((s `unsafeShiftR` 7) .&. 0x7F))) <> word8 (fromIntegral (s .&. 0x7F)) | s >= 2 ^ (14 :: Int) = word8 (fromIntegral (0x80 .|. (s `unsafeShiftR` 14))) <> word8 (fromIntegral (0x80 .|. ((s `unsafeShiftR` 7) .&. 0x7F))) <> word8 (fromIntegral (s .&. 0x7F)) | s >= 2 ^ (7 :: Int) = word8 (fromIntegral (0x80 .|. (s `unsafeShiftR` 7))) <> word8 (fromIntegral (s .&. 0x7F)) | otherwise = word8 (fromIntegral s)