diff options
Diffstat (limited to 'haskell')
-rw-r--r-- | haskell/Data/XDelta.hsc | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/haskell/Data/XDelta.hsc b/haskell/Data/XDelta.hsc new file mode 100644 index 0000000..11a8579 --- /dev/null +++ b/haskell/Data/XDelta.hsc | |||
@@ -0,0 +1,223 @@ | |||
1 | {-# LANGUAGE BangPatterns #-} | ||
2 | {-# LANGUAGE GADTs #-} | ||
3 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
4 | {-# LANGUAGE LambdaCase #-} | ||
5 | {-# LANGUAGE PatternSynonyms #-} | ||
6 | {-# LANGUAGE NondecreasingIndentation #-} | ||
7 | module Data.XDelta where | ||
8 | |||
9 | import Control.Monad | ||
10 | import Control.Monad.Primitive | ||
11 | import Control.Monad.ST | ||
12 | import Control.Monad.ST.Unsafe | ||
13 | import Data.BA | ||
14 | import qualified Data.ByteString as B | ||
15 | import qualified Data.ByteString.Unsafe as B | ||
16 | import qualified Data.ByteString.Internal as B | ||
17 | import Data.Coerce | ||
18 | import Data.Primitive.Addr | ||
19 | import Data.Primitive.ByteArray | ||
20 | import Data.STRef | ||
21 | import qualified Data.Text as T | ||
22 | import Data.Text.Encoding | ||
23 | import Data.Word | ||
24 | import Foreign.C.Types | ||
25 | import Foreign.ForeignPtr (withForeignPtr) | ||
26 | import Foreign.Ptr | ||
27 | import Foreign.Concurrent | ||
28 | import Foreign.ForeignPtr (ForeignPtr) | ||
29 | import GHC.Exts | ||
30 | |||
31 | import XDelta.Types | ||
32 | |||
33 | #ifndef SIZEOF_SIZE_T | ||
34 | #define SIZEOF_SIZE_T __SIZEOF_SIZE_T__ | ||
35 | #define SIZEOF_UNSIGNED_INT __SIZEOF_INT__ | ||
36 | #define SIZEOF_UNSIGNED_LONG __SIZEOF_LONG__ | ||
37 | #define SIZEOF_UNSIGNED_LONG_LONG __SIZEOF_LONG_LONG__ | ||
38 | #define static_assert(...) | ||
39 | #endif | ||
40 | #include <xdelta3.h> | ||
41 | |||
42 | data Stream s = Stream | ||
43 | { streamArray :: MutableByteArray (PrimState (ST s)) | ||
44 | , streamPtr :: ForeignPtr Xd3Stream | ||
45 | , streamSource :: STRef s (Maybe (MutableByteArray (PrimState (ST s)))) | ||
46 | } | ||
47 | |||
48 | foreign import ccall "xdelta3.h xd3_config_stream" xd3_config_stream :: Ptr Xd3Stream -> Ptr Config -> IO ErrorCode | ||
49 | |||
50 | foreign import ccall "xdelta3.h xd3_free_stream" xd3_free_stream :: Ptr Xd3Stream -> IO () | ||
51 | |||
52 | foreign import ccall "xdelta3.h xd3_abort_stream" xd3_abort_stream :: Ptr Xd3Stream -> IO () | ||
53 | |||
54 | foreign import ccall "xdelta3.h xd3_close_stream" xd3_close_stream :: Ptr Xd3Stream -> IO ErrorCode | ||
55 | |||
56 | |||
57 | writeCompressorConfig c o sec = do | ||
58 | writeAtByte c (o + #{offset xd3_sec_cfg,ngroups}) (ngroups sec) | ||
59 | writeAtByte c (o + #{offset xd3_sec_cfg,sector_size}) (sector_size sec) | ||
60 | writeAtByte c (o + #{offset xd3_sec_cfg,inefficient}) (inefficient sec) | ||
61 | |||
62 | writeMatcher c o sm = do | ||
63 | -- handled elsewhere: const char *name; <- smName :: String | ||
64 | writeAtByte c (o + #{offset xd3_smatcher, string_match }) (smStringMatch sm) | ||
65 | writeAtByte c (o + #{offset xd3_smatcher, large_look }) (smLargeLook sm) | ||
66 | writeAtByte c (o + #{offset xd3_smatcher, large_step }) (smLargeStep sm) | ||
67 | writeAtByte c (o + #{offset xd3_smatcher, small_look }) (smSmallLook sm) | ||
68 | writeAtByte c (o + #{offset xd3_smatcher, small_chain }) (smSmallChain sm) | ||
69 | writeAtByte c (o + #{offset xd3_smatcher, small_lchain }) (smSmallLchain sm) | ||
70 | writeAtByte c (o + #{offset xd3_smatcher, max_lazy }) (smMaxLazy sm) | ||
71 | writeAtByte c (o + #{offset xd3_smatcher, long_enough }) (smLongEnough sm) | ||
72 | |||
73 | ptr (Addr a) = Ptr a | ||
74 | adr (Ptr a) = Addr a | ||
75 | |||
76 | -- The xd3_config structure is used to initialize a stream - all data | ||
77 | -- is copied into stream so config may be a temporary variable. See | ||
78 | -- the [documentation] or comments on the xd3_config structure. | ||
79 | config_stream :: Config -> ST s (Either ErrorCode (Stream s)) | ||
80 | config_stream cfg = do | ||
81 | let (len,n) = case smatch_cfg cfg of | ||
82 | Left m -> let n = encodeUtf8 $ T.pack $ smName m | ||
83 | in ( #{const sizeof(xd3_stream)} + B.length n + 1 | ||
84 | , n ) | ||
85 | Right _ -> ( #{const sizeof(xd3_stream)}, B.empty ) | ||
86 | s <- newPinnedByteArray len | ||
87 | let sptr = ptr (mutableByteArrayContents s) :: Ptr Xd3Stream | ||
88 | fillByteArray s 0 #{const sizeof(xd3_stream)} 0 | ||
89 | nptr <- case smatch_cfg cfg of | ||
90 | Right _ -> writeStringAt s #{const sizeof(xd3_stream)} n | ||
91 | Left _ -> return nullPtr | ||
92 | c <- do | ||
93 | c <- newPinnedByteArray #const sizeof(xd3_config) | ||
94 | fillByteArray c 0 #{const sizeof(xd3_config)} 0 | ||
95 | writeAtByte c #{offset xd3_config, winsize} (winsize cfg) | ||
96 | writeAtByte c #{offset xd3_config, sprevsz} (sprevsz cfg) | ||
97 | writeAtByte c #{offset xd3_config, iopt_size} (iopt_size cfg) | ||
98 | writeAtByte c #{offset xd3_config, flags} (coerce (flags cfg) :: Word32) | ||
99 | writeCompressorConfig c #{offset xd3_config, sec_data} (sec_data cfg) | ||
100 | writeCompressorConfig c #{offset xd3_config, sec_inst} (sec_inst cfg) | ||
101 | writeCompressorConfig c #{offset xd3_config, sec_addr} (sec_addr cfg) | ||
102 | let msel :: #type xd3_smatch_cfg | ||
103 | msel = either (const #{const XD3_SMATCH_SOFT}) | ||
104 | (fromIntegral . fromEnum) | ||
105 | (smatch_cfg cfg) | ||
106 | writeAtByte c #{offset xd3_config, smatch_cfg} msel | ||
107 | case smatch_cfg cfg of | ||
108 | Right _ -> return () | ||
109 | Left matcher -> do | ||
110 | let o = #offset xd3_config,smatcher_soft | ||
111 | writeAtByte c (o + #{offset xd3_smatcher, name}) nptr | ||
112 | writeMatcher c o matcher | ||
113 | unsafeFreezeByteArray c | ||
114 | let cptr = ptr (byteArrayContents c) :: Ptr Config | ||
115 | srcvar <- newSTRef Nothing | ||
116 | stream <- unsafeIOToST $ do | ||
117 | let finalize = do | ||
118 | -- freeHaskellFunPtr: aloc,free,getblk | ||
119 | xd3_abort_stream sptr | ||
120 | xd3_close_stream sptr | ||
121 | xd3_free_stream sptr | ||
122 | fp <- newForeignPtr sptr finalize | ||
123 | return Stream | ||
124 | { streamArray = s | ||
125 | , streamPtr = fp | ||
126 | , streamSource = srcvar | ||
127 | } | ||
128 | unsafeIOToST (xd3_config_stream sptr cptr) >>= \case | ||
129 | XD3_SUCCESS -> return $ c `seq` Right stream | ||
130 | ecode -> return $ Left ecode | ||
131 | |||
132 | writeStringAt src o bsname = do | ||
133 | (p,cnt) <- unsafeIOToST $ B.unsafeUseAsCStringLen bsname return | ||
134 | let nptr = ptr (mutableByteArrayContents src) `plusPtr` o | ||
135 | copyAddr (adr nptr) (adr p) cnt | ||
136 | writeOffAddr (adr nptr) cnt (0 :: Word8) | ||
137 | return nptr | ||
138 | |||
139 | data Xd3Source | ||
140 | |||
141 | foreign import ccall "xdelta3.h xd3_set_source" xd3_set_source :: Ptr Xd3Stream -> Ptr Xd3Source -> IO ErrorCode | ||
142 | |||
143 | set_source :: Stream s -> String -- ^ name for debug/print purposes. | ||
144 | -> Usize_t -- ^ block size | ||
145 | -> Xoff_t -- ^ maximum visible buffer | ||
146 | -> ST s () | ||
147 | set_source stream nm blksz maxwinsz = do | ||
148 | let bsname = encodeUtf8 $ T.pack nm | ||
149 | src <- newPinnedByteArray $ 1 + B.length bsname + #{const sizeof(xd3_source)} | ||
150 | nptr <- writeStringAt src #{const sizeof(xd3_source)} bsname | ||
151 | writeAtByte src #{offset xd3_source, blksize } blksz | ||
152 | writeAtByte src #{offset xd3_source, name } nptr | ||
153 | writeAtByte src #{offset xd3_source, max_winsize} maxwinsz | ||
154 | writeAtByte src #{offset xd3_source, curblkno } (maxBound :: Xoff_t) | ||
155 | {- | ||
156 | writeAtByte (streamArray stream) | ||
157 | #{offset xd3_stream, getblk} | ||
158 | nullPtr -- xdelta3.h documents this as an internal field. | ||
159 | -} | ||
160 | let strm = ptr (mutableByteArrayContents $ streamArray stream) | ||
161 | srcptr = ptr (mutableByteArrayContents src) | ||
162 | unsafeIOToST (xd3_set_source strm srcptr) | ||
163 | writeSTRef (streamSource stream) (Just src) | ||
164 | |||
165 | data XDeltaMethods m = XDeltaMethods | ||
166 | { xConfig :: Config | ||
167 | , xGetSource :: Xoff_t -> B.ByteString | ||
168 | , xOutput :: Ptr Word8 -> Int -> IO m | ||
169 | , xOnError :: ErrorCode -> String -> IO m | ||
170 | , xBlockSize :: Usize_t | ||
171 | } | ||
172 | |||
173 | -- | Checks for legal flag changes. | ||
174 | foreign import ccall "xdelta3.h xd3_set_flags" xd3_set_flags :: Ptr Xd3Stream -> Flags -> IO () | ||
175 | |||
176 | setFlush :: Stream s -> Bool -> ST s () | ||
177 | setFlush stream wantFlush = return () -- todo | ||
178 | |||
179 | setSkipWindow :: Stream s -> Bool -> ST s () | ||
180 | setSkipWindow stream wantSkipWin = return () -- todo | ||
181 | |||
182 | {- | ||
183 | xdelta :: Monoid m => XDeltaMethods m -> (Stream s -> IO ErrorCode) -> [B.ByteString] -> ST s m | ||
184 | xdelta x xxcode_input ds = do | ||
185 | mstream <- config_stream (xConfig x) | ||
186 | forM_ mstream $ \stream -> do | ||
187 | set_source stream "xdelta" (xBlockSize x) (xBlockSize x) | ||
188 | let go withBlk (d:ds) = do | ||
189 | let (fp,off,len) = B.toForeignPtr d | ||
190 | withForeignPtr fp $ \indata0 -> do | ||
191 | let indata = indata0 `plusPtr` off | ||
192 | eof = null ds | ||
193 | when eof $ xd3_set_flags (ptr $ mutableByteArrayContents $ streamArray stream) XD3_FLUSH | ||
194 | avail_input stream indata (fromIntegral len) | ||
195 | go2 withBlk eof ds | ||
196 | go2 withBlk eof ds = do | ||
197 | ret <- withBlk $ xxcode_input stream | ||
198 | case ret of | ||
199 | XD3_INPUT -> if (not eof) then go withBlk ds else return mempty | ||
200 | XD3_OUTPUT -> do | ||
201 | m' <- nextOut stream (uncurry $ xOutput x) | ||
202 | ms <- unsafeInterleaveIO $ go2 withBlk eof ds | ||
203 | return $ m' <> ms | ||
204 | XD3_GETSRCBLK -> do | ||
205 | n <- requestedBlockNumber stream | ||
206 | let blk = xGetSource x n | ||
207 | withBlk' act = let (fp,off,len) = B.toForeignPtr blk | ||
208 | in withForeignPtr fp $ \p -> do | ||
209 | pokeCurrentBlock stream $ CurrentBlock n (fromIntegral len) (plusPtr p off) | ||
210 | act | ||
211 | go2 withBlk' eof ds | ||
212 | XD3_GOTHEADER -> go2 withBlk eof ds -- No | ||
213 | XD3_WINSTART -> go2 withBlk eof ds -- action | ||
214 | XD3_WINFINISH -> go2 withBlk eof ds -- neccessary | ||
215 | -- -- These are set for each XD3_WINFINISH return. | ||
216 | -- xd3_encoder_used_source :: Ptr Stream -> IO Bool | ||
217 | -- xd3_encoder_srcbase :: Ptr Stream -> IO Xoff_t | ||
218 | -- xd3_encoder_srclen :: Ptr Stream -> IO Usize_t | ||
219 | e -> do | ||
220 | s <- errorString stream | ||
221 | xOnError x e s | ||
222 | go id ds | ||
223 | -} | ||