summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2018-10-25 23:39:44 -0400
committerJoe Crayne <joe@jerkface.net>2018-10-25 23:40:19 -0400
commit05ae232e966eccae46d535126a579740b6cf780d (patch)
tree686b27aa2010cb4eb8322388552c4dd320e8e585
parentf8bb9011375e39b71fe1764159e034a97ea70f2e (diff)
Added chunk_size parameter to high-level streaming interface.
-rw-r--r--examples/testdiff.hs8
-rw-r--r--haskell/Data/XDelta.hsc81
-rw-r--r--haskell/XDelta/Types.hsc1
3 files changed, 51 insertions, 39 deletions
diff --git a/examples/testdiff.hs b/examples/testdiff.hs
index 9360545..2847fc3 100644
--- a/examples/testdiff.hs
+++ b/examples/testdiff.hs
@@ -10,7 +10,7 @@ source = "It could be said that Joe was here. I don't know what to do about it."
10patched :: L.ByteString 10patched :: L.ByteString
11patched = "It could be said that Joe, the magnificent, was here. I don't know what to do about it." 11patched = "It could be said that Joe, the magnificent, was here. I don't know what to do about it."
12 12
13delta :: XDeltaFailable XDelta 13delta :: Result VCDIFF
14delta = computeDiff defaultConfig source patched 14delta = computeDiff defaultConfig source patched
15 15
16main = do 16main = do
@@ -19,11 +19,13 @@ main = do
19 mapM_ putStrLn $ xxd2 0 (L.toStrict patched) 19 mapM_ putStrLn $ xxd2 0 (L.toStrict patched)
20 putStrLn "" 20 putStrLn ""
21 case delta of 21 case delta of
22 XResult δ@(XDelta d) me -> do 22 Result δ@(VCDIFF d) me -> do
23 -- mapM_ (mapM_ putStrLn . xxd2 0) (chunksOf 16 d) 23 -- mapM_ (mapM_ putStrLn . xxd2 0) (chunksOf 16 d)
24 mapM_ putStrLn $ xxd2 0 (L.toStrict d) 24 mapM_ putStrLn $ xxd2 0 (L.toStrict d)
25 print me 25 print me
26 putStrLn "" 26 putStrLn ""
27 let XResult patched' pe = applyPatch defaultConfig source δ 27 let Result patched' pe = applyPatch defaultConfig source δ
28 mapM_ putStrLn $ xxd2 0 (L.toStrict patched') -- $ L.take 48 patched') 28 mapM_ putStrLn $ xxd2 0 (L.toStrict patched') -- $ L.take 48 patched')
29 print pe 29 print pe
30 print ("source",source)
31 print ("patched",patched)
diff --git a/haskell/Data/XDelta.hsc b/haskell/Data/XDelta.hsc
index bb08cb6..8128a61 100644
--- a/haskell/Data/XDelta.hsc
+++ b/haskell/Data/XDelta.hsc
@@ -140,7 +140,7 @@ config_stream cfg = do
140 xd3_abort_stream sptr 140 xd3_abort_stream sptr
141 xd3_close_stream sptr 141 xd3_close_stream sptr
142 xd3_free_stream sptr 142 xd3_free_stream sptr
143 seq s $ return () -- Keep array s alive until the ffi functions finish. 143 seq srcvar $ seq s $ return () -- Keep array s alive until the ffi functions finish.
144 fp <- newForeignPtr sptr finalize 144 fp <- newForeignPtr sptr finalize
145 return Stream 145 return Stream
146 { streamArray = s 146 { streamArray = s
@@ -166,7 +166,8 @@ foreign import ccall "xdelta3.h xd3_set_source" xd3_set_source :: Ptr Xd3Stream
166set_source :: PrimMonad m => 166set_source :: PrimMonad m =>
167 Stream m -> String -- ^ name for debug/print purposes. 167 Stream m -> String -- ^ name for debug/print purposes.
168 -> Usize_t -- ^ block size 168 -> Usize_t -- ^ block size
169 -> Xoff_t -- ^ maximum visible buffer 169 -> Xoff_t -- ^ maximum visible buffer (Suggested: set same as block size).
170 -- Rounds up to approx 16k.
170 -> m () 171 -> m ()
171set_source stream nm blksz maxwinsz = do 172set_source stream nm blksz maxwinsz = do
172 let bsname = encodeUtf8 $ T.pack nm 173 let bsname = encodeUtf8 $ T.pack nm
@@ -280,7 +281,7 @@ xdelta x xxcode_input ds = do
280 either (\e _ -> xOnError x e "config_stream failed") 281 either (\e _ -> xOnError x e "config_stream failed")
281 (flip ($)) 282 (flip ($))
282 mstream $ \stream -> do 283 mstream $ \stream -> do
283 set_source stream "xdelta" (xBlockSize x) (xBlockSize x) 284 set_source stream "VCDIFF" (xBlockSize x) (xBlockSize x)
284 let go withBlk [] = return mempty 285 let go withBlk [] = return mempty
285 go withBlk (d:ds) = do 286 go withBlk (d:ds) = do
286 let (fp,off,len) = B.toForeignPtr d 287 let (fp,off,len) = B.toForeignPtr d
@@ -331,7 +332,8 @@ encode_input :: PrimMonad m => Stream m -> m ErrorCode
331encode_input stream = 332encode_input stream =
332 unsafeIOToPrim $ xd3_encode_input (ptr $ mutableByteArrayContents $ streamArray stream) 333 unsafeIOToPrim $ xd3_encode_input (ptr $ mutableByteArrayContents $ streamArray stream)
333 334
334newtype XDelta = XDelta L.ByteString 335-- RFC 3284
336newtype VCDIFF = VCDIFF L.ByteString
335 deriving Show 337 deriving Show
336 338
337chunksOf :: Usize_t -> L.ByteString -> [B.ByteString] 339chunksOf :: Usize_t -> L.ByteString -> [B.ByteString]
@@ -339,45 +341,52 @@ chunksOf len bs | L.null bs = []
339 | otherwise = let (b,bs') = L.splitAt (fromIntegral len) bs 341 | otherwise = let (b,bs') = L.splitAt (fromIntegral len) bs
340 in L.toStrict b : chunksOf len bs' 342 in L.toStrict b : chunksOf len bs'
341 343
342computeDiff :: Config -> L.ByteString -> L.ByteString -> XDeltaFailable XDelta 344computeDiff :: Config -> L.ByteString -> L.ByteString -> Result VCDIFF
343computeDiff cfg source patched = fmap XDelta $ xdeltaPure encode_input cfg source patched 345computeDiff cfg source patched = fmap VCDIFF $ xdeltaPure encode_input cfg source patched
344 346
345applyPatch :: Config -> L.ByteString -> XDelta -> XDeltaFailable L.ByteString 347applyPatch :: Config -> L.ByteString -> VCDIFF -> Result L.ByteString
346applyPatch cfg source (XDelta delta) = xdeltaPure decode_input cfg source delta 348applyPatch cfg source (VCDIFF delta) = xdeltaPure decode_input cfg source delta
347 349
348data XDeltaFailable x = XResult { xresult :: x, xerror :: (Maybe (ErrorCode,String)) } 350data Result x = Result
349 deriving (Show,Functor) 351 { result :: x -- ^ A possibly invalid result. To consume a lazy stream with fusion, avoid
350 352 -- evaluating 'resultError' until this field is fully processed.
351instance Monoid x => Monoid (XDeltaFailable x) where 353 , resultError :: Maybe (ErrorCode,String)
352 mempty = XResult mempty Nothing 354 -- ^ If something went wrong while producing 'result', this
353 mappend (XResult x xe) y = XResult (mappend x $ xresult y) (maybe (xerror y) Just xe) 355 -- is an error code and message indicating what.
354 356 } deriving (Show,Functor)
355xdeltaPure :: (forall s. Stream (ST s) -> ST s ErrorCode) -> Config -> L.ByteString -> L.ByteString -> XDeltaFailable L.ByteString 357
356xdeltaPure codec cfg source delta = 358instance Monoid x => Monoid (Result x) where
357 let smap = IntMap.fromList $ zip [0..] (chunksOf bsize source) 359 mempty = Result mempty Nothing
358 bsize = 4096 360 mappend (Result x xe) y = Result (mappend x $ result y) (maybe (resultError y) Just xe)
359 x :: XDeltaMethods (ST s) (XDeltaFailable L.ByteString) 361
362xdeltaPure :: (forall s. Stream (ST s) -> ST s ErrorCode) -> Config -> L.ByteString -> L.ByteString -> Result L.ByteString
363xdeltaPure codec cfg source input =
364 let bsize = chunk_size cfg
365 ds = chunksOf bsize input
366 smap = IntMap.fromList $ zip [0..] (chunksOf bsize source)
367 x :: XDeltaMethods (ST s) (Result L.ByteString)
360 x = XDeltaMethods 368 x = XDeltaMethods
361 { xConfig = cfg 369 { xConfig = cfg
362 , xGetSource = \i -> case IntMap.lookup (fromIntegral i) smap of 370 , xGetSource = \i -> case IntMap.lookup (fromIntegral i) smap of
363 Nothing -> B.empty 371 Nothing -> B.empty
364 Just bs -> bs 372 Just bs -> bs
365 , xOutput = \ptr len -> unsafeIOToST $ flip XResult Nothing . L.fromStrict <$> B.packCStringLen (castPtr ptr,len) 373 , xOutput = \ptr len -> unsafeIOToST $ flip Result Nothing . L.fromStrict
366 , xOnError = \e s -> return (XResult L.empty (Just (e,s))) -- :: ErrorCode -> String -> m u 374 <$> B.packCStringLen (castPtr ptr,len)
367 , xBlockSize = bsize -- :: Usize_t 375 , xOnError = \e s -> return (Result L.empty (Just (e,s)))
368 , xInterleave = unsafeInterleaveST -- :: forall a. m a -> m a 376 , xBlockSize = bsize
377 , xInterleave = unsafeInterleaveST
369 } 378 }
370 ds = chunksOf bsize delta -- L.toChunks delta
371 in runST $ xdelta x codec ds 379 in runST $ xdelta x codec ds
372 380
373defaultConfig :: Config 381defaultConfig :: Config
374defaultConfig = Config 382defaultConfig = Config
375 { winsize = XD3_DEFAULT_WINSIZE 383 { winsize = XD3_DEFAULT_WINSIZE
376 , sprevsz = XD3_DEFAULT_SPREVSZ 384 , sprevsz = XD3_DEFAULT_SPREVSZ
377 , iopt_size = XD3_DEFAULT_IOPT_SIZE 385 , iopt_size = XD3_DEFAULT_IOPT_SIZE
378 , flags = mempty 386 , flags = mempty
379 , sec_data = CompressorConfig 0 0 0 387 , sec_data = CompressorConfig 0 0 0
380 , sec_inst = CompressorConfig 0 0 0 388 , sec_inst = CompressorConfig 0 0 0
381 , sec_addr = CompressorConfig 0 0 0 389 , sec_addr = CompressorConfig 0 0 0
382 , smatch_cfg = Right SMATCH_DEFAULT 390 , smatch_cfg = Right SMATCH_DEFAULT
391 , chunk_size = 4096
383 } 392 }
diff --git a/haskell/XDelta/Types.hsc b/haskell/XDelta/Types.hsc
index a0f2cfa..8a60805 100644
--- a/haskell/XDelta/Types.hsc
+++ b/haskell/XDelta/Types.hsc
@@ -113,6 +113,7 @@ data Config = Config
113 , sec_inst :: CompressorConfig -- ^ Secondary compressor config: inst 113 , sec_inst :: CompressorConfig -- ^ Secondary compressor config: inst
114 , sec_addr :: CompressorConfig -- ^ Secondary compressor config: addr 114 , sec_addr :: CompressorConfig -- ^ Secondary compressor config: addr
115 , smatch_cfg :: Either StringMatcher SMatchSelect -- ^ See enum: use fields below for soft config 115 , smatch_cfg :: Either StringMatcher SMatchSelect -- ^ See enum: use fields below for soft config
116 , chunk_size :: Usize_t -- ^ Suggested chunking size for streaming.
116 } 117 }
117 118
118pattern XD3_DEFAULT_WINSIZE = #const XD3_DEFAULT_WINSIZE 119pattern XD3_DEFAULT_WINSIZE = #const XD3_DEFAULT_WINSIZE