2012-01-24 9 views
5

Ho un semplice pdf parser basato su attoparsec. Funziona bene fino a quando non viene utilizzato con iteratee. Quando la dimensione dell'ingresso supera la dimensione del buffer.attoparsec-iteratee non funziona quando l'input è maggiore della dimensione del buffer

import qualified Data.ByteString as BS 
import qualified Data.Iteratee as I 
import qualified Data.Attoparsec as P 
import qualified Data.Attoparsec.Iteratee as P 
import System.Environment (getArgs) 
import Control.Monad 

import Pdf.Parser.Value 

main :: IO() 
main = do 
    [i] <- getArgs 
    liftM (P.parseOnly parseValue) (BS.readFile i) >>= print -- works 
    I.fileDriverRandomVBuf 2048 (P.parserToIteratee parseValue) i >>= print -- works 
    I.fileDriverRandomVBuf 1024 (P.parserToIteratee parseValue) i >>= print -- DOES NOT works!!! 

ingresso:

<< /Annots [ 404 0 R 547 0 R ] /ArtBox [ 0.000000 0.000000 612.000000 792.000000 ] /BleedBox [ 0.000000 0.000000 612.000000 792.000000 ] /Contents [ 435 0 R 436 0 R 437 0 R 444 0 R 448 0 R 449 0 R 450 0 R 453 0 R ] /CropBox [ 0.000000 0.000000 612.000000 792.000000 ] /Group 544 0 R /MediaBox [ 0.000000 0.000000 612.000000 792.000000 ] /Parent 239 0 R /Resources << /ColorSpace << /CS0 427 0 R /CS1 427 0 R /CS2 428 0 R >> /ExtGState << /GS0 430 0 R /GS1 431 0 R /GS2 469 0 R /GS3 475 0 R /GS4 439 0 R /GS5 480 0 R /GS6 485 0 R /GS7 491 0 R /GS8 497 0 R >> /Font << /C2_0 447 0 R /T1_0 421 0 R /T1_1 422 0 R /T1_2 423 0 R /T1_3 424 0 R /T1_4 425 0 R /T1_5 426 0 R /T1_6 438 0 R >> /ProcSet [ /PDF /Text /ImageC /ImageI ] /Properties << /MC0 << /Metadata 502 0 R >> >> /XObject << /Fm0 451 0 R /Fm1 504 0 R /Fm2 513 0 R /Fm3 515 0 R /Fm4 517 0 R /Fm5 526 0 R /Fm6 528 0 R /Fm7 537 0 R /Fm8 539 0 R /Im0 540 0 R /Im1 541 0 R /Im2 452 0 R /Im3 542 0 R /Im4 543 0 R >> >> /Rotate 0 /StructParents 1 /TrimBox [ 0.000000 0.000000 612.000000 792.000000 ] /Type /Page >> 

Così, il parser funziona senza iteratee, lavora con grandi blocchi abbastanza, ma non funziona con i blocchi più piccoli. Bug in iteratee? In attoparsec-iteratee? Nel mio codice? C'è qualche soluzione? È un problema davvero urgente per me.

Grazie.

+0

Non ho idea di dove sia l'errore, ma è possibile utilizzare solo una dimensione di blocco abbastanza grande? O usare 'ByteString's invece di' Iteratees'? –

+0

Il valore del pdf può essere arbitrario a lungo, quindi non ci sono grandi dimensioni del blocco. Re ByteString: intendi pigro IO? Pdf richiede un accesso casuale, e la tabella di riferimento si trova di solito alla fine del file. Quindi IO pigro ~ = "rigoroso" in questo caso particolare e userà la memoria in modo inefficiente. – Yuras

+0

Do Iteratee consente l'accesso casuale? Non ne ho sentito parlare (non significa niente, non sono un utente). Se hai bisogno dell'accesso casuale, puoi leggere l'intero file in una volta o disporre di un'impalcatura per cercare e leggere parti del file. Se possibile, la prima opzione è ** molto ** più semplice. –

risposta

2

Edit 2: Ho creato un nuovo parser in Pdf/Parser/Valore

dictOrStream :: Parser PdfValue 
dictOrStream = do 
    dict <- parseDict 
    P.skipSpace 
    let s1 = do 
      P.string $ fromString "stream" 
      content <- P.manyTill P.anyWord8 $ P.endOfLine >> P.string (fromString "endstream") 
      return $ PdfValStream (PdfStream dict (BS.pack content)) 
    s1 <|> return (PdfValDict dict) 

poi utilizzato questo parser in parseValue. Questo funziona per tutti i tuoi casi. Non so perché choice non riesca a tornare indietro correttamente, forse un bug attoparsec?

Modifica: noto che, se sostituisco il livello superiore parseValue con parseDict, funziona. Funziona anche se rimuovo parseStream dalle scelte in parseValue. Penso che attoparsec si sia impegnato a "parseStream" dopo il completamento del dizionario di primo livello, quindi si aspetta più input (uno spazio, il token "stream", ecc.) Che porta a questo errore. A questo punto c'è un'ambiguità tra queste due opzioni di analisi che dovrai risolvere. Non so perché funzioni correttamente quando l'intero input è disponibile; Mi aspetto che venga segnalato un errore come quando il parser viene alimentato in blocchi.

A partire da ora, ho il sospetto di un bug nel codice, o forse in un attoparsec. Ho eseguito il seguente test con la lettura manualmente pezzi bytestring e l'alimentazione al vostro parser attoparsec:

*Main System.IO> h <- openFile "test.pdf" ReadMode 
*Main System.IO Data.ByteString> let hget = hGetSome h 1024 
*Main System.IO Data.ByteString> b <- hget 
*Main System.IO Data.ByteString> let r = P.parse parseValue b 
*Main System.IO Data.ByteString> r 
Partial _ 
*Main System.IO Data.ByteString> b <- hget 
*Main System.IO Data.ByteString> let r' = P.feed r b 
*Main System.IO Data.ByteString> r' 
Partial _ 
*Main System.IO Data.ByteString> b <- hget 
*Main System.IO Data.ByteString> Data.ByteString.length b 
0 
*Main System.IO Data.ByteString> let r'2 = P.feed r' b 
*Main System.IO Data.ByteString> r'2 
Fail "<< /Annots [ 404 0 R 547 0 R ] /ArtBox [ 0.000000 0.000000 612.000000 792.000000 ] /BleedBox [ 0.000000 0.000000 612.000000 792.000000 ] /Contents [ 435 0 R 436 0 R 437 0 R 444 0 R 448 0 R 449 0 R 450 0 R 453 0 R ] /CropBox [ 0.000000 0.000000 612.000000 792.000000 ] /Group 544 0 R /MediaBox [ 0.000000 0.000000 612.000000 792.000000 ] /Parent 239 0 R /Resources << /ColorSpace << /CS0 427 0 R /CS1 427 0 R /CS2 428 0 R >> /ExtGState << /GS0 430 0 R /GS1 431 0 R /GS2 469 0 R /GS3 475 0 R /GS4 439 0 R /GS5 480 0 R /GS6 485 0 R /GS7 491 0 R /GS8 497 0 R >> /Font << /C2_0 447 0 R /T1_0 421 0 R /T1_1 422 0 R /T1_2 423 0 R /T1_3 424 0 R /T1_4 425 0 R /T1_5 426 0 R /T1_6 438 0 R >> /ProcSet [ /PDF /Text /ImageC /ImageI ] /Properties << /MC0 << /Metadata 502 0 R >> >> /XObject << /Fm0 451 0 R /Fm1 504 0 R /Fm2 513 0 R /Fm3 515 0 R /Fm4 517 0 R /Fm5 526 0 R /Fm6 528 0 R /Fm7 537 0 R /Fm8 539 0 R /Im0 540 0 R /Im1 541 0 R /Im2 452 0 R /Im3 542 0 R /Im4 543 0 R >> >> /Rotate 0 /StructParents 1 /TrimBox [ 0.000000 0.000000" [] "Failed reading: empty" 

Per qualche ragione, il parser non sembra gradire la ricezione dei dati in blocchi, e non riesce dopo aver ricevuto il terzo (vuoto chunk senza consumare alcun input. Non ho ancora capito dove il tuo parser sta andando male, ma non è sicuramente iteratee o attoparsec-iteratee.

+0

Hai ragione, sembra che sia l'iterate che l'attoparsec-iteratee non abbiano nulla a che fare con quello. ty, John – Yuras

+0

Potresti spiegare perché è ambiguo? Prevedo che 'parserDict' fallirà se non trovi" stream ", e' choice' proverà la prossima opzione - 'parseDict'. – Yuras

+0

scusate, voglio dire che 'parseStream' fallirà se non trovate" stream " – Yuras