data-layout (empty) → 0.1.0.0
raw patch · 9 files changed
+774/−0 lines, 9 filesdep +basedep +bytestringdep +vectorsetup-changed
Dependencies added: base, bytestring, vector
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- cbits/layout.c +107/−0
- data-layout.cabal +39/−0
- src/Data/Layout.hs +15/−0
- src/Data/Layout/ForeignPtr.hs +18/−0
- src/Data/Layout/Internal.hs +51/−0
- src/Data/Layout/Language.hs +193/−0
- src/Data/Layout/Vector.hs +319/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012, Jacob Stanley++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Jacob Stanley nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cbits/layout.c view
@@ -0,0 +1,107 @@+#include <stdint.h>++#define id(n) (n)++#define bswap32 __builtin_bswap32+#define bswap64 __builtin_bswap64++/*+ * Unfortunately GCC does not have a __builtin_bswap16, but+ * this should compile to pretty good assembly.+ */+inline static uint16_t bswap16(uint16_t n)+{+ return (n >> 8) | (n << 8);+}++/*+ * Defines an encode/decode case for the specified type+ * and byte order.+ *+ * enc The memory area (src or dst) which contains or will+ * contain the encoded data. During encoding this will+ * be 'dst' and during decoding this will be 'src'.+ *+ * type The type of the values to copy.+ *+ * bswap The function to use to swap the byte order. Pass+ * the identity function if no byte swapping is+ * required.+ */+#define CASE(_enc,_type,_bswap) \+case sizeof (_type): \+ /* repeat 'num_reps' times */ \+ for (r = 0; r < num_reps; ++r) { \+ /* skip 'offsets[0]' bytes */ \+ _enc += offsets[0]; \+ for (o = 1; o < num_offsets; ++o) { \+ /* copy 'num_values * sizeof (_type)' bytes */ \+ for (v = 0; v < num_values; v++) { \+ *(_type *)dst = _bswap(*(_type *)src); \+ dst += sizeof (_type); \+ src += sizeof (_type); \+ } \+ /* skip 'offsets[o]' bytes */ \+ _enc += offsets[o]; \+ } \+ } \+break;++#define OK 0+#define ERROR 1++/*+ * data_layout_encode / data_layout_decode+ *+ * Copies data from the second memory area (source) into the first+ * memory area (destination) using the specified operations.+ *+ * dst destination memory area+ * src source memory area+ * num_reps number of times to repeat the decode+ * num_offsets number of skip operations in 'offsets'+ * offsets list with number of bytes to skip in between each copy+ * num_values number of values to copy in between each skip+ * value_size size of a single value in bytes+ * swap_bytes non-zero to swap the byte order of values+ *+ * Returns zero on success, non-zero otherwise.+ */++#define CODEC_FN(_name,_enc) \+int data_layout_##_name \+ ( char *dst \+ , char *src \+ , int num_reps \+ , int num_offsets \+ , int *offsets \+ , int num_values \+ , int value_size \+ , int swap_bytes \+ ) \+{ \+ int r, o, v; \+ \+ if (swap_bytes) { \+ switch (value_size) { \+ CASE (_enc, uint64_t, bswap64); \+ CASE (_enc, uint32_t, bswap32); \+ CASE (_enc, uint16_t, bswap16); \+ CASE (_enc, uint8_t, id); \+ default: return ERROR; \+ } \+ } else { \+ switch (value_size) { \+ CASE (_enc, uint64_t, id); \+ CASE (_enc, uint32_t, id); \+ CASE (_enc, uint16_t, id); \+ CASE (_enc, uint8_t, id); \+ default: return ERROR; \+ } \+ } \+ \+ return OK; \+}++CODEC_FN (encode, dst)+CODEC_FN (decode, src)
+ data-layout.cabal view
@@ -0,0 +1,39 @@+name: data-layout+version: 0.1.0.0+synopsis: Read/write arbitrary binary layouts to a "Data.Vector.Storable".+homepage: http://github.com/jystic/data-layout+license: BSD3+license-file: LICENSE+copyright: Jacob Stanley (c) 2012-2013+author: Jacob Stanley+maintainer: Jacob Stanley <jacob@stanley.io>+category: Data+build-type: Simple+cabal-version: >= 1.6++library+ hs-source-dirs: src++ exposed-modules:+ Data.Layout+ Data.Layout.Language+ Data.Layout.Vector+ Data.Layout.Internal++ other-modules:+ Data.Layout.ForeignPtr++ build-depends:+ base == 4.*+ , bytestring >= 0.10+ , vector >= 0.10++ c-sources: cbits/layout.c+ cc-options: -Wall -Werror -O3++ ghc-options:+ -O2 -Wall+ -funbox-strict-fields+ -fno-warn-unused-do-bind+ -fno-warn-orphans+ -fwarn-tabs
+ src/Data/Layout.hs view
@@ -0,0 +1,15 @@+-- | A DSL for describing data laid out in a regular fashion with+-- a combination of structures and arrays.++module Data.Layout (+ Layout+ , Bytes+ , Reps+ , ByteOrder(..)+ , module Data.Layout.Language+ , module Data.Layout.Vector+ ) where++import Data.Layout.Internal+import Data.Layout.Language+import Data.Layout.Vector
+ src/Data/Layout/ForeignPtr.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE CPP #-}++module Data.Layout.ForeignPtr+ ( mallocPlainForeignPtrBytes+ ) where++#if __GLASGOW_HASKELL__ >= 605++import GHC.ForeignPtr (mallocPlainForeignPtrBytes)++#else++import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrBytes)++mallocPlainForeignPtrBytes :: Int -> IO (ForeignPtr a)+mallocPlainForeignPtrBytes = mallocForeignPtrBytes++#endif
+ src/Data/Layout/Internal.hs view
@@ -0,0 +1,51 @@+-- | /Use the internal representation of layouts at your own risk./+--+-- The smart constructors in "Data.Layout.Language" enforce the+-- documented invariants for `Layout`. The library has undefined+-- behavior if these invariants are violated.+--+-- The recommended way to use this library is to import "Data.Layout".++module Data.Layout.Internal where++-- | Size in bytes.+type Bytes = Int++-- | Number of repetitions.+type Reps = Int++-- | Describes the binary layout of a set of data.+data Layout =+ -- | A value with a known format.+ Value ValueFormat++ -- | Skip 'n' bytes in to a struct.+ -- /The offset must be 1 or more bytes./+ | Offset Bytes Layout++ -- | A struct which is 'n' bytes in size.+ -- /The size of the struct must be larger than that of the field layout it wraps./+ | Group Bytes Layout++ -- | An array with 'n' elements.+ -- /An array must have 2 or more elements./+ | Repeat Reps Layout+ deriving (Eq, Show)++-- | The size and byte order of a value.+data ValueFormat =+ Word8 -- ^ 8-bit word.+ | Word16le -- ^ 16-bit word, little endian.+ | Word32le -- ^ 32-bit word, little endian.+ | Word64le -- ^ 64-bit word, little endian.+ | Word16be -- ^ 16-bit word, big endian.+ | Word32be -- ^ 32-bit word, big endian.+ | Word64be -- ^ 64-bit word, big endian.+ deriving (Eq, Show)++-- | The byte order of a value.+data ByteOrder =+ NoByteOrder -- ^ The word is only one byte in size.+ | LittleEndian -- ^ The least significant byte is first in the word.+ | BigEndian -- ^ The most significant byte is first in the word.+ deriving (Eq, Show)
+ src/Data/Layout/Language.hs view
@@ -0,0 +1,193 @@+-- | 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]
+ src/Data/Layout/Vector.hs view
@@ -0,0 +1,319 @@+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Data.Layout.Vector (+ Codec+ , compile++ , StorableVector (..)+ , encodeVectors+ , decodeVector+ ) where++import Control.Monad (when)+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.ByteString.Internal (ByteString(..))+import qualified Data.Vector.Storable as V+import Data.Word (Word8, Word32)+import Foreign.C (CInt (..))+import Foreign.ForeignPtr ()+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, castForeignPtr)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr, plusPtr, castPtr)+import Foreign.Storable (Storable, peek, poke, sizeOf)+import System.IO.Unsafe (unsafePerformIO)++import Data.Layout.ForeignPtr (mallocPlainForeignPtrBytes)+import qualified Data.Layout.Language as L+import Data.Layout.Internal (Layout(..), ByteOrder(..))++------------------------------------------------------------------------+-- Vector Encoding Operations++-- | Abstracts over vectors of storable types to allow+-- calling `encodeVectors`. The `SV` constructor provides+-- proof that the `V.Vector` contains `Storable` elements.+data StorableVector where+ SV :: Storable a => V.Vector a -> StorableVector++-- | Creates a strict `ByteString` by interleaving multiple `V.Vector`s.+encodeVectors :: [(Codec, StorableVector)] -> ByteString+encodeVectors [] = B.empty+encodeVectors xs@((codec, SV vec):_) = unsafePerformIO $ do+ -- allocate memory for the bytestring+ bp <- mallocPlainForeignPtrBytes bstrBytes++ -- encode each vector+ mapM_ (go (DstPtr bp 0)) xs++ -- return the bytestring+ return (PS bp 0 bstrBytes)+ where+ bstrBytes = encodeReps codec vec * encodedSize codec++ go dp (c, SV v) = encodeVector c dp v++encodeVector :: forall a. Storable a => Codec -> DstPtr -> V.Vector a -> IO ()+encodeVector codec dstPtr vec = do+ -- check that the vector type matches the codec+ checkValueSize "encodeVector" codec vec++ -- get a pointer to the vector elements+ let vp = castForeignPtr (fst (V.unsafeToForeignPtr0 vec))++ -- encode the vector+ encode codec n dstPtr (SrcPtr vp 0)+ where+ n = encodeReps codec vec++encodeReps :: Storable a => Codec -> V.Vector a -> Int+encodeReps c v = repetitions "Vector" (vectorSize v) (decodedSize c)++------------------------------------------------------------------------+-- Vector Decoding Operations++-- | Creates a `V.Vector` by decoding a strict `ByteString`+decodeVector :: forall a. Storable a => Codec -> ByteString -> V.Vector a+decodeVector codec bstr@(PS bp bpOff _) = unsafePerformIO $ do+ -- check that the vector type matches the codec+ checkValueSize "decodeVector" codec (V.empty :: V.Vector a)++ -- allocate memory for the vector+ vp <- mallocPlainForeignPtrBytes vectorBytes++ -- decode the bytestring in to the vector+ decode codec n (DstPtr vp 0) (SrcPtr bp bpOff)++ -- return the vector+ return (V.unsafeFromForeignPtr0 (castForeignPtr vp) vectorElems)+ where+ n = repetitions "ByteString" (B.length bstr) (encodedSize codec)++ vectorBytes = n * decodedSize codec+ vectorElems = n * valueCount codec++------------------------------------------------------------------------+-- Vector Utils++vectorSize :: forall a. Storable a => V.Vector a -> Int+vectorSize v = V.length v * sizeOf (undefined :: a)++-- | Ensures the value size of the codec matches the vector type.+checkValueSize :: forall a m. (Storable a, Monad m)+ => String -> Codec -> V.Vector a -> m ()+checkValueSize fn codec _ =+ when (codecValueSize /= vectorElemSize) (error errorMsg)+ where+ codecValueSize = valueSize codec+ vectorElemSize = sizeOf (undefined :: a)++ errorMsg = concat+ [ "Data.Layout.Vector.", fn, ": "+ , "Value size mismatch. The value size of a codec ("+ , show codecValueSize, " bytes) did not match the size of "+ , "individual elements (", show vectorElemSize, " bytes) in "+ , "the corresponding vector. This means that the wrong type "+ , "of vector is being used for a given codec." ]++repetitions :: String -> Int -> Int -> Int+repetitions sourceName sourceBytes codecBytes =+ if leftover /= 0 then error msg else n+ where+ (n, leftover) = sourceBytes `quotRem` codecBytes++ msg = concat+ [ "Data.Layout.Vector.encodeReps: "+ , "The source ", sourceName, " is not a multiple of "+ , show codecBytes, " bytes, as required by the Codec. "+ , show sourceBytes, " bytes were provided, which leaves "+ , show leftover, " bytes unused." ]+++------------------------------------------------------------------------+-- Ptr Operations++data DstPtr = DstPtr {-# UNPACK #-} !(ForeignPtr Word8)+ {-# UNPACK #-} !Int++data SrcPtr = SrcPtr {-# UNPACK #-} !(ForeignPtr Word8)+ {-# UNPACK #-} !Int++-- | Contains the information required to encode or decode a+-- `V.Vector` from its arbitrary layout in a strict `ByteString`.+data Codec = Codec+ { encode :: Int -> DstPtr -> SrcPtr -> IO ()+ , decode :: Int -> DstPtr -> SrcPtr -> IO ()+ , encodedSize :: Int+ , decodedSize :: Int+ , valueCount :: Int+ , valueSize :: Int+ }++-- | Compiles a data layout in to a codec capable of encoding+-- and decoding data stored in the layout.+compile :: Layout -> Codec+compile layout = Codec+ { encode = runCodec copyInfo c_encode+ , decode = runCodec copyInfo c_decode+ , encodedSize = L.size layout+ , decodedSize = L.valueSizeN layout+ , valueCount = L.valueCount layout+ , valueSize = L.valueSize1 layout+ }+ where+ copyInfo = buildCopyInfo layout++runCodec :: CopyInfo -> CodecFn -> Int -> DstPtr -> SrcPtr -> IO ()+runCodec info c_codec_fn reps (DstPtr dstFP dstOff) (SrcPtr srcFP srcOff) =+ -- unbox foreign pointers for use+ withForeignPtr dstFP $ \dst0 -> do+ withForeignPtr srcFP $ \src0 -> do++ -- add the offset+ let dst = dst0 `plusPtr` dstOff+ src = src0 `plusPtr` srcOff++ -- get a pointer to the offsets+ V.unsafeWith (ciOffsets info) $ \offsets -> do++ -- decode the data+ err <- c_codec_fn+ dst src+ (fromIntegral reps)+ (ciNumOffsets info)+ offsets+ (ciNumValues info)+ (ciValueSize info)+ (ciSwapBytes info)++ -- check error code+ case err of+ 0 -> return ()+ 1 -> error ("runCodec: invalid value size: " ++ show (ciValueSize info))+ _ -> error "runCodec: unknown error"+++------------------------------------------------------------------------+-- CopyInfo++data CopyInfo = CopyInfo+ { ciOffsets :: V.Vector CInt+ , ciNumValues :: CInt+ , ciValueSize :: CInt+ , ciSwapBytes :: CInt+ } deriving (Show)++ciNumOffsets :: CopyInfo -> CInt+ciNumOffsets = fromIntegral . V.length . ciOffsets++type SkipCopyOp = CInt++-- | Build copy instructions for the specified layout.+buildCopyInfo :: Layout -> CopyInfo+buildCopyInfo layout =+ CopyInfo { ciOffsets, ciNumValues, ciValueSize, ciSwapBytes }+ where+ ciNumValues = copySize `quot` ciValueSize+ ciValueSize = fromIntegral (L.valueSize1 layout)+ ciSwapBytes = if needsByteSwap layout then 1 else 0++ (copySize, ciOffsets) = (splitOps . optimize . toSkipCopyOps) layout++ -- convert from layout to skip/copy operations+ toSkipCopyOps :: Layout -> V.Vector SkipCopyOp+ toSkipCopyOps = go+ where+ go v@(Value _) = V.singleton (copyOp (L.valueSize1 v))+ go (Offset n xs) = skipOp n `V.cons` go xs+ go (Repeat n xs) = V.concat (replicate n (go xs))+ go (Group n xs) = go xs `V.snoc` skipOp (n - L.size xs)++ -- positive number means skip 'n' bytes+ skipOp = fromIntegral++ -- negative number means copy 'n' bytes+ copyOp n = fromIntegral (-n)+++ -- squash copies and skips together, remove no-ops+ optimize :: V.Vector SkipCopyOp -> V.Vector SkipCopyOp+ optimize = skips+ where+ skips = sumWhile (> 0) copies+ copies = sumWhile (<= 0) skips++ sumWhile p k xs+ | V.null xs = V.empty+ | otherwise = let (ys, zs) = V.span p xs+ in case V.sum ys of+ 0 -> k zs+ s -> s `V.cons` k zs++ -- ensures first and last operations are skips or no-ops,+ -- then strips all copies out returns a tuple of the form+ -- (copy size, skip operations)+ splitOps :: V.Vector SkipCopyOp -> (CInt, V.Vector CInt)+ splitOps = split . head0 . last0+ where+ head0 xs | V.head xs < 0 = 0 `V.cons` xs+ | otherwise = xs++ last0 xs | V.last xs < 0 = xs `V.snoc` 0+ | otherwise = xs++ split :: V.Vector SkipCopyOp -> (CInt, V.Vector CInt)+ split xs = (-copyOp, V.filter isSkip xs)+ where+ copyOp = V.head (V.dropWhile (>= 0) xs)++ isSkip x | x == copyOp = False -- remove+ | x >= 0 = True -- keep+ | otherwise = error $+ "buildCopyInfo: invalid copy operation " +++ "(expected <" ++ show (-copyOp) ++ " bytes>," +++ " actual <" ++ show (-x) ++ " bytes>)"+++------------------------------------------------------------------------+-- Endian check++needsByteSwap :: Layout -> Bool+needsByteSwap x = case L.byteOrder x of+ NoByteOrder -> False+ LittleEndian -> hostIsBigEndian+ BigEndian -> hostIsLittleEndian++endianCheck :: Word8+endianCheck = unsafePerformIO $ alloca $ \p -> do+ poke p (0x01020304 :: Word32)+ peek (castPtr p :: Ptr Word8)++hostIsLittleEndian :: Bool+hostIsLittleEndian = endianCheck == 4++hostIsBigEndian :: Bool+hostIsBigEndian = endianCheck == 1+++------------------------------------------------------------------------+-- FFI++type CodecFn =+ Ptr Word8 -- ^ destination memory area+ -> Ptr Word8 -- ^ source memory area+ -> CInt -- ^ number of times to repeat the encode/decode+ -> CInt -- ^ number of skip operations in 'offsets'+ -> Ptr CInt -- ^ offset / skip list+ -> CInt -- ^ number of values to copy in between each skip+ -> CInt -- ^ size of a single value in bytes+ -> CInt -- ^ non-zero to swap the byte order of values+ -> IO CInt -- ^ zero on success, non-zero otherwise++foreign import ccall unsafe "data_layout_encode"+ c_encode :: CodecFn++foreign import ccall unsafe "data_layout_decode"+ c_decode :: CodecFn