Permalink
Browse files

Improved FFI binding, and moved BDelta.hsc into the Data/ directory.

  • Loading branch information...
1 parent e7afc80 commit a2f6bb505036d640d3a338bce0a2576b5e38a6f0 @joeyadams committed Sep 21, 2011
Showing with 111 additions and 117 deletions.
  1. +0 −45 BDelta.cabal
  2. +0 −67 BDelta.hs
  3. +106 −0 Data/BDelta.hsc
  4. +1 −1 test.hs → Tests.hs
  5. +2 −2 bdelta.c
  6. +2 −2 bdelta.h
View
@@ -1,45 +0,0 @@
-Name: BDelta
-Version: 0.1.1
-License: MIT
-Maintainer: Joey Adams <joeyadams3.14159@gmail.com>
-Author: Joey Adams
-Copyright: Copyright (C) 2011 Joseph Adams
-License-file: LICENSE
-Category: Data
-Synopsis: ByteString diff/patch
-
-Description:
- Space-efficient delta encoding between two ByteStrings.
- .
- The 'diff' function takes two ByteStrings, and produces a "patch" that
- can later be applied with the 'patch' function to the first string to produce
- the second string. It exploits similarities between the two strings, and can
- be used to save bandwidth and disk space when many strings differing by a
- small number of bytes need to be transmitted or stored.
- .
- Deltas produced by this version of the library can be applied using
- current or future versions, but may not be compatible with past versions.
- .
- bdelta implements the algorithm described in
- An O(ND) Difference Algorithm and Its Variations by Eugene W. Myers.
- Because its memory usage and expected running time are O(N + D^2),
- it works well only when the strings differ by a small number of bytes.
- This implementation stops trying when the strings differ by more than
- 1000 bytes, and falls back to producing a patch that simply emits the new
- string.
- .
- Thus, bdelta does not save any space when given two strings that differ by
- more than 1000 bytes. This may be improved in a future version of the
- library.
-
-Extra-Source-Files: c/bdelta.c c/bdelta.h
-
-Stability: experimental
-Build-Type: Simple -- ???
-
-Library
- Exposed-Modules: Data.BDelta
- GHC-Options: -Wall -O2
- C-Sources: c/bdelta.c
- CC-Options: -Wall -O3
- Include-Dirs: c
View
@@ -1,67 +0,0 @@
-module BDelta where
-
-import Data.ByteString (ByteString, packCStringLen)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
-import Foreign (Ptr, alloca, peek, unsafePerformIO)
-import Foreign.C.Types (CSize, CInt, CChar)
-
-type BDeltaFunc = Ptr CChar -> CSize
- -> Ptr CChar -> CSize
- -> Ptr (Ptr CChar) -> Ptr CSize
- -> IO CInt
-
-data BDeltaResult = BDeltaOK ByteString
- | BDeltaMemory
- | BDeltaPatchInvalid
- | BDeltaPatchMismatch
- | BDeltaInternal
-
-foreign import ccall safe "bdelta.h bdelta_diff"
- bdelta_diff :: BDeltaFunc
-
-foreign import ccall safe "bdelta.h bdelta_patch"
- bdelta_patch :: BDeltaFunc
-
--- I don't know if Foreign.Marshal.Alloc.free is guaranteed
--- to be compatible with stdlib's free or not.
-foreign import ccall unsafe "stdlib.h free"
- free :: Ptr a -> IO ()
-
-callBDeltaFunc :: BDeltaFunc -> ByteString -> ByteString -> BDeltaResult
-callBDeltaFunc func old new =
- unsafePerformIO $
- 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 (BDeltaOK result)
- 1 -> return BDeltaMemory
- 2 -> return BDeltaPatchInvalid
- 3 -> return BDeltaPatchMismatch
- _ -> return BDeltaInternal
-
-diff :: ByteString -> ByteString -> ByteString
-diff old new =
- case callBDeltaFunc bdelta_diff old new of
- BDeltaOK result -> result
- BDeltaMemory -> error "BDelta.diff: out of memory"
- _ -> error "BDelta.diff: internal error"
-
-patch :: ByteString -> ByteString -> Maybe ByteString
-patch old new =
- case callBDeltaFunc bdelta_patch old new of
- BDeltaOK result -> Just result
- BDeltaMemory -> error "BDelta.patch: out of memory"
- BDeltaPatchInvalid -> Nothing
- BDeltaPatchMismatch -> Nothing
- _ -> error "BDelta.patch: internal error"
View
@@ -0,0 +1,106 @@
+-- |
+-- Module: Data.BDelta
+-- Copyright: (c) Joseph Adams 2011
+-- License: MIT
+--
+-- Maintainer: joeyadams3.14159@gmail.com
+-- Stability: experimental
+-- Portability: unknown
+--
+-- Space-efficient delta encoding between two ByteStrings.
+--
+-- The 'diff' function takes two ByteStrings, and produces a \"patch\" that
+-- can later be applied with the 'patch' function to the first string to produce
+-- the second string. It exploits common subsequences between the two strings,
+-- and can be used to save bandwidth and disk space when many strings differing
+-- by a small number of bytes need to be transmitted or stored.
+--
+-- Deltas produced by this version of the library can be applied using
+-- current or future versions, but may not be compatible with past versions.
+--
+-- bdelta implements the algorithm described in
+-- /An O(ND) Difference Algorithm and Its Variations/ by Eugene W. Myers.
+-- Because its memory usage and expected running time are O(N + D^2),
+-- it works well only when the strings differ by a small number of bytes.
+-- This implementation stops trying when the strings differ by more than
+-- 1000 bytes, and falls back to producing a patch that simply emits the new
+-- string.
+--
+-- Thus, bdelta does not save any space when given two strings that differ by
+-- more than 1000 bytes. This may be improved in a future version of the
+-- library.
+
+module Data.BDelta (diff, patch) where
+
+#include "bdelta.h"
+
+import Data.ByteString (ByteString, packCStringLen)
+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
+import Foreign (Ptr, alloca, peek, unsafePerformIO)
+import Foreign.C.String (peekCAString)
+import Foreign.C.Types (CSize, CInt, CChar)
+
+type BDELTAcode = #{type BDELTAcode}
+
+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 -> CString
+
+-- I don't know if Foreign.Marshal.Alloc.free is guaranteed
+-- to be compatible with stdlib's free or not.
+foreign import ccall unsafe "stdlib.h free"
+ free :: Ptr a -> IO ()
+
+callBDeltaFunc :: BDeltaFunc -> ByteString -> ByteString -> Either BDELTAcode ByteString
+callBDeltaFunc func old new =
+ unsafePerformIO $
+ 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
+ #{const BDELTA_OK} -> do
+ diffPtr <- peek diffPtrPtr
+ diffSize <- peek diffSizePtr
+ result <- packCStringLen (diffPtr, fromIntegral diffSize)
+ free diffPtr
+ return $ Right result
+ _ -> return $ Left rc
+
+strerror :: BDELTAcode -> String
+strerror = peekCAString . bdelta_strerror
+
+-- | Compute a delta between two 'ByteString's.
+--
+-- > patch old (diff old new) == Right new
+diff :: ByteString -> ByteString -> ByteString
+diff old new =
+ case callBDeltaFunc bdelta_diff old new of
+ Right result -> result
+ Left errcode -> error $ "BDelta.diff: " ++ strerror errcode
+
+-- | Apply a delta produced by 'diff'.
+--
+-- If the patch cannot be applied, this function returns @Left errmsg@,
+-- where @errmsg@ is a string describing the error.
+patch :: ByteString -> ByteString -> Either String ByteString
+patch old patch =
+ case callBDeltaFunc bdelta_patch old patch of
+ BDeltaOK result -> Right result
+ rc@#{const BDELTA_PATCH_INVALID} -> Left $ strerror rc
+ rc@#{const BDELTA_PATCH_MISMATCH} -> Left $ strerror rc
+ rc -> error $ "BDelta.patch: " ++ strerror rc
View
@@ -1,4 +1,4 @@
-import BDelta (diff, patch)
+import Data.BDelta (diff, patch)
import Control.Applicative ((<$>))
import Control.Exception (evaluate)
View
@@ -623,7 +623,7 @@ static BDELTAcode diff_myers(
return BDELTA_MEMORY;
}
-int bdelta_diff(
+BDELTAcode bdelta_diff(
const void *old, size_t old_size,
const void *new_, size_t new_size,
void **patch_out, size_t *patch_size_out)
@@ -758,7 +758,7 @@ static BDELTAcode patch_csi32(
return BDELTA_MEMORY;
}
-int bdelta_patch(
+BDELTAcode bdelta_patch(
const void *old, size_t old_size,
const void *patch, size_t patch_size,
void **new_out, size_t *new_size_out)
View
@@ -61,7 +61,7 @@ typedef enum {
* ...
* free(patch);
*/
-int bdelta_diff(
+BDELTAcode bdelta_diff(
const void *old, size_t old_size,
const void *new_, size_t new_size,
void **patch_out, size_t *patch_size_out
@@ -91,7 +91,7 @@ int bdelta_diff(
* putchar('\n');
* free(new_);
*/
-int bdelta_patch(
+BDELTAcode bdelta_patch(
const void *old, size_t old_size,
const void *patch, size_t patch_size,
void **new_out, size_t *new_size_out

0 comments on commit a2f6bb5

Please sign in to comment.