diff --git a/Bench/Bench.hs b/Bench/Bench.hs
deleted file mode 100644
--- a/Bench/Bench.hs
+++ /dev/null
@@ -1,33 +0,0 @@
-{-# LANGUAGE BangPatterns #-}
-import Criterion.Main
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as L
-import qualified Crypto.Hash.SHA256 as SHA256
-
-hashmany (i,u,f) = f . foldl u i
-
-allHashs =
-    [ ("SHA2-256",SHA256.hash, hashmany (SHA256.init,SHA256.update,SHA256.finalize))
-    ]
-
-benchHash :: a -> (a -> B.ByteString) -> Benchmarkable
-benchHash bs f = whnf f bs
-
-withHashesFilter out f = map f $ filter (\(n,_,_) -> not (n `elem` out)) allHashs
-withHashes f = map f allHashs
-
-main = do
-    let !bs32     = B.replicate 32 0
-        !bs256    = B.replicate 256 0
-        !bs4096   = B.replicate 4096 0
-        !bs1M     = B.replicate (1*1024*1024) 0
-    let !lbs64x256 = (map (const (B.replicate 64 0)) [0..3])
-        !lbs64x4096 = (map (const (B.replicate 64 0)) [0..63])
-    defaultMain
-        [ bgroup "hash-32b" (withHashes (\(name, f,_) -> bench name $ benchHash bs32 f))
-        , bgroup "hash-256b" (withHashes (\(name, f,_) -> bench name $ benchHash bs256 f))
-        , bgroup "hash-4Kb" (withHashes (\(name, f,_) -> bench name $ benchHash bs4096 f))
-        , bgroup "hash-1Mb" (withHashesFilter ["MD2"] (\(name, f,_) -> bench name $ benchHash bs1M f))
-        , bgroup "iuf-64x256" (withHashes (\(name, _,f) -> bench name $ benchHash lbs64x256 f))
-        , bgroup "iuf-64x4096" (withHashes (\(name, _,f) -> bench name $ benchHash lbs64x4096 f))
-        ]
diff --git a/Bench/BenchAPI.hs b/Bench/BenchAPI.hs
deleted file mode 100644
--- a/Bench/BenchAPI.hs
+++ /dev/null
@@ -1,39 +0,0 @@
-{-# LANGUAGE BangPatterns #-}
-import Criterion.Main
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as L
-import qualified Crypto.Hash.SHA256 as SHA256
-
-sha256F = ( "SHA256"
-        , SHA256.hash
-        , SHA256.finalize . SHA256.update SHA256.init
-        )
-
-main = do
-    let !bs32     = B.replicate 32 0
-        !bs256    = B.replicate 256 0
-        !bs4096   = B.replicate 4096 0
-        !bs1M     = B.replicate (1*1024*1024) 0
-    let !lbs64x256 = (map (const (B.replicate 64 0)) [0..3])
-        !lbs64x4096 = (map (const (B.replicate 64 0)) [0..63])
-
-    let (fname, fHash, fIncr) = sha256F
-    let benchName ty z = fname ++ "." ++ ty -- ++ " " ++ show z
-    defaultMain
-        [ bgroup "hash-0b"
-            [ bench (benchName "hash" 0) $ whnf fHash B.empty
-            , bench (benchName "incr" 0) $ whnf fIncr B.empty
-            ]
-        , bgroup "hash-32b"
-            [ bench (benchName "hash" 32) $ whnf fHash bs32
-            , bench (benchName "incr" 32) $ whnf fIncr bs32
-            ]
-        , bgroup "hash-256b"
-            [ bench (benchName "hash" 256) $ whnf fHash bs256
-            , bench (benchName "incr" 256) $ whnf fIncr bs256
-            ]
-        , bgroup "hash-4Kb"
-            [ bench (benchName "hash" 4096) $ whnf fHash bs4096
-            , bench (benchName "incr" 4096) $ whnf fIncr bs4096
-            ]
-        ]
diff --git a/Crypto/Hash/SHA256.hs b/Crypto/Hash/SHA256.hs
deleted file mode 100644
--- a/Crypto/Hash/SHA256.hs
+++ /dev/null
@@ -1,184 +0,0 @@
--- |
--- Module      : Crypto.Hash.SHA256
--- License     : BSD-style
--- Maintainer  : Herbert Valerio Riedel <hvr@gnu.org>
--- Stability   : stable
--- Portability : unknown
---
--- A module containing <https://en.wikipedia.org/wiki/SHA-2 SHA-256> bindings
---
-module Crypto.Hash.SHA256
-    (
-
-    -- * Incremental API
-    --
-    -- | This API is based on 4 different functions, similar to the
-    -- lowlevel operations of a typical hash:
-    --
-    --  - 'init': create a new hash context
-    --  - 'update': update non-destructively a new hash context with a strict bytestring
-    --  - 'updates': same as update, except that it takes a list of strict bytestring
-    --  - 'finalize': finalize the context and returns a digest bytestring.
-    --
-    -- all those operations are completely pure, and instead of
-    -- changing the context as usual in others language, it
-    -- re-allocates a new context each time.
-    --
-    -- Example:
-    --
-    -- > import qualified Data.ByteString
-    -- > import qualified Crypto.Hash.SHA256 as SHA256
-    -- >
-    -- > main = print digest
-    -- >   where
-    -- >     digest = SHA256.finalize ctx
-    -- >     ctx    = foldl SHA256.update ctx0 (map Data.ByteString.pack [ [1,2,3], [4,5,6] ])
-    -- >     ctx0   = SHA256.init
-
-      Ctx(..)
-    , init     -- :: Ctx
-    , update   -- :: Ctx -> ByteString -> Ctx
-    , updates  -- :: Ctx -> [ByteString] -> Ctx
-    , finalize -- :: Ctx -> ByteString
-
-    -- * Single Pass API
-    --
-    -- | This API use the incremental API under the hood to provide
-    -- the common all-in-one operations to create digests out of a
-    -- 'ByteString' and lazy 'L.ByteString'.
-    --
-    --  - 'hash': create a digest ('init' + 'update' + 'finalize') from a strict 'ByteString'
-    --  - 'hashlazy': create a digest ('init' + 'update' + 'finalize') from a lazy 'L.ByteString'
-    --
-    -- Example:
-    --
-    -- > import qualified Data.ByteString
-    -- > import qualified Crypto.Hash.SHA256 as SHA256
-    -- >
-    -- > main = print $ SHA256.hash (Data.ByteString.pack [0..255])
-    --
-    -- __NOTE__: The returned digest is a binary 'ByteString'. For
-    -- converting to a base16/hex encoded digest the
-    -- <https://hackage.haskell.org/package/base16-bytestring base16-bytestring>
-    -- package is recommended.
-
-    , hash     -- :: ByteString -> ByteString
-    , hashlazy -- :: ByteString -> ByteString
-    ) where
-
-import Prelude hiding (init)
-import Foreign.Ptr
-import Foreign.ForeignPtr (withForeignPtr)
-import Foreign.Storable
-import Foreign.Marshal.Alloc
-import qualified Data.ByteString.Lazy as L
-import qualified Data.ByteString as B
-import Data.ByteString (ByteString)
-import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
-import Data.ByteString.Internal (create, toForeignPtr)
-import Data.Word
-import System.IO.Unsafe (unsafeDupablePerformIO)
-
--- | perform IO for hashes that do allocation and ffi.
--- unsafeDupablePerformIO is used when possible as the
--- computation is pure and the output is directly linked
--- to the input. we also do not modify anything after it has
--- been returned to the user.
-unsafeDoIO :: IO a -> a
-unsafeDoIO = unsafeDupablePerformIO
-
--- | SHA-256 Context
-newtype Ctx = Ctx ByteString
-
-{-# INLINE digestSize #-}
-digestSize :: Int
-digestSize = 32
-
-{-# INLINE sizeCtx #-}
-sizeCtx :: Int
-sizeCtx = 192
-
-{-# RULES "digestSize" B.length (finalize init) = digestSize #-}
-{-# RULES "hash" forall b. finalize (update init b) = hash b #-}
-{-# RULES "hash.list1" forall b. finalize (updates init [b]) = hash b #-}
-{-# RULES "hashmany" forall b. finalize (foldl update init b) = hashlazy (L.fromChunks b) #-}
-{-# RULES "hashlazy" forall b. finalize (foldl update init $ L.toChunks b) = hashlazy b #-}
-
-{-# INLINE withByteStringPtr #-}
-withByteStringPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a
-withByteStringPtr b f =
-    withForeignPtr fptr $ \ptr -> f (ptr `plusPtr` off)
-    where (fptr, off, _) = toForeignPtr b
-
-{-# INLINE memcopy64 #-}
-memcopy64 :: Ptr Word64 -> Ptr Word64 -> IO ()
-memcopy64 dst src = mapM_ peekAndPoke [0..(24-1)]
-    where peekAndPoke i = peekElemOff src i >>= pokeElemOff dst i
-
-withCtxCopy :: Ctx -> (Ptr Ctx -> IO ()) -> IO Ctx
-withCtxCopy (Ctx ctxB) f = Ctx `fmap` createCtx
-    where createCtx = create sizeCtx $ \dstPtr ->
-                      withByteStringPtr ctxB $ \srcPtr -> do
-                          memcopy64 (castPtr dstPtr) (castPtr srcPtr)
-                          f (castPtr dstPtr)
-
-withCtxThrow :: Ctx -> (Ptr Ctx -> IO a) -> IO a
-withCtxThrow (Ctx ctxB) f =
-    allocaBytes sizeCtx $ \dstPtr ->
-    withByteStringPtr ctxB $ \srcPtr -> do
-        memcopy64 (castPtr dstPtr) (castPtr srcPtr)
-        f (castPtr dstPtr)
-
-withCtxNew :: (Ptr Ctx -> IO ()) -> IO Ctx
-withCtxNew f = Ctx `fmap` create sizeCtx (f . castPtr)
-
-withCtxNewThrow :: (Ptr Ctx -> IO a) -> IO a
-withCtxNewThrow f = allocaBytes sizeCtx (f . castPtr)
-
-foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_init"
-    c_sha256_init :: Ptr Ctx -> IO ()
-
-foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_update"
-    c_sha256_update :: Ptr Ctx -> Ptr Word8 -> Word32 -> IO ()
-
-foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_finalize"
-    c_sha256_finalize :: Ptr Ctx -> Ptr Word8 -> IO ()
-
-updateInternalIO :: Ptr Ctx -> ByteString -> IO ()
-updateInternalIO ptr d =
-    unsafeUseAsCStringLen d (\(cs, len) -> c_sha256_update ptr (castPtr cs) (fromIntegral len))
-
-finalizeInternalIO :: Ptr Ctx -> IO ByteString
-finalizeInternalIO ptr = create digestSize (c_sha256_finalize ptr)
-
-{-# NOINLINE init #-}
--- | init a context
-init :: Ctx
-init = unsafeDoIO $ withCtxNew $ c_sha256_init
-
-{-# NOINLINE update #-}
--- | update a context with a bytestring
-update :: Ctx -> ByteString -> Ctx
-update ctx d = unsafeDoIO $ withCtxCopy ctx $ \ptr -> updateInternalIO ptr d
-
-{-# NOINLINE updates #-}
--- | updates a context with multiples bytestring
-updates :: Ctx -> [ByteString] -> Ctx
-updates ctx d = unsafeDoIO $ withCtxCopy ctx $ \ptr -> mapM_ (updateInternalIO ptr) d
-
-{-# NOINLINE finalize #-}
--- | finalize the context into a digest bytestring
-finalize :: Ctx -> ByteString
-finalize ctx = unsafeDoIO $ withCtxThrow ctx finalizeInternalIO
-
-{-# NOINLINE hash #-}
--- | hash a strict bytestring into a digest bytestring
-hash :: ByteString -> ByteString
-hash d = unsafeDoIO $ withCtxNewThrow $ \ptr -> do
-    c_sha256_init ptr >> updateInternalIO ptr d >> finalizeInternalIO ptr
-
-{-# NOINLINE hashlazy #-}
--- | hash a lazy bytestring into a digest bytestring
-hashlazy :: L.ByteString -> ByteString
-hashlazy l = unsafeDoIO $ withCtxNewThrow $ \ptr -> do
-    c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr
diff --git a/Tests/KAT.hs b/Tests/KAT.hs
deleted file mode 100644
--- a/Tests/KAT.hs
+++ /dev/null
@@ -1,92 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ViewPatterns #-}
-import Data.Char
-import Data.Bits
-import Data.Word
-import Data.ByteString (ByteString)
-import Data.Foldable (foldl')
-import Data.Monoid (mconcat)
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Char8 as BC
-import qualified Crypto.Hash.SHA256 as SHA256
-
-import Test.Tasty
-import Test.Tasty.QuickCheck
-import Test.Tasty.HUnit
-
-v0,v1,v2 :: ByteString
-v0 = ""
-v1 = "The quick brown fox jumps over the lazy dog"
-v2 = "The quick brown fox jumps over the lazy cog"
-vectors = [ v0, v1, v2 ]
-
-instance Arbitrary ByteString where
-    arbitrary = B.pack `fmap` arbitrary
-
-data HashFct = HashFct
-    { fctHash   :: (B.ByteString -> B.ByteString)
-    , fctInc    :: ([B.ByteString] -> B.ByteString) }
-
-hashinc i u f = f . foldl u i
-
-sha256Hash = HashFct { fctHash = SHA256.hash, fctInc = hashinc SHA256.init SHA256.update SHA256.finalize }
-
-
-results :: [ (String, HashFct, [String]) ]
-results = [
-    ("SHA256", sha256Hash, [
-        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
-        "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592",
-        "e4c4d8f3bf76b692de791a173e05321150f7a345b46484fe427f6acc7ecc81be" ])
-    ]
-
-hexalise s = concatMap (\c -> [ hex $ c `div` 16, hex $ c `mod` 16 ]) s
-        where hex i
-                | i >= 0 && i <= 9   = fromIntegral (ord '0') + i
-                | i >= 10 && i <= 15 = fromIntegral (ord 'a') + i - 10
-                | otherwise          = 0
-
-hexaliseB :: B.ByteString -> B.ByteString
-hexaliseB = B.pack . hexalise . B.unpack
-
-splitB :: Int -> ByteString -> [ByteString]
-splitB l b =
-    if B.length b > l
-        then
-            let (b1, b2) = B.splitAt l b in
-            b1 : splitB l b2
-        else
-            [ b ]
-
-showHash :: B.ByteString -> String
-showHash = map (toEnum.fromEnum) . hexalise . B.unpack
-
-runhash hash v = showHash $ (fctHash hash) $ v
-runhashinc hash v = showHash $ (fctInc hash) $ v
-
-makeTestAlg (name, hash, results) = testGroup name $ concatMap maketest (zip3 [0..] vectors results)
-    where
-        runtest :: ByteString -> String
-        runtest v = runhash hash v
-
-        runtestinc :: Int -> ByteString -> String
-        runtestinc i v = runhashinc hash $ splitB i v
-
-        maketest (i, v, r) =
-            [ testCase (show i ++ " one-pass") (r @=? runtest v)
-            , testCase (show i ++ " inc 1") (r @=? runtestinc 1 v)
-            , testCase (show i ++ " inc 2") (r @=? runtestinc 2 v)
-            , testCase (show i ++ " inc 3") (r @=? runtestinc 3 v)
-            , testCase (show i ++ " inc 4") (r @=? runtestinc 4 v)
-            , testCase (show i ++ " inc 5") (r @=? runtestinc 5 v)
-            , testCase (show i ++ " inc 9") (r @=? runtestinc 9 v)
-            , testCase (show i ++ " inc 16") (r @=? runtestinc 16 v)
-            ]
-
-katTests :: [TestTree]
-katTests = map makeTestAlg results
-
-
-main = defaultMain $ testGroup "cryptohash"
-    [ testGroup "KATs" katTests
-    ]
diff --git a/cbits/bitfn.h b/cbits/bitfn.h
deleted file mode 100644
--- a/cbits/bitfn.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2006-2009 Vincent Hanquez <vincent@snarc.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef BITFN_H
-#define BITFN_H
-#include <stdint.h>
-
-#ifndef NO_INLINE_ASM
-/**********************************************************/
-# if (defined(__i386__))
-#  define ARCH_HAS_SWAP32
-static inline uint32_t bitfn_swap32(uint32_t a)
-{
-	asm ("bswap %0" : "=r" (a) : "0" (a));
-	return a;
-}
-/**********************************************************/
-# elif (defined(__arm__))
-#  define ARCH_HAS_SWAP32
-static inline uint32_t bitfn_swap32(uint32_t a)
-{
-	uint32_t tmp = a;
-	asm volatile ("eor %1, %0, %0, ror #16\n"
-	              "bic %1, %1, #0xff0000\n"
-	              "mov %0, %0, ror #8\n"
-	              "eor %0, %0, %1, lsr #8\n"
-	             : "=r" (a), "=r" (tmp) : "0" (a), "1" (tmp));
-	return a;
-}
-/**********************************************************/
-# elif defined(__x86_64__)
-#  define ARCH_HAS_SWAP32
-#  define ARCH_HAS_SWAP64
-static inline uint32_t bitfn_swap32(uint32_t a)
-{
-	asm ("bswap %0" : "=r" (a) : "0" (a));
-	return a;
-}
-
-static inline uint64_t bitfn_swap64(uint64_t a)
-{
-	asm ("bswap %0" : "=r" (a) : "0" (a));
-	return a;
-}
-
-# endif
-#endif /* NO_INLINE_ASM */
-/**********************************************************/
-
-#ifndef ARCH_HAS_ROL32
-static inline uint32_t rol32(uint32_t word, uint32_t shift)
-{
-	return (word << shift) | (word >> (32 - shift));
-}
-#endif
-
-#ifndef ARCH_HAS_ROR32
-static inline uint32_t ror32(uint32_t word, uint32_t shift)
-{
-	return (word >> shift) | (word << (32 - shift));
-}
-#endif
-
-#ifndef ARCH_HAS_ROL64
-static inline uint64_t rol64(uint64_t word, uint32_t shift)
-{
-	return (word << shift) | (word >> (64 - shift));
-}
-#endif
-
-#ifndef ARCH_HAS_ROR64
-static inline uint64_t ror64(uint64_t word, uint32_t shift)
-{
-	return (word >> shift) | (word << (64 - shift));
-}
-#endif
-
-#ifndef ARCH_HAS_SWAP32
-static inline uint32_t bitfn_swap32(uint32_t a)
-{
-	return (a << 24) | ((a & 0xff00) << 8) | ((a >> 8) & 0xff00) | (a >> 24);
-}
-#endif
-
-#ifndef ARCH_HAS_ARRAY_SWAP32
-static inline void array_swap32(uint32_t *d, uint32_t *s, uint32_t nb)
-{
-	while (nb--)
-		*d++ = bitfn_swap32(*s++);
-}
-#endif
-
-#ifndef ARCH_HAS_SWAP64
-static inline uint64_t bitfn_swap64(uint64_t a)
-{
-	return ((uint64_t) bitfn_swap32((uint32_t) (a >> 32))) |
-	       (((uint64_t) bitfn_swap32((uint32_t) a)) << 32);
-}
-#endif
-
-#ifndef ARCH_HAS_ARRAY_SWAP64
-static inline void array_swap64(uint64_t *d, uint64_t *s, uint32_t nb)
-{
-	while (nb--)
-		*d++ = bitfn_swap64(*s++);
-}
-#endif
-
-#ifndef ARCH_HAS_MEMORY_ZERO
-static inline void memory_zero(void *ptr, uint32_t len)
-{
-	uint32_t *ptr32 = ptr;
-	uint8_t *ptr8;
-	int i;
-
-	for (i = 0; i < len / 4; i++)
-		*ptr32++ = 0;
-	if (len % 4) {
-		ptr8 = (uint8_t *) ptr32;
-		for (i = len % 4; i >= 0; i--)
-			ptr8[i] = 0;
-	}
-}
-#endif
-
-#ifndef ARCH_HAS_ARRAY_COPY32
-static inline void array_copy32(uint32_t *d, uint32_t *s, uint32_t nb)
-{
-	while (nb--) *d++ = *s++;
-}
-#endif
-
-#ifndef ARCH_HAS_ARRAY_COPY64
-static inline void array_copy64(uint64_t *d, uint64_t *s, uint32_t nb)
-{
-	while (nb--) *d++ = *s++;
-}
-#endif
-
-#ifdef __MINGW32__
-  # define LITTLE_ENDIAN 1234
-  # define BYTE_ORDER    LITTLE_ENDIAN
-#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
-  # include <sys/endian.h>
-#elif defined(__OpenBSD__) || defined(__SVR4)
-  # include <sys/types.h>
-#elif defined(__APPLE__)
-  # include <machine/endian.h>
-#elif defined( BSD ) && ( BSD >= 199103 )
-  # include <machine/endian.h>
-#elif defined( __QNXNTO__ ) && defined( __LITTLEENDIAN__ )
-  # define LITTLE_ENDIAN 1234
-  # define BYTE_ORDER    LITTLE_ENDIAN
-#elif defined( __QNXNTO__ ) && defined( __BIGENDIAN__ )
-  # define BIG_ENDIAN 1234
-  # define BYTE_ORDER    BIG_ENDIAN
-#elif defined( _AIX )
-  # include <sys/machine.h>
-#else
-  # include <endian.h>
-#endif
-/* big endian to cpu */
-#if LITTLE_ENDIAN == BYTE_ORDER
-
-# define be32_to_cpu(a) bitfn_swap32(a)
-# define cpu_to_be32(a) bitfn_swap32(a)
-# define le32_to_cpu(a) (a)
-# define cpu_to_le32(a) (a)
-# define be64_to_cpu(a) bitfn_swap64(a)
-# define cpu_to_be64(a) bitfn_swap64(a)
-# define le64_to_cpu(a) (a)
-# define cpu_to_le64(a) (a)
-
-# define cpu_to_le32_array(d, s, l) array_copy32(d, s, l)
-# define le32_to_cpu_array(d, s, l) array_copy32(d, s, l)
-# define cpu_to_be32_array(d, s, l) array_swap32(d, s, l)
-# define be32_to_cpu_array(d, s, l) array_swap32(d, s, l)
-
-# define cpu_to_le64_array(d, s, l) array_copy64(d, s, l)
-# define le64_to_cpu_array(d, s, l) array_copy64(d, s, l)
-# define cpu_to_be64_array(d, s, l) array_swap64(d, s, l)
-# define be64_to_cpu_array(d, s, l) array_swap64(d, s, l)
-
-# define ror32_be(a, s) rol32(a, s)
-# define rol32_be(a, s) ror32(a, s)
-
-# define ARCH_IS_LITTLE_ENDIAN
-
-#elif BIG_ENDIAN == BYTE_ORDER
-
-# define be32_to_cpu(a) (a)
-# define cpu_to_be32(a) (a)
-# define be64_to_cpu(a) (a)
-# define cpu_to_be64(a) (a)
-# define le64_to_cpu(a) bitfn_swap64(a)
-# define cpu_to_le64(a) bitfn_swap64(a)
-# define le32_to_cpu(a) bitfn_swap32(a)
-# define cpu_to_le32(a) bitfn_swap32(a)
-
-# define cpu_to_le32_array(d, s, l) array_swap32(d, s, l)
-# define le32_to_cpu_array(d, s, l) array_swap32(d, s, l)
-# define cpu_to_be32_array(d, s, l) array_copy32(d, s, l)
-# define be32_to_cpu_array(d, s, l) array_copy32(d, s, l)
-
-# define cpu_to_le64_array(d, s, l) array_swap64(d, s, l)
-# define le64_to_cpu_array(d, s, l) array_swap64(d, s, l)
-# define cpu_to_be64_array(d, s, l) array_copy64(d, s, l)
-# define be64_to_cpu_array(d, s, l) array_copy64(d, s, l)
-
-# define ror32_be(a, s) ror32(a, s)
-# define rol32_be(a, s) rol32(a, s)
-
-# define ARCH_IS_BIG_ENDIAN
-
-#else
-# error "endian not supported"
-#endif
-
-#endif /* !BITFN_H */
diff --git a/cbits/sha256.c b/cbits/sha256.c
--- a/cbits/sha256.c
+++ b/cbits/sha256.c
@@ -23,15 +23,67 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <string.h>
 #include "sha256.h"
-#include "bitfn.h"
 
+#include <assert.h>
+#include <string.h>
+#include <ghcautoconf.h>
+
+#if defined(static_assert)
+static_assert(sizeof(struct sha256_ctx) == SHA256_CTX_SIZE, "unexpected sha256_ctx size");
+#else
+// poor man's pre-C11 _Static_assert
+typedef char static_assertion__unexpected_sha256_ctx_size[(sizeof(struct sha256_ctx) == SHA256_CTX_SIZE)?1:-1];
+#endif
+
+static inline uint32_t
+ror32(uint32_t word, uint32_t shift)
+{
+  // GCC usually transforms this into a 'ror'-insn
+  return (word >> shift) | (word << (32 - shift));
+}
+
+static inline uint32_t
+hs_htonl(uint32_t hl)
+{
+#if WORDS_BIGENDIAN
+  return hl;
+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+  return __builtin_bswap32(hl);
+#else
+  // GCC usually transforms this into a bswap insn
+  return ((hl & 0xff000000) >> 24) |
+         ((hl & 0x00ff0000) >> 8)  |
+         ((hl & 0x0000ff00) << 8)  |
+         ( hl               << 24);
+#endif
+}
+
+static inline void
+hs_htonl_array(uint32_t *dest, const uint32_t *src, unsigned wordcnt)
+{
+  while (wordcnt--)
+    *dest++ = hs_htonl(*src++);
+}
+
+static inline uint64_t
+hs_htonll(uint64_t hll)
+{
+#if WORDS_BIGENDIAN
+  return hll;
+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+  return __builtin_bswap64(hll);
+#else
+  return ((uint64_t)hs_htonl(hll & 0xFFFFFFFF) << 32LL) | hs_htonl(hll >> 32);
+#endif
+}
+
+
 void
 hs_cryptohash_sha256_init (struct sha256_ctx *ctx)
 {
 	memset(ctx, 0, sizeof(*ctx));
-
+        
 	ctx->h[0] = 0x6a09e667;
 	ctx->h[1] = 0xbb67ae85;
 	ctx->h[2] = 0x3c6ef372;
@@ -69,7 +121,7 @@
 	int i;
 	uint32_t w[64];
 
-	cpu_to_be32_array(w, buf, 16);
+	hs_htonl_array(w, buf, 16);
 	for (i = 16; i < 64; i++)
 		w[i] = s1(w[i - 2]) + w[i - 7] + s0(w[i - 15]) + w[i - 16];
 
@@ -100,12 +152,12 @@
 }
 
 void
-hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, uint32_t len)
+hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, size_t len)
 {
-	uint32_t index, to_fill;
+	size_t index, to_fill;
 
 	/* check for partial buffer */
-	index = (uint32_t) (ctx->sz & 0x3f);
+	index = (size_t) (ctx->sz & 0x3f);
 	to_fill = 64 - index;
 
 	ctx->sz += len;
@@ -133,11 +185,10 @@
 {
 	static uint8_t padding[64] = { 0x80, };
 	uint64_t bits;
-	uint32_t i, index, padlen;
-	uint32_t *p = (uint32_t *) out;
+	uint32_t index, padlen;
 
 	/* cpu -> big endian */
-	bits = cpu_to_be64(ctx->sz << 3);
+	bits = hs_htonll(ctx->sz << 3);
 
 	/* pad out to 56 */
 	index = (uint32_t) (ctx->sz & 0x3f);
@@ -148,6 +199,5 @@
 	hs_cryptohash_sha256_update(ctx, (uint8_t *) &bits, sizeof(bits));
 
 	/* store to digest */
-	for (i = 0; i < 8; i++)
-		p[i] = cpu_to_be32(ctx->h[i]);
+        hs_htonl_array((uint32_t *) out, ctx->h, 8);
 }
diff --git a/cbits/sha256.h b/cbits/sha256.h
--- a/cbits/sha256.h
+++ b/cbits/sha256.h
@@ -27,19 +27,21 @@
 #define CRYPTOHASH_SHA256_H
 
 #include <stdint.h>
+#include <stddef.h>
 
 struct sha256_ctx
 {
 	uint64_t sz;
-	uint8_t  buf[128];
+	uint8_t  buf[64];
 	uint32_t h[8];
 };
 
+/* keep this synchronised with 'digestSize'/'sizeCtx' in SHA256.hs */
 #define SHA256_DIGEST_SIZE	32
-#define SHA256_CTX_SIZE		sizeof(struct sha256_ctx)
+#define SHA256_CTX_SIZE		104
 
 void hs_cryptohash_sha256_init (struct sha256_ctx *ctx);
-void hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, uint32_t len);
+void hs_cryptohash_sha256_update (struct sha256_ctx *ctx, const uint8_t *data, size_t len);
 void hs_cryptohash_sha256_finalize (struct sha256_ctx *ctx, uint8_t *out);
 
 #endif
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,11 @@
+## 0.11.7.2
+
+ - switch to 'safe' FFI for calls where overhead becomes neglible
+ - removed inline assembly in favour of portable C constructs
+ - fix 32bit length overflow bug in `hash` function
+ - fix inaccurate context-size
+ - add context-size verification to incremental API operations
+
+## 0.11.7.1
+
+ - first version forked off `cryptohash-0.11.7` release
diff --git a/cryptohash-sha256.cabal b/cryptohash-sha256.cabal
--- a/cryptohash-sha256.cabal
+++ b/cryptohash-sha256.cabal
@@ -1,5 +1,5 @@
 name:                cryptohash-sha256
-version:             0.11.7.1
+version:             0.11.7.2
 description:
     A practical incremental and one-pass, pure API to the
     <https://en.wikipedia.org/wiki/SHA-2 SHA-256 hash algorithm>
@@ -7,7 +7,7 @@
     .
     The implementation is made in C with a haskell FFI wrapper that hides the C implementation.
     .
-    NOTE: This package has been forked off @cryptohash-0.11.7@ because the @cryptohash@ has been
+    NOTE: This package has been forked off @cryptohash-0.11.7@ because the @cryptohash@ package has been
     deprecated and so this package continues to satisfy the need for a lightweight package
     providing the SHA256 hash algorithm without any dependencies on packages other than
     @base@ and @bytestring@.
@@ -31,8 +31,8 @@
                    , GHC == 7.10.3
                    , GHC == 8.0.1
 
-extra-source-files:  cbits/bitfn.h
-                     cbits/sha256.h
+extra-source-files:  cbits/sha256.h
+                     changelog.md
 
 source-repository head
   type:     git
@@ -43,39 +43,34 @@
   build-depends:     base             >= 4.5   && < 4.10
                    , bytestring       >= 0.9.2 && < 0.11
 
+  hs-source-dirs:    src
   exposed-modules:   Crypto.Hash.SHA256
   ghc-options:       -Wall -fno-cse -O2
   cc-options:        -O3
   c-sources:         cbits/sha256.c
   include-dirs:      cbits
 
-test-suite test-kat
+test-suite test-sha256
   default-language:  Haskell2010
   type:              exitcode-stdio-1.0
-  hs-source-dirs:    Tests
-  main-is:           KAT.hs
+  hs-source-dirs:    src-tests
+  main-is:           test-sha256.hs
+  ghc-options:       -Wall -threaded
   build-depends:     cryptohash-sha256
                    , base
                    , bytestring
-                   , tasty            == 0.11.*
-                   , tasty-quickcheck == 0.8.*
-                   , tasty-hunit      == 0.9.*
 
-benchmark bench-hashes
-  default-language:  Haskell2010
-  type:              exitcode-stdio-1.0
-  main-is:           Bench.hs
-  hs-source-dirs:    Bench
-  build-depends:     cryptohash-sha256
-                   , base
-                   , bytestring
-                   , criterion        == 1.1.*
+                   , base16-bytestring >= 0.1.1  && < 0.2
+                   , SHA               >= 1.6.4  && < 1.7
+                   , tasty             == 0.11.*
+                   , tasty-quickcheck  == 0.8.*
+                   , tasty-hunit       == 0.9.*
 
-benchmark bench-api
+benchmark bench-sha256
   default-language:  Haskell2010
   type:              exitcode-stdio-1.0
-  main-is:           BenchAPI.hs
-  hs-source-dirs:    Bench
+  main-is:           bench-sha256.hs
+  hs-source-dirs:    src-bench
   build-depends:     cryptohash-sha256
                    , base
                    , bytestring
diff --git a/src-bench/bench-sha256.hs b/src-bench/bench-sha256.hs
new file mode 100644
--- /dev/null
+++ b/src-bench/bench-sha256.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE BangPatterns #-}
+
+import           Criterion.Main
+import qualified Crypto.Hash.SHA256   as SHA256
+import qualified Data.ByteString      as B
+import qualified Data.ByteString.Lazy as L
+
+benchSize :: Int -> Benchmark
+benchSize sz = bs `seq` bench msg (whnf SHA256.hash bs)
+  where
+    bs = B.replicate sz 0
+    msg = "bs-" ++ show sz
+
+main :: IO ()
+main = do
+    let !lbs64x256  = L.fromChunks $ replicate 4  (B.replicate 64 0)
+        !lbs64x4096 = L.fromChunks $ replicate 64 (B.replicate 64 0)
+    defaultMain
+        [ bgroup "cryptohash-sha256"
+          [ benchSize 0
+          , benchSize 8
+          , benchSize 32
+          , benchSize 64
+          , benchSize 128
+          , benchSize 256
+          , benchSize 1024
+          , benchSize 4096
+          , benchSize (128*1024)
+          , benchSize (1024*1024)
+          , benchSize (2*1024*1024)
+          , benchSize (4*1024*1024)
+
+          , L.length lbs64x256  `seq` bench "lbs64x256"  (whnf SHA256.hashlazy lbs64x256)
+          , L.length lbs64x4096 `seq` bench "lbs64x4096" (whnf SHA256.hashlazy lbs64x4096)
+          ]
+        ]
diff --git a/src-tests/test-sha256.hs b/src-tests/test-sha256.hs
new file mode 100644
--- /dev/null
+++ b/src-tests/test-sha256.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main (main) where
+
+import           Data.ByteString        (ByteString)
+import qualified Data.ByteString        as B
+import qualified Data.ByteString.Lazy   as BL
+import qualified Data.ByteString.Base16 as B16
+
+-- reference implementation
+import qualified Data.Digest.Pure.SHA as REF
+
+-- implementation under test
+import qualified Crypto.Hash.SHA256     as IUT
+
+import           Test.Tasty
+import           Test.Tasty.HUnit
+import           Test.Tasty.QuickCheck as QC
+
+vectors :: [ByteString]
+vectors =
+    [ ""
+    , "The quick brown fox jumps over the lazy dog"
+    , "The quick brown fox jumps over the lazy cog"
+    , "abc"
+    , "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+    , "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+    , B.replicate 1000000 0x61
+    ]
+
+answers :: [ByteString]
+answers = map (B.filter (/= 0x20))
+    [ "e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855"
+    , "d7a8fbb3 07d78094 69ca9abc b0082e4f 8d5651e4 6d3cdb76 2d02d0bf 37c9e592"
+    , "e4c4d8f3 bf76b692 de791a17 3e053211 50f7a345 b46484fe 427f6acc 7ecc81be"
+    , "ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad"
+    , "248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1"
+    , "cf5b16a7 78af8380 036ce59e 7b049237 0b249b11 e8f07a51 afac4503 7afee9d1"
+    , "cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0"
+    ]
+
+ansXLTest :: ByteString
+ansXLTest = B.filter (/= 0x20)
+    "50e72a0e 26442fe2 552dc393 8ac58658 228c0cbf b1d2ca87 2ae43526 6fcd055e"
+
+katTests :: [TestTree]
+katTests
+  | length vectors == length answers = map makeTest (zip3 [1::Int ..] vectors answers) ++ [xltest]
+  | otherwise = error "vectors/answers length mismatch"
+  where
+    makeTest (i, v, r) = testGroup ("vec"++show i) $
+        [ testCase "one-pass" (r @=? runTest v)
+        , testCase "inc-1"    (r @=? runTestInc 1 v)
+        , testCase "inc-2"    (r @=? runTestInc 2 v)
+        , testCase "inc-3"    (r @=? runTestInc 3 v)
+        , testCase "inc-4"    (r @=? runTestInc 4 v)
+        , testCase "inc-5"    (r @=? runTestInc 5 v)
+        , testCase "inc-7"    (r @=? runTestInc 7 v)
+        , testCase "inc-8"    (r @=? runTestInc 8 v)
+        , testCase "inc-9"    (r @=? runTestInc 9 v)
+        , testCase "inc-16"   (r @=? runTestInc 16 v)
+        , testCase "lazy-1"   (r @=? runTestLazy 1 v)
+        , testCase "lazy-2"   (r @=? runTestLazy 2 v)
+        , testCase "lazy-7"   (r @=? runTestLazy 7 v)
+        , testCase "lazy-8"   (r @=? runTestLazy 8 v)
+        , testCase "lazy-16"  (r @=? runTestLazy 16 v)
+        ]
+
+    runTest :: ByteString -> ByteString
+    runTest = B16.encode . IUT.hash
+
+    runTestInc :: Int -> ByteString -> ByteString
+    runTestInc i = B16.encode . IUT.finalize . myfoldl' IUT.update IUT.init . splitB i
+
+    runTestLazy :: Int -> ByteString -> ByteString
+    runTestLazy i = B16.encode . IUT.hashlazy . BL.fromChunks . splitB i
+
+    ----
+
+    xltest = testGroup "XL-vec"
+        [ testCase "inc" (ansXLTest @=? (B16.encode . IUT.hashlazy) vecXL) ]
+      where
+        vecXL = BL.fromChunks (replicate 16777216 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno")
+
+    splitB :: Int -> ByteString -> [ByteString]
+    splitB l b
+      | B.length b > l = b1 : splitB l b2
+      | otherwise = [b]
+      where
+        (b1, b2) = B.splitAt l b
+
+
+-- define own 'foldl' here to avoid RULE rewriting to 'hashlazy'
+myfoldl' :: (b -> a -> b) -> b -> [a] -> b
+myfoldl' f z0 xs0 = lgo z0 xs0
+  where
+    lgo z []     = z
+    lgo z (x:xs) = let z' = f z x
+                   in z' `seq` lgo z' xs
+
+newtype RandBS = RandBS { unRandBS :: ByteString }
+newtype RandLBS = RandLBS BL.ByteString
+
+instance Arbitrary RandBS where
+    arbitrary = fmap (RandBS . B.pack) arbitrary
+    shrink (RandBS x) = fmap RandBS (go x)
+      where
+        go bs = zipWith B.append (B.inits bs) (tail $ B.tails bs)
+
+instance Show RandBS where
+    show (RandBS x) = "RandBS {len=" ++ show (B.length x)++"}"
+
+instance Arbitrary RandLBS where
+    arbitrary = fmap (RandLBS . BL.fromChunks . map unRandBS) arbitrary
+
+instance Show RandLBS where
+    show (RandLBS x) = "RandLBS {len=" ++ show (BL.length x) ++ ", chunks=" ++ show (length $ BL.toChunks x)++"}"
+
+
+refImplTests :: [TestTree]
+refImplTests =
+    [ testProperty "hash" prop_hash
+    , testProperty "hashlazy" prop_hashlazy
+    ]
+  where
+    prop_hash (RandBS bs)
+        = ref_hash bs == IUT.hash bs
+
+    prop_hashlazy (RandLBS bs)
+        = ref_hashlazy bs == IUT.hashlazy bs
+
+    ref_hash :: ByteString -> ByteString
+    ref_hash = toStrict . REF.bytestringDigest . REF.sha256 . fromStrict
+
+    ref_hashlazy :: BL.ByteString -> ByteString
+    ref_hashlazy = toStrict . REF.bytestringDigest . REF.sha256
+
+    -- toStrict/fromStrict only available with bytestring-0.10 and later
+    toStrict = B.concat . BL.toChunks
+    fromStrict = BL.fromChunks . (:[])
+
+main :: IO ()
+main = defaultMain $ testGroup "cryptohash-sha256"
+    [ testGroup "KATs" katTests
+    , testGroup "REF" refImplTests
+    ]
diff --git a/src/Crypto/Hash/SHA256.hs b/src/Crypto/Hash/SHA256.hs
new file mode 100644
--- /dev/null
+++ b/src/Crypto/Hash/SHA256.hs
@@ -0,0 +1,215 @@
+-- |
+-- Module      : Crypto.Hash.SHA256
+-- License     : BSD-style
+-- Maintainer  : Herbert Valerio Riedel <hvr@gnu.org>
+-- Stability   : stable
+-- Portability : unknown
+--
+-- A module containing <https://en.wikipedia.org/wiki/SHA-2 SHA-256> bindings
+--
+module Crypto.Hash.SHA256
+    (
+
+    -- * Incremental API
+    --
+    -- | This API is based on 4 different functions, similar to the
+    -- lowlevel operations of a typical hash:
+    --
+    --  - 'init': create a new hash context
+    --  - 'update': update non-destructively a new hash context with a strict bytestring
+    --  - 'updates': same as update, except that it takes a list of strict bytestrings
+    --  - 'finalize': finalize the context and returns a digest bytestring.
+    --
+    -- all those operations are completely pure, and instead of
+    -- changing the context as usual in others language, it
+    -- re-allocates a new context each time.
+    --
+    -- Example:
+    --
+    -- > import qualified Data.ByteString
+    -- > import qualified Crypto.Hash.SHA256 as SHA256
+    -- >
+    -- > main = print digest
+    -- >   where
+    -- >     digest = SHA256.finalize ctx
+    -- >     ctx    = foldl SHA256.update ctx0 (map Data.ByteString.pack [ [1,2,3], [4,5,6] ])
+    -- >     ctx0   = SHA256.init
+
+      Ctx(..)
+    , init     -- :: Ctx
+    , update   -- :: Ctx -> ByteString -> Ctx
+    , updates  -- :: Ctx -> [ByteString] -> Ctx
+    , finalize -- :: Ctx -> ByteString
+
+    -- * Single Pass API
+    --
+    -- | This API use the incremental API under the hood to provide
+    -- the common all-in-one operations to create digests out of a
+    -- 'ByteString' and lazy 'L.ByteString'.
+    --
+    --  - 'hash': create a digest ('init' + 'update' + 'finalize') from a strict 'ByteString'
+    --  - 'hashlazy': create a digest ('init' + 'update' + 'finalize') from a lazy 'L.ByteString'
+    --
+    -- Example:
+    --
+    -- > import qualified Data.ByteString
+    -- > import qualified Crypto.Hash.SHA256 as SHA256
+    -- >
+    -- > main = print $ SHA256.hash (Data.ByteString.pack [0..255])
+    --
+    -- __NOTE__: The returned digest is a binary 'ByteString'. For
+    -- converting to a base16/hex encoded digest the
+    -- <https://hackage.haskell.org/package/base16-bytestring base16-bytestring>
+    -- package is recommended.
+
+    , hash     -- :: ByteString -> ByteString
+    , hashlazy -- :: ByteString -> ByteString
+    ) where
+
+import Prelude hiding (init)
+import Foreign.C.Types
+import Foreign.Ptr
+import Foreign.ForeignPtr (withForeignPtr)
+import Foreign.Marshal.Alloc
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString as B
+import Data.ByteString (ByteString)
+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, toForeignPtr, memcpy)
+import Data.Word
+import System.IO.Unsafe (unsafeDupablePerformIO)
+
+-- | perform IO for hashes that do allocation and ffi.
+-- unsafeDupablePerformIO is used when possible as the
+-- computation is pure and the output is directly linked
+-- to the input. we also do not modify anything after it has
+-- been returned to the user.
+unsafeDoIO :: IO a -> a
+unsafeDoIO = unsafeDupablePerformIO
+
+-- | SHA-256 Context
+--
+-- The context data is exactly 104 bytes long, however
+-- the data in the context is stored in host-endianness.
+--
+-- The context data is made up of
+--
+--  * a 'Word64' representing the number of bytes already feed to hash algorithm so far,
+--
+--  * a 64-element 'Word8' buffer holding partial input-chunks, and finally
+--
+--  * a 8-element 'Word32' array holding the current work-in-progress digest-value.
+--
+-- Consequently, a SHA-256 digest as produced by 'hash', 'hashlazy', or 'finalize' is 32 bytes long.
+newtype Ctx = Ctx ByteString
+
+-- keep this synchronised with cbits/sha256.h
+{-# INLINE digestSize #-}
+digestSize :: Int
+digestSize = 32
+
+{-# INLINE sizeCtx #-}
+sizeCtx :: Int
+sizeCtx = 104
+
+{-# RULES "digestSize" B.length (finalize init) = digestSize #-}
+{-# RULES "hash" forall b. finalize (update init b) = hash b #-}
+{-# RULES "hash.list1" forall b. finalize (updates init [b]) = hash b #-}
+{-# RULES "hashmany" forall b. finalize (foldl update init b) = hashlazy (L.fromChunks b) #-}
+{-# RULES "hashlazy" forall b. finalize (foldl update init $ L.toChunks b) = hashlazy b #-}
+
+{-# INLINE withByteStringPtr #-}
+withByteStringPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a
+withByteStringPtr b f =
+    withForeignPtr fptr $ \ptr -> f (ptr `plusPtr` off)
+    where (fptr, off, _) = toForeignPtr b
+
+copyCtx :: Ptr Ctx -> Ptr Ctx -> IO ()
+copyCtx dst src = memcpy (castPtr dst) (castPtr src) (fromIntegral sizeCtx)
+
+withCtxCopy :: Ctx -> (Ptr Ctx -> IO ()) -> IO Ctx
+withCtxCopy (Ctx ctxB) f = Ctx `fmap` createCtx
+  where
+    createCtx = create sizeCtx $ \dstPtr ->
+                withByteStringPtr ctxB $ \srcPtr -> do
+                    copyCtx (castPtr dstPtr) (castPtr srcPtr)
+                    f (castPtr dstPtr)
+
+withCtxThrow :: Ctx -> (Ptr Ctx -> IO a) -> IO a
+withCtxThrow (Ctx ctxB) f =
+    allocaBytes sizeCtx $ \dstPtr ->
+    withByteStringPtr ctxB $ \srcPtr -> do
+        copyCtx (castPtr dstPtr) (castPtr srcPtr)
+        f (castPtr dstPtr)
+
+withCtxNew :: (Ptr Ctx -> IO ()) -> IO Ctx
+withCtxNew f = Ctx `fmap` create sizeCtx (f . castPtr)
+
+withCtxNewThrow :: (Ptr Ctx -> IO a) -> IO a
+withCtxNewThrow f = allocaBytes sizeCtx (f . castPtr)
+
+foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_init"
+    c_sha256_init :: Ptr Ctx -> IO ()
+
+foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_update"
+    c_sha256_update_unsafe :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()
+
+foreign import ccall safe "sha256.h hs_cryptohash_sha256_update"
+    c_sha256_update_safe :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()
+
+-- 'safe' call overhead neglible for 4KiB and more
+c_sha256_update :: Ptr Ctx -> Ptr Word8 -> CSize -> IO ()
+c_sha256_update pctx pbuf sz
+  | sz < 4096 = c_sha256_update_unsafe pctx pbuf sz
+  | otherwise = c_sha256_update_safe   pctx pbuf sz
+
+foreign import ccall unsafe "sha256.h hs_cryptohash_sha256_finalize"
+    c_sha256_finalize :: Ptr Ctx -> Ptr Word8 -> IO ()
+
+updateInternalIO :: Ptr Ctx -> ByteString -> IO ()
+updateInternalIO ptr d =
+    unsafeUseAsCStringLen d (\(cs, len) -> c_sha256_update ptr (castPtr cs) (fromIntegral len))
+
+finalizeInternalIO :: Ptr Ctx -> IO ByteString
+finalizeInternalIO ptr = create digestSize (c_sha256_finalize ptr)
+
+{-# NOINLINE init #-}
+-- | create a new hash context
+init :: Ctx
+init = unsafeDoIO $ withCtxNew $ c_sha256_init
+
+validCtx :: Ctx -> Bool
+validCtx (Ctx b) = B.length b == sizeCtx
+
+{-# NOINLINE update #-}
+-- | update a context with a bytestring
+update :: Ctx -> ByteString -> Ctx
+update ctx d
+  | validCtx ctx = unsafeDoIO $ withCtxCopy ctx $ \ptr -> updateInternalIO ptr d
+  | otherwise    = error "SHA256.update: invalid Ctx"
+
+{-# NOINLINE updates #-}
+-- | updates a context with multiple bytestrings
+updates :: Ctx -> [ByteString] -> Ctx
+updates ctx d
+  | validCtx ctx = unsafeDoIO $ withCtxCopy ctx $ \ptr -> mapM_ (updateInternalIO ptr) d
+  | otherwise    = error "SHA256.updates: invalid Ctx"
+
+{-# NOINLINE finalize #-}
+-- | finalize the context into a digest bytestring (32 bytes)
+finalize :: Ctx -> ByteString
+finalize ctx
+  | validCtx ctx = unsafeDoIO $ withCtxThrow ctx finalizeInternalIO
+  | otherwise    = error "SHA256.finalize: invalid Ctx"
+
+{-# NOINLINE hash #-}
+-- | hash a strict bytestring into a digest bytestring (32 bytes)
+hash :: ByteString -> ByteString
+hash d = unsafeDoIO $ withCtxNewThrow $ \ptr -> do
+    c_sha256_init ptr >> updateInternalIO ptr d >> finalizeInternalIO ptr
+
+{-# NOINLINE hashlazy #-}
+-- | hash a lazy bytestring into a digest bytestring (32 bytes)
+hashlazy :: L.ByteString -> ByteString
+hashlazy l = unsafeDoIO $ withCtxNewThrow $ \ptr -> do
+    c_sha256_init ptr >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr
