2016-02-24 8 views
6

Ho un ByteString b con una lunghezza di forma 4 * n (con n intero) e si desidera utilizzare map con una funzione di f::Word32->Word32 su b (in modo che f viene applicata a "b[0..3]" , "b[4..7]", ecc.). Come può essere fatto in modo efficiente (ed elegante)?applicare una funzione Word32 ad un ByteString

+0

I tuoi dati sono binari o leggibili? Se il byte 4 byte '0x31343239' viene interpretato come '1429',' 9241' o binario 'Word32' 825,504,313? – Zeta

risposta

4

È possibile pezzo fino il ByteString in modo molto efficiente, dato che B.take e B.drop sono entrambi O(1) operazioni:

import Data.ByteString (ByteString) 
import qualified Data.ByteString as B 

chunk :: Int -> ByteString -> [ByteString] 
chunk k = takeWhile (not . B.null) . map (B.take k) . iterate (B.drop k) 

poi:

\> :set -XOverloadedStrings 
\> chunk 4 "abcdefghijkl" 
["abcd","efgh","ijkl"] 

il resto sarebbe quello di mappare sulla lista la conversione da e verso il tipo desiderato e una singola chiamata a B.concat alla fine.

Una possibile fromByteString potrebbero essere attuate mediante turni bit e piega a sinistra:

import Data.Bits (Bits, shiftL, (.|.)) 

fromByteString :: (Num a, Bits a) => ByteString -> a 
fromByteString = B.foldl go 0 
    where go acc i = (acc `shiftL` 8) .|. (fromIntegral i) 

quindi: (! Che non richiede una copia)

\> map fromByteString $ chunk 4 "abcdefghijkl" :: [Word32] 
[1633837924,1701209960,1768581996] 
+1

Ohhh, sembra molto buono :)! Grazie mille!!! – dmw64

+1

Non sono sicuro che 'ByteString.Conversion' fa la cosa giusta. Analizza i numeri come 'Char8' con attoparsec. – Zeta

+1

Se l'obiettivo finale è qualcosa di tipo 'ByteString -> ByteString', il passaggio di riassemblaggio dai blocchi di 4 byte non terminerà le prestazioni? – Cactus

4

Un modo hacker ma efficiente è quello di convert to a storable vector , mappare sopra e riconvertire:

import Data.Vector.Storable.ByteString 
import qualified Data.Vector.Storable as VS 
import qualified Data.ByteString as BS 

mapBSChunks :: (VS.Storable a, VS.Storable b) 
       => (a->b) -> BS.ByteString -> BS.ByteString 
mapBSChunks f = vectorToByteString . VS.map f . byteStringToVector 

come da Michael's comment, si può facilmente definire le funzioni di conversione hacker a livello locale:

bytestringToVector bs = runST 
    (V.unsafeThaw (toByteVector bs) >>= V.unsafeFreeze . M.unsafeCast) 
vectorToByteString v = runST 
    (V.unsafeThaw v >>= fmap fromByteVector . V.unsafeFreeze . M.unsafeCast) 

anche se preferirei dipendo da una biblioteca per fornire questo, in particolare perché il casting non sicuro è un po 'di pesce.

+0

* "A seconda di questa libreria dovrebbe essere visto come un modo per documentare e standardizzare un hack esistente, e non come una garanzia assoluta di comportamento corretto." * Dato che la libreria è del 2011 e non ha una versione in LTS di Stack, funziona ancora come previsto? – Zeta

+0

Anch'io ho avuto i miei dubbi, ma sembra funzionare bene. Forse qualcuno dovrebbe trasferire tali conversioni in un pacchetto ben mantenuto. – leftaroundabout

+0

Penso che Monotraversable's ['Data.ByteVector'] (http://hackage.haskell.org/package/mono-traversable-0.10.1/docs/Data-ByteVector.html) sia la cosa più visibile come questa - almeno se preso insieme a ['unssafeCast'] (http://hackage.haskell.org/package/vector-0.11.0.0/docs/src/Data-Vector-Storable-Mutable.html#unsafeCast) Forse è possibile eseguire operazioni monotraversibili con un poche altre funzioni come questa. – Michael

Problemi correlati