summaryrefslogtreecommitdiff
path: root/haskell
diff options
context:
space:
mode:
Diffstat (limited to 'haskell')
-rw-r--r--haskell/Data/XDelta.hsc223
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 #-}
7module Data.XDelta where
8
9import Control.Monad
10import Control.Monad.Primitive
11import Control.Monad.ST
12import Control.Monad.ST.Unsafe
13import Data.BA
14import qualified Data.ByteString as B
15import qualified Data.ByteString.Unsafe as B
16import qualified Data.ByteString.Internal as B
17import Data.Coerce
18import Data.Primitive.Addr
19import Data.Primitive.ByteArray
20import Data.STRef
21import qualified Data.Text as T
22import Data.Text.Encoding
23import Data.Word
24import Foreign.C.Types
25import Foreign.ForeignPtr (withForeignPtr)
26import Foreign.Ptr
27import Foreign.Concurrent
28import Foreign.ForeignPtr (ForeignPtr)
29import GHC.Exts
30
31import 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
42data Stream s = Stream
43 { streamArray :: MutableByteArray (PrimState (ST s))
44 , streamPtr :: ForeignPtr Xd3Stream
45 , streamSource :: STRef s (Maybe (MutableByteArray (PrimState (ST s))))
46 }
47
48foreign import ccall "xdelta3.h xd3_config_stream" xd3_config_stream :: Ptr Xd3Stream -> Ptr Config -> IO ErrorCode
49
50foreign import ccall "xdelta3.h xd3_free_stream" xd3_free_stream :: Ptr Xd3Stream -> IO ()
51
52foreign import ccall "xdelta3.h xd3_abort_stream" xd3_abort_stream :: Ptr Xd3Stream -> IO ()
53
54foreign import ccall "xdelta3.h xd3_close_stream" xd3_close_stream :: Ptr Xd3Stream -> IO ErrorCode
55
56
57writeCompressorConfig 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
62writeMatcher 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
73ptr (Addr a) = Ptr a
74adr (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.
79config_stream :: Config -> ST s (Either ErrorCode (Stream s))
80config_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
132writeStringAt 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
139data Xd3Source
140
141foreign import ccall "xdelta3.h xd3_set_source" xd3_set_source :: Ptr Xd3Stream -> Ptr Xd3Source -> IO ErrorCode
142
143set_source :: Stream s -> String -- ^ name for debug/print purposes.
144 -> Usize_t -- ^ block size
145 -> Xoff_t -- ^ maximum visible buffer
146 -> ST s ()
147set_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
165data 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.
174foreign import ccall "xdelta3.h xd3_set_flags" xd3_set_flags :: Ptr Xd3Stream -> Flags -> IO ()
175
176setFlush :: Stream s -> Bool -> ST s ()
177setFlush stream wantFlush = return () -- todo
178
179setSkipWindow :: Stream s -> Bool -> ST s ()
180setSkipWindow stream wantSkipWin = return () -- todo
181
182{-
183xdelta :: Monoid m => XDeltaMethods m -> (Stream s -> IO ErrorCode) -> [B.ByteString] -> ST s m
184xdelta 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-}