
George Pollard wrote:
The structure of an ID3 tag goes something like this: Header: - total size of tag - other header info A series of frames, each with: - total size of frame - other header info - frame data
Since the ID3 tag as a whole has size information, I need to pass that into the frame-reading functions to ensure that I never read past the end of the total header. This exact same requirement is present within the frame-reading function itself; it must read the frame data without reading past the end of the frame.
The structure of the chunk-encoded content is similar: first comes the size, then come data of that size. We have to read exactly that amount (and then check for the chunk trailer, CRLF). In a sense, we have a data stream embedded inside another stream. The Iteratee IO framework was specifically designed to process these sorts of arbitrarily nested and encoded data streams. In particular, the file http://okmij.org/ftp/Haskell/Iteratee/IterateeM.hs contains the function -- Read n elements from a stream and apply the given iteratee to the -- stream of the read elements. Unless the stream is terminated early, we -- read exactly n elements (even if the iteratee has accepted fewer). -- This procedure shows a different way of composing two iteratees: -- `vertical' rather than `horizontal' stake :: Monad m => Int -> IterateeG el m a -> IterateeGM el m (IterateeG el m a) The implementation is 11 lines long, counting the `where' line. I believe the code is declarative rather than imperative. The function stake (the analogue of List.take) is then used in enum_chunk_decoded :: Monad m => Iteratee m a -> IterateeM m a The end of the file IterateeM.hs has several complete tests.