summaryrefslogtreecommitdiff
path: root/haskell/Data/XDelta.hsc
diff options
context:
space:
mode:
Diffstat (limited to 'haskell/Data/XDelta.hsc')
-rw-r--r--haskell/Data/XDelta.hsc97
1 files changed, 69 insertions, 28 deletions
diff --git a/haskell/Data/XDelta.hsc b/haskell/Data/XDelta.hsc
index 4d9ffe2..4ebdd51 100644
--- a/haskell/Data/XDelta.hsc
+++ b/haskell/Data/XDelta.hsc
@@ -5,6 +5,8 @@
5{-# LANGUAGE NondecreasingIndentation #-} 5{-# LANGUAGE NondecreasingIndentation #-}
6{-# LANGUAGE PatternSynonyms #-} 6{-# LANGUAGE PatternSynonyms #-}
7{-# LANGUAGE RankNTypes #-} 7{-# LANGUAGE RankNTypes #-}
8{-# LANGUAGE FlexibleInstances #-}
9{-# LANGUAGE DeriveFunctor #-}
8module Data.XDelta where 10module Data.XDelta where
9 11
10import Control.Monad 12import Control.Monad
@@ -18,6 +20,7 @@ import qualified Data.ByteString.Unsafe as B
18import qualified Data.ByteString.Internal as B 20import qualified Data.ByteString.Internal as B
19import qualified Data.ByteString.Lazy as L 21import qualified Data.ByteString.Lazy as L
20import Data.Coerce 22import Data.Coerce
23import Data.Int
21import qualified Data.IntMap as IntMap 24import qualified Data.IntMap as IntMap
22import Data.Monoid 25import Data.Monoid
23import Data.Primitive.Addr 26import Data.Primitive.Addr
@@ -27,6 +30,7 @@ import Data.STRef
27import qualified Data.Text as T 30import qualified Data.Text as T
28import Data.Text.Encoding 31import Data.Text.Encoding
29import Data.Word 32import Data.Word
33import Debug.Trace
30import Foreign.C.Types 34import Foreign.C.Types
31import Foreign.C.String 35import Foreign.C.String
32import Foreign.ForeignPtr (withForeignPtr) 36import Foreign.ForeignPtr (withForeignPtr)
@@ -205,23 +209,27 @@ setFlush = setFlag XD3_FLUSH
205setSkipWindow :: PrimMonad m => Stream m -> Bool -> m () 209setSkipWindow :: PrimMonad m => Stream m -> Bool -> m ()
206setSkipWindow = setFlag XD3_SKIP_WINDOW 210setSkipWindow = setFlag XD3_SKIP_WINDOW
207 211
208foreign import ccall "xdelta3.h xd3_avail_input" xd3_avail_input :: Ptr Xd3Stream -> Ptr a -> Usize_t -> IO () 212-- -- declared static
213-- foreign import ccall "xdelta3.h xd3_avail_input" xd3_avail_input :: Ptr Xd3Stream -> Ptr a -> Usize_t -> IO ()
209 214
210avail_input :: PrimMonad m => Stream m -> Ptr a -> Usize_t -> m () 215avail_input :: PrimMonad m => Stream m -> Ptr a -> Usize_t -> m ()
211avail_input stream p sz = 216avail_input stream p sz = do
212 unsafeIOToPrim $ xd3_avail_input (ptr $ mutableByteArrayContents $ streamArray stream) p sz 217 writeAtByte (streamArray stream) #{offset xd3_stream, next_in} p
218 writeAtByte (streamArray stream) #{offset xd3_stream, avail_in} sz
213 219
214-- | This acknowledges receipt of output data, must be called after any 220-- | This acknowledges receipt of output data, must be called after any
215-- XD3_OUTPUT return. 221-- XD3_OUTPUT return.
216foreign import ccall "xdelta3.h xd3_consume_output" xd3_consume_output :: Ptr Xd3Stream -> IO () 222-- -- declared static
223-- foreign import ccall "xdelta3.h xd3_consume_output" xd3_consume_output :: Ptr Xd3Stream -> IO ()
217 224
218nextOut :: PrimMonad m => Stream m -> ((Ptr Word8, Int) -> m a) -> m a 225nextOut :: PrimMonad m => Stream m -> ((Ptr Word8, Usize_t) -> m a) -> m a
219nextOut stream action = do 226nextOut stream action = do
220 buf <- (,) 227 buf <- (,)
221 <$> readAtByte (streamArray stream) #{offset xd3_stream, next_out} 228 <$> readAtByte (streamArray stream) #{offset xd3_stream, next_out}
222 <*> readAtByte (streamArray stream) #{offset xd3_stream, avail_out} 229 <*> readAtByte (streamArray stream) #{offset xd3_stream, avail_out}
223 a <- action buf 230 a <- action buf
224 unsafeIOToPrim $ xd3_consume_output (ptr $ mutableByteArrayContents $ streamArray stream) 231 -- unsafeIOToPrim $ xd3_consume_output (ptr $ mutableByteArrayContents $ streamArray stream)
232 writeAtByte (streamArray stream) #{offset xd3_stream, avail_out} (0 :: Usize_t)
225 return a 233 return a
226 234
227 235
@@ -236,14 +244,17 @@ data CurrentBlock = CurrentBlock
236 , blkPtr :: !(Ptr Word8) -- ^ current block array 244 , blkPtr :: !(Ptr Word8) -- ^ current block array
237 } 245 }
238 246
239foreign import ccall "xdelta3.h xd3_errstring" xd3_errstring :: Ptr Xd3Stream -> IO CString 247-- -- declared static
248-- foreign import ccall "xdelta3.h xd3_errstring" xd3_errstring :: Ptr Xd3Stream -> IO CString
240 249
241-- | Gives some extra information about the latest library error, if any 250-- | Gives some extra information about the latest library error, if any
242-- is known. 251-- is known.
243errorString :: PrimMonad m => Stream m -> m String 252errorString :: PrimMonad m => Stream m -> m String
244errorString stream = unsafeIOToPrim $ do 253errorString stream = do
245 cstring <- xd3_errstring (ptr $ mutableByteArrayContents $ streamArray stream) 254 cstring <- readAtByte (streamArray stream) #offset xd3_stream, msg
246 peekCString cstring 255 if cstring /= nullPtr
256 then unsafeIOToPrim $ peekCString cstring -- TODO: avoid unsafeIOToPrim
257 else return ""
247 258
248pokeCurrentBlock :: PrimMonad m => Stream m -> CurrentBlock -> m () 259pokeCurrentBlock :: PrimMonad m => Stream m -> CurrentBlock -> m ()
249pokeCurrentBlock stream (CurrentBlock no sz ptr) = do 260pokeCurrentBlock stream (CurrentBlock no sz ptr) = do
@@ -279,14 +290,18 @@ xdelta x xxcode_input ds = do
279 case ret of 290 case ret of
280 XD3_INPUT -> if (not eof) then go withBlk ds else return mempty 291 XD3_INPUT -> if (not eof) then go withBlk ds else return mempty
281 XD3_OUTPUT -> do 292 XD3_OUTPUT -> do
282 m' <- nextOut stream (uncurry $ xOutput x) 293 m' <- nextOut stream (\(p,len) -> xOutput x p (fromIntegral len))
283 ms <- xInterleave x $ go2 withBlk eof ds 294 ms <- xInterleave x $ undefined -- go2 withBlk eof ds
284 return $ m' <> ms 295 return $ trace "chunk" m' <> ms
296 -- XXX: This output is to test for laziness.
285 XD3_GETSRCBLK -> do 297 XD3_GETSRCBLK -> do
286 Just n <- requestedBlockNumber stream 298 Just n <- requestedBlockNumber stream
287 let blk = xGetSource x n 299 let blk = xGetSource x n
288 withBlk' act = withByteString blk $ \p len -> do 300 withBlk' act = withByteString blk $ \p len -> do
289 pokeCurrentBlock stream $ CurrentBlock n len p 301 pokeCurrentBlock stream $ CurrentBlock n len p
302 when (len < xBlockSize x) $ do
303 Just src <- readMutVar $ streamSource stream
304 writeAtByte src #{offset xd3_source, eof_known} (1 :: #{type int})
290 act 305 act
291 go2 withBlk' eof ds 306 go2 withBlk' eof ds
292 XD3_GOTHEADER -> go2 withBlk eof ds -- No 307 XD3_GOTHEADER -> go2 withBlk eof ds -- No
@@ -314,29 +329,55 @@ encode_input stream =
314 unsafeIOToPrim $ xd3_encode_input (ptr $ mutableByteArrayContents $ streamArray stream) 329 unsafeIOToPrim $ xd3_encode_input (ptr $ mutableByteArrayContents $ streamArray stream)
315 330
316newtype XDelta = XDelta L.ByteString 331newtype XDelta = XDelta L.ByteString
332 deriving Show
317 333
318chunksOf :: Usize_t -> L.ByteString -> [B.ByteString] 334chunksOf :: Usize_t -> L.ByteString -> [B.ByteString]
319chunksOf len bs | L.null bs = [] 335chunksOf len bs | L.null bs = []
320 | otherwise = let (b,bs') = L.splitAt (fromIntegral len) bs 336 | otherwise = let (b,bs') = L.splitAt (fromIntegral len) bs
321 in L.toStrict b : chunksOf len bs' 337 in L.toStrict b : chunksOf len bs'
322 338
323computeDiff :: Config -> L.ByteString -> L.ByteString -> XDelta 339computeDiff :: Config -> L.ByteString -> L.ByteString -> XDeltaFailable XDelta
324computeDiff cfg source patched = XDelta $ xdeltaPure cfg source patched 340computeDiff cfg source patched = fmap XDelta $ xdeltaPure encode_input cfg source patched
325 341
326applyPatch :: Config -> L.ByteString -> XDelta -> L.ByteString 342applyPatch :: Config -> L.ByteString -> XDelta -> XDeltaFailable L.ByteString
327applyPatch cfg source (XDelta delta) = xdeltaPure cfg source delta 343applyPatch cfg source (XDelta delta) = xdeltaPure decode_input cfg source delta
328 344
329xdeltaPure :: Config -> L.ByteString -> L.ByteString -> L.ByteString 345data XDeltaFailable x = XError ErrorCode String
330xdeltaPure cfg source delta = 346 | XSuccess x
331 let smap = IntMap.fromList $ zip [0..] (chunksOf 4096 source) 347 deriving (Show,Functor)
332 x :: XDeltaMethods (ST s) L.ByteString 348
349instance Monoid x => Monoid (XDeltaFailable x) where
350 mempty = XSuccess mempty
351 mappend (XSuccess x) (XSuccess y) = XSuccess $ mappend x y
352 mappend x@XError{} _ = x
353 mappend _ y@XError{} = y
354
355xdeltaPure :: (forall s. Stream (ST s) -> ST s ErrorCode) -> Config -> L.ByteString -> L.ByteString -> XDeltaFailable L.ByteString
356xdeltaPure codec cfg source delta =
357 let smap = IntMap.fromList $ zip [0..] (chunksOf 16 source)
358 x :: XDeltaMethods (ST s) (XDeltaFailable L.ByteString)
333 x = XDeltaMethods 359 x = XDeltaMethods
334 { xConfig = cfg 360 { xConfig = cfg
335 , xGetSource = (smap IntMap.!) . fromIntegral -- :: Xoff_t -> B.ByteString 361 , xGetSource = \i -> case IntMap.lookup (fromIntegral i) smap of
336 , xOutput = \ptr len -> unsafeIOToST $ L.fromStrict <$> B.packCStringLen (castPtr ptr,len) 362 Nothing -> B.empty
337 , xOnError = \e s -> return L.empty -- :: ErrorCode -> String -> m u 363 Just bs -> bs
338 , xBlockSize = 4096 -- :: Usize_t 364 , xOutput = \ptr len -> unsafeIOToST $ XSuccess . L.fromStrict <$> B.packCStringLen (castPtr ptr,len)
339 , xInterleave = id -- :: forall a. m a -> m a 365 , xOnError = \e s -> return (XError e s) -- :: ErrorCode -> String -> m u
366 , xBlockSize = 16 -- 4096 -- :: Usize_t
367 , xInterleave = unsafeInterleaveST -- :: forall a. m a -> m a
368 -- XXX: Why isn't unsafeInterleaveST making it lazy?
340 } 369 }
341 ds = L.toChunks delta 370 ds = chunksOf 16 delta -- L.toChunks delta
342 in runST $ xdelta x decode_input ds 371 in runST $ xdelta x codec ds
372
373defaultConfig :: Config
374defaultConfig = Config
375 { winsize = 4096
376 , sprevsz = 0
377 , iopt_size = 0
378 , flags = mempty
379 , sec_data = CompressorConfig 0 0 0
380 , sec_inst = CompressorConfig 0 0 0
381 , sec_addr = CompressorConfig 0 0 0
382 , smatch_cfg = Right SMATCH_DEFAULT
383 }