module Data.ByteString.Delta (
diff,
patch,
) where
import Data.ByteString (ByteString, packCStringLen)
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import Data.Int
import Data.Word
import Foreign (Ptr, alloca, peek)
import Foreign.C.String (CString, peekCAString)
import Foreign.C.Types
#if MIN_VERSION_base(4,4,0)
import Foreign.Marshal.Unsafe (unsafeLocalState)
#else
import System.IO.Unsafe (unsafePerformIO)
unsafeLocalState :: IO a -> a
unsafeLocalState = unsafePerformIO
#endif
type BDELTAcode = Int32
type BDeltaFunc = Ptr CChar -> CSize
-> Ptr CChar -> CSize
-> Ptr (Ptr CChar) -> Ptr CSize
-> IO BDELTAcode
foreign import ccall safe "bdelta.h bdelta_diff"
bdelta_diff :: BDeltaFunc
foreign import ccall safe "bdelta.h bdelta_patch"
bdelta_patch :: BDeltaFunc
foreign import ccall unsafe "bdelta.h bdelta_strerror"
bdelta_strerror :: BDELTAcode -> IO CString
foreign import ccall unsafe "stdlib.h free"
free :: Ptr a -> IO ()
callBDeltaFunc :: BDeltaFunc -> ByteString -> ByteString -> Either BDELTAcode ByteString
callBDeltaFunc func old new =
unsafeLocalState $
unsafeUseAsCStringLen old $ \(oldPtr, oldSize) ->
unsafeUseAsCStringLen new $ \(newPtr, newSize) ->
alloca $ \diffPtrPtr ->
alloca $ \diffSizePtr ->
do
rc <- func oldPtr (fromIntegral oldSize)
newPtr (fromIntegral newSize)
diffPtrPtr diffSizePtr
case rc of
0 -> do
diffPtr <- peek diffPtrPtr
diffSize <- peek diffSizePtr
result <- packCStringLen (diffPtr, fromIntegral diffSize)
free diffPtr
return $ Right result
_ -> return $ Left rc
strerror :: BDELTAcode -> String
strerror code = unsafeLocalState $ bdelta_strerror code >>= peekCAString
diff :: ByteString -> ByteString -> ByteString
diff old new =
case callBDeltaFunc bdelta_diff old new of
Right result -> result
Left errcode -> error $ "Data.ByteString.Delta.diff: " ++ strerror errcode
patch :: ByteString -> ByteString -> Either String ByteString
patch old patch_ =
case callBDeltaFunc bdelta_patch old patch_ of
Right result -> Right result
Left (rc @ 2) -> Left $ strerror rc
Left (rc @ 3) -> Left $ strerror rc
Left rc -> error $ "Data.ByteString.Delta.patch: " ++ strerror rc