ebml-0.1.0.0: src/Codec/EBML/Get.hs
module Codec.EBML.Get where
import Data.Binary.Get (Get, bytesRead, getByteString, isEmpty, lookAheadM)
import Data.Text.Encoding (decodeUtf8)
import Data.Word (Word64)
import Codec.EBML.Element
import Codec.EBML.Schema
import Data.Bits (Bits)
getElement :: EBMLSchemas -> Get EBMLElement
getElement schemas = do
elth <- getElementHeader
getElementValue schemas elth
getElementValue :: EBMLSchemas -> EBMLElementHeader -> Get EBMLElement
getElementValue schemas elth = do
-- here is a good place to add traceM debug
val <- case lookupSchema elth.eid schemas of
Nothing -> getBinary elth
Just schema -> schema.decode schemas elth
pure $ EBMLElement elth val
getDocument :: EBMLSchemas -> Get EBMLDocument
getDocument schemas = EBMLDocument <$> go
where
go = do
empty <- isEmpty
if empty
then pure []
else do
elt <- getElement schemas
elts <- go
pure (elt : elts)
getBinary :: EBMLElementHeader -> Get EBMLValue
getBinary elth = case elth.size of
Nothing -> fail "Invalid binary header size"
Just sz -> EBMLBinary <$> getByteString (fromIntegral sz)
getText :: EBMLElementHeader -> Get EBMLValue
getText elth = case elth.size of
Nothing -> fail "Invalid text header size"
Just sz -> EBMLText . decodeUtf8 <$> getByteString (fromIntegral sz)
getUnsignedInteger :: EBMLElementHeader -> Get EBMLValue
getUnsignedInteger elth = EBMLUnsignedInteger <$> getInt elth.size
getInteger :: EBMLElementHeader -> Get EBMLValue
getInteger elth = EBMLUnsignedInteger <$> getInt elth.size
getInt :: (Bits a, Integral a) => Maybe Word64 -> Get a
getInt size = getVar sz 0
where
-- TODO: check the value is in the [0..8] range
sz = maybe 0 fromIntegral size
getRoot :: EBMLSchemas -> EBMLElementHeader -> Get EBMLValue
getRoot schemas elth = case elth.size of
Nothing -> EBMLRoot <$> getUntil schemas elth.eid
Just sz -> getRootFixed schemas sz
getUntil :: EBMLSchemas -> EBMLID -> Get [EBMLElement]
getUntil schemas eid = go
where
getChild :: Get (Maybe EBMLElement)
getChild = do
-- This is not exactly correct. The rfc-8794 spec (chapter 6.2) says we should decode until
-- any valid parent or global element. Because the EBMLSchema doesn't yet contain this information,
-- and because in practice such unknown-sized element are segment/cluster, we simply decode until
-- we find a matching element.
elth <- getElementHeader
if elth.eid == eid
then pure Nothing
else Just <$> getElementValue schemas elth
go = do
empty <- isEmpty
if empty
then pure []
else goGet
goGet =
lookAheadM getChild >>= \case
Just elt -> do
elts <- go
pure (elt : elts)
Nothing -> pure []
getRootFixed :: EBMLSchemas -> Word64 -> Get EBMLValue
getRootFixed schemas sz = do
startPosition <- bytesRead
let maxPosition = startPosition + fromIntegral sz
getChilds = do
currentPosition <- bytesRead
if
| currentPosition > maxPosition ->
fail $ "Element decode position " <> show currentPosition <> " exceed parent size " <> show sz
| currentPosition == maxPosition ->
pure []
| otherwise -> do
elt <- getElement schemas
elts <- getChilds
pure (elt : elts)
EBMLRoot <$> getChilds