2013-02-17 12 views
5

Powiedzmy mam następujące rodzaje:Jak bezboleśnie serializować Little-endian PODs?

data WaveFormatChunk = WaveFormatChunk { 
    compression :: Word16, 
    channels :: Word16, 
    sampleRate :: Word32, 
    averageBps :: Word32, 
    blockAlign :: Word16, 
    significantBits :: Word16 
    } deriving (Show) 

Czy istnieje sposób, aby po prostu zrzucić to wszystko na ByteString (lub podobna konstrukcja) hurtowo (w sposób elemencie Ye Olde C)? Jeśli nie i muszę napisać funkcję, która osobno umieszcza je wszystkie na liście, czy istnieją co najmniej funkcje, które ułatwiają przyklejenie wartości do, na przykład, listy Word8? Coś w stylu putWordBBxe, z wyjątkiem ciągów bajtów lub list (chociaż najprawdopodobniej bardzo się mylę, ponieważ nie czytałem jeszcze w Monadach, wydaje mi się, że Get/Put są najczęściej używane ze strumieniami).

Data.Binary nie jest tym, czego szukam, wydaje się bardziej przydatny do zrzucania danych na dysk niż do przechowywania go w określonym formacie z konkretną (i "złą") endianowością.

+1

Pisanie instancji ['Storable'] (http://hackage.haskell.org/packages/archive/base/latest/doc/html/Foreign-haskell.org/packages/archive/base/latest/doc/html/Foreign-Storable.html) dla' WaveFormatChunk' jest całkiem proste. To pozwoli ci pisać ('poke') i czytać (' peek') 'WaveFormatChunk's do iz lokalizacji w pamięci (tak jakby były to stare C-y). Można je następnie przekształcić w 'ByteString's, jeśli sobie tego życzysz. Nie wiem, czy to jest dokładnie to, czego szukasz, ale mogę je rozwinąć, jeśli chcesz. Istnieje również [pakiet binarny] (http://hackage.haskell.org/package/binary) do serializacji. – gspr

+0

... oh, widzę, że moja edycja o pakiecie binarnym była całkiem bezwartościowa, ponieważ już o tym wiesz :-) – gspr

+1

Schludny to zdecydowanie droga. Nie zbliżysz się do C, zarówno pod względem stylu realizacji, jak i wydajności. [Oto przykład] (http://hpaste.org/76299). –

Odpowiedz

12

Data.Binary pozwoli ci serializować strukturę do bytestringu, używając jawnie little-endian operators.

{-# OPTIONS_GHC -funbox-strict-fields #-} 
{-# LANGUAGE RecordWildCards #-} 

import Data.Binary 
import Data.Binary.Put 

import qualified Data.ByteString.Char8 as C 
import qualified Data.ByteString.Lazy as L 

data WaveFormatChunk = 
        WaveFormatChunk {  
            compression     :: !Word16, 
            channels        :: !Word16, 
            sampleRate      :: !Word32, 
            averageBps      :: !Word32, 
            blockAlign      :: !Word16, 
            significantBits :: !Word16 
        }  

instance Binary WaveFormatChunk where 
    put (WaveFormatChunk{..}) = do 
        putWord16le compression  
        putWord16le channels 
        putWord32le sampleRate 
        putWord32le averageBps 
        putWord16le blockAlign 
        putWord16le significantBits 

    get = undefined 

main = C.putStr $ C.concat $ L.toChunks $ encode test 
  where 
    test = WaveFormatChunk { 
      compression  = 0xcafe 
      , channels  = 0xface 
      , sampleRate  = 0xdeadbeef 
      , averageBps  = 0xf01dab1e 
      , blockAlign  = 0x5566 
      , significantBits = 0xb01d 
          } 

przyniesie:

$ ./A | od -x 
0000000 cafe face beef dead ab1e f01d 5566 b01d 

Więc trzeba precyzyjną kontrolę bajt szczebla reprezentacji. Możesz również uzyskać ten sam efekt z pakietu płatków, jeśli nie interesuje Cię transmisja strumieniowa.

+0

Dzięki - pewnie nieobligacyjna kwestia, ale ja tylko słabo znam deklaracje ścisłości i cały jazz, który z nimi jest ... czy rzeczywiście tam trzeba? – jaymmer

+6

Są higieniczne - nie potrzebujesz tutaj lenistwa, lenistwo na typach atomowych jest prawie zawsze błędne - więc określ, że powinno być surowe. –

+1

Zauważ, że pakiet binarny obsługuje teraz także streaming, nie musisz już wybierać między binarnymi i zbożowymi. –

4

Istnieje jeszcze inne, zupełnie odmienne podejście. Zamiast takiej struktury można zdefiniować ByteString Wrapper:

import Data.ByteString (ByteString) 

newtype WaveFormatChunk = 
    WaveFormatChunk { 
     getWaveFormatChunk :: ByteString 
    } 

Pisanie to do pliku jest prosta. Aby zmodyfikować taką strukturę można używać obiektywów:

data Compression = {- ... -} 

compression :: Lens' WaveFormatChunk Compression 

lub jeśli wolisz:

compression :: Lens' WaveFormatChunk Word16 

Soczewki działają jak bezpiecznych tłumaczy poszczególnych grup bajtowych. Istnieją jednak trzy problemy: Przede wszystkim powinieneś użyć ram testowych dla tego, ponieważ łatwo jest złapać soczewki źle. Po drugie każda zmiana wymaga nowej kopii ByteString. W zależności od tego, co robisz, może to być wolniejsze lub szybsze niż oryginalne podejście.

Moja osobista rekomendacja to korzystanie z typowego dla Haskella typu danych i stosowanie odpowiedniej serializacji. Jak zauważyli inni, instancje są dość łatwe do napisania.