Problems decoding a file strictly using Binary in Haskell

I am trying to read and decode the binary strictly, which seems to work most of the time. But, unfortunately, in some cases, my program does not work with

"too few bytes. Could not read at byte position 1"

I think the binary function of decoding it thinks that there is no data available, but I know that there is and just reloading the program works fine.

I tried several solutions, but none of them could solve my problem :(

  • using withBinaryFile:

    decodeFile' path = withBinaryFile path ReadMode doDecode
      where
        doDecode h = do c <- LBS.hGetContents h
                        return $! decode c
    
  • reading the entire file with the ByteString string and decoding from it:

    decodeFile' path = decode . LBS.fromChunks . return <$> BS.readFile path
    
  • adding more rigor

    decodeFile' path = fmap (decode . LBS.fromChunks . return) $! BS.readFile path
    

Any ideas what is going on here and how to solve the problem?

Thank!

EDIT: , . . , , , . , , , , "Binary.encodeFile" ( "", , ).

EDIT - , POSIX IO File Locks. .

- , , - /, .

:

safeEncodeFile path value = do
    fd <- openFd path WriteOnly (Just 0o600) (defaultFileFlags {trunc = True})
    waitToSetLock fd (WriteLock, AbsoluteSeek, 0, 0)
    let cs = encode value
    let outFn = LBS.foldrChunks (\c rest -> writeChunk fd c >> rest) (return ()) cs
    outFn
    closeFd fd
  where
    writeChunk fd bs = unsafeUseAsCString bs $ \ptr ->
                         fdWriteBuf fd (castPtr ptr) (fromIntegral $ BS.length bs)

:

safeDecodeFile def path = do
    e <- doesFileExist path
    if e
      then do fd <- openFd path ReadOnly Nothing
                           (defaultFileFlags{nonBlock=True})
              waitToSetLock fd (ReadLock, AbsoluteSeek, 0, 0)
              c  <- fdGetContents fd
              let !v = decode $! c
              return v
      else return def

fdGetContents fd = lazyRead
  where
    lazyRead = unsafeInterleaveIO loop

    loop = do blk <- readBlock fd
              case blk of
                Nothing -> return LBS.Empty
                Just c  -> do cs <- lazyRead
                              return (LBS.Chunk c cs)

readBlock fd = do buf <- mallocBytes 4096
                  readSize <- fdReadBuf fd buf 4096
                  if readSize == 0
                    then do free buf
                            closeFd fd
                            return Nothing
                    else do bs <- unsafePackCStringFinalizer buf
                                         (fromIntegral readSize)
                                         (free buf)
                            return $ Just bs

:

import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Lazy.Internal as LBS
+3
1

, , . , , /, / . , , .

import Data.Trie as T
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.Binary
import System.IO

tmp = "blah"

main = do
    let trie = T.fromList    [(B.pack [p], p) | p <- [0..]]
    (file,hdl) <- openTempFile "/tmp" tmp
    B.hPutStr hdl (B.concat $ L.toChunks $ encode trie)
    hClose hdl
    putStrLn file
    t <- B.readFile file
    let trie' = decode (L.fromChunks [t])
    print (trie' == trie)
+1

Source: https://habr.com/ru/post/1756432/


All Articles