Próbuję parsować dane binarne za pomocą pipe-attoparsec w Haskell. Przyczyną są potoki przewodów (serwery proxy) polegające na przeplataniu odczytu z analizą, aby uniknąć dużego wykorzystania pamięci w przypadku dużych plików. Wiele formatów binarnych opiera się na blokach (lub porcjach), a ich rozmiary są często opisywane przez pole w pliku. Nie jestem pewien, co nazywa się analizatorem składni takiego bloku, ale właśnie to mam na myśli przez "podseparator" w tytule. Problem polega na tym, że wdrażam je w zwięzły sposób, bez potencjalnie dużego śladu pamięci. Wymyśliłem dwie alternatywy, z których każda zawiodła pod pewnym względem."Podseparatory" w pipe-attoparsec
Alternatywa 1 polega na przeczytaniu bloku w oddzielnym teście bytestring i uruchomieniu oddzielnego analizatora składni. Choć zwięzły, duży blok spowoduje wysokie zużycie pamięci.
Alternatywa 2 to parsowanie w tym samym kontekście i śledzenie liczby zużytych bajtów. To śledzenie jest podatne na błędy i zdaje się atakować wszystkie analizatory składające się na końcowy obiekt blockParser. W przypadku zniekształconego pliku wejściowego może on również tracić czas, parsując dalej niż wskazuje pole rozmiaru, zanim można porównać śledzony rozmiar.
import Control.Proxy.Attoparsec
import Control.Proxy.Trans.Either
import Data.Attoparsec as P
import Data.Attoparsec.Binary
import qualified Data.ByteString as BS
parser = do
size <- fromIntegral <$> anyWord32le
-- alternative 1 (ignore the Either for simplicity):
Right result <- parseOnly blockParser <$> P.take size
return result
-- alternative 2
(result, trackedSize) <- blockparser
when (size /= trackedSize) $ fail "size mismatch"
return result
blockParser = undefined
main = withBinaryFile "bin" ReadMode go where
go h = fmap print . runProxy . runEitherK $ session h
session h = printD <-< parserD parser <-< throwParsingErrors <-< parserInputD <-< readChunk h 128
readChunk h n() = runIdentityP go where
go = do
c <- lift $ BS.hGet h n
unless (BS.null c) $ respond c *> go
Wstawianie fajki, która zlicza dźwięki w górę strumienia, jest interesujące, ale skąd będzie wiedzieć, ile bajtów należy policzyć? Ta wartość jest odkrywana tylko przez analizator składni, który nie może wywołać żądania bezpośrednio z wartością jako parametrem, ponieważ jest uruchamiany przez parserD. – absence
@absence Cóż, zignoruj interfejs pipe-attoparsec na razie, ponieważ Renzo i ja to naprawimy wkrótce. Stały parser wejściowy używa wewnętrznie potoku, który ogranicza liczbę bajtów. Pomyśl o tym w następujący sposób: 'parser1 >> (restrict n> -> parser2) >> parser3'. Kombinatoryczna wstawka o stałej szerokości coś w rodzaju 'ograniczenia' przed tym danym parserem. Jest to bardziej skomplikowane, ale dość podobne w duchu. –
Łącza są martwe – SwiftsNamesake