data-layout-0.1.0.0: src/Data/Layout/Language.hs
-- | A DSL for describing data laid out in a regular fashion with
-- a combination of structures and arrays.
module Data.Layout.Language (
-- * Combinators
repeat
, offset
, group
-- * Value Formats
, word8
, word16le
, word32le
, word64le
, word16be
, word32be
, word64be
-- * Optimizations
, optimize
-- * Utility
, size
, byteOrder
, valueSize1
, valueSizeN
, valueCount
, chunk
) where
import Prelude hiding (repeat)
import Data.Layout.Internal
------------------------------------------------------------------------
-- Combinators
-- | Repeat a layout 'n' times.
repeat :: Reps -> Layout -> Layout
repeat n | n > 1 = Repeat n
| n == 1 = id
| otherwise = invalid "repeat" "repetition must be at least 1"
-- | Skip 'n' bytes before accessing the next part of the layout.
offset :: Bytes -> Layout -> Layout
offset n | n > 0 = Offset n
| n == 0 = id
| otherwise = invalid "offset" "must skip 0 or more bytes"
-- | Wrap a layout with a group which is 'n' bytes in size.
group :: Bytes -> Layout -> Layout
group n xs | n <= 0 = invalid "group" "must contain 1 or more bytes"
| n < size xs = invalid "group" "cannot be smaller than the inner layout"
| otherwise = Group n xs
invalid :: String -> String -> a
invalid fn msg = error ("Data.Layout.Language." ++ fn ++ ": " ++ msg)
------------------------------------------------------------------------
-- Value Formats
-- | 8-bit word.
word8 :: Layout
word8 = Value Word8
-- | 16-bit word, little endian.
word16le :: Layout
word16le = Value Word16le
-- | 32-bit word, little endian.
word32le :: Layout
word32le = Value Word32le
-- | 64-bit word, little endian.
word64le :: Layout
word64le = Value Word64le
-- | 16-bit word, big endian.
word16be :: Layout
word16be = Value Word16be
-- | 32-bit word, big endian.
word32be :: Layout
word32be = Value Word32be
-- | 64-bit word, big endian.
word64be :: Layout
word64be = Value Word64be
formatSize :: ValueFormat -> Bytes
formatSize Word8 = 1
formatSize Word16le = 2
formatSize Word32le = 4
formatSize Word64le = 8
formatSize Word16be = 2
formatSize Word32be = 4
formatSize Word64be = 8
formatByteOrder :: ValueFormat -> ByteOrder
formatByteOrder Word8 = NoByteOrder
formatByteOrder Word16le = LittleEndian
formatByteOrder Word32le = LittleEndian
formatByteOrder Word64le = LittleEndian
formatByteOrder Word16be = BigEndian
formatByteOrder Word32be = BigEndian
formatByteOrder Word64be = BigEndian
------------------------------------------------------------------------
-- Optimizations
data GroupStatus = NoGroup | GotGroup
-- | Removes redundant data groups. Grouping a set of data is only
-- sensible when the data can be repeated. If we find multiple
-- groupings without a repeat in the middle we can remove all but
-- the outer group.
optimizeGroups :: Layout -> Layout
optimizeGroups = go NoGroup
where
-- found a group, pass GotGroup to inner layout
go NoGroup (Group n xs) = Group n (go GotGroup xs)
-- already got an outer group, drop this one as it is redundant
go GotGroup (Group _ xs) = go GotGroup xs
-- reset group status, further grouping may be required for repeats
go _ (Repeat n xs) = Repeat n (go NoGroup xs)
-- other configurations are uninteresting for this optimization
go status x = mapInner (go status) x
-- | Folds adjancent data offset operations in to a single one.
optimizeOffsets :: Layout -> Layout
optimizeOffsets = go
where
go (Offset n (Offset m xs)) = go (Offset (n + m) xs)
go x = mapInner go x
-- | Remove redundancies from a layout.
optimize :: Layout -> Layout
optimize = optimizeOffsets . optimizeGroups
------------------------------------------------------------------------
-- Utility
-- | Calculates the total size of the layout.
size :: Layout -> Int
size (Value x) = formatSize x
size (Offset x xs) = x + size xs
size (Group x _) = x
size (Repeat n xs) = n * size xs
-- | Gets the format of a single value in the layout.
valueFormat :: Layout -> ValueFormat
valueFormat (Value fmt) = fmt
valueFormat (Offset _ xs) = valueFormat xs
valueFormat (Group _ xs) = valueFormat xs
valueFormat (Repeat _ xs) = valueFormat xs
-- | Gets the byte order of the values in the layout.
byteOrder :: Layout -> ByteOrder
byteOrder = formatByteOrder . valueFormat
-- | Calculates the size of a single value in the layout.
valueSize1 :: Layout -> Int
valueSize1 = formatSize . valueFormat
-- | Calculates the total size of the values stored in the layout.
valueSizeN :: Layout -> Int
valueSizeN x = valueSize1 x * valueCount x
-- | Counts the number of times a value is repeated in the layout.
valueCount :: Layout -> Int
valueCount (Value _) = 1
valueCount (Offset _ xs) = valueCount xs
valueCount (Group _ xs) = valueCount xs
valueCount (Repeat n xs) = valueCount xs * n
-- | Applys a transformation to the inner layout.
mapInner :: (Layout -> Layout) -> Layout -> Layout
mapInner _ (Value fmt) = Value fmt
mapInner f (Offset n xs) = Offset n (f xs)
mapInner f (Group n xs) = Group n (f xs)
mapInner f (Repeat n xs) = Repeat n (f xs)
-- | Splits a repeated layout in to multiple layouts each with a maximum
-- of 'n' repetitions.
chunk :: Int -> Layout -> [Layout]
chunk n (Repeat m xs) = replicate (m `div` n) (Repeat n xs) ++ [Repeat (m `rem` n) xs]
chunk _ xs = [xs]