diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # lz4
 
+## 0.1.4.0
+
+  * Add `compressSz` supporting higher levels of compression for streams
+
 ## 0.1.3.0
 
   * Add `decompressBufSz` for frames
diff --git a/lz4-hs.cabal b/lz4-hs.cabal
--- a/lz4-hs.cabal
+++ b/lz4-hs.cabal
@@ -1,6 +1,6 @@
 cabal-version:   1.18
 name:            lz4-hs
-version:         0.1.3.0
+version:         0.1.4.0
 license:         BSD3
 license-file:    LICENSE
 copyright:       Copyright: (c) 2020 Vanessa McHale
diff --git a/src/Codec/Lz4.chs b/src/Codec/Lz4.chs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Lz4.chs
@@ -0,0 +1,197 @@
+{-# LANGUAGE BangPatterns #-}
+
+-- | The functions in this module throw exceptions on error.
+--
+-- 'decompress' and 'compress' are fully lazy, i.e. memory efficient.
+module Codec.Lz4 ( -- * Functions for working with blocks
+                   compressBlock
+                 , decompressBlockSz
+                 , lZ4MaxInputSize
+                 , compressBlockHC
+                 , lZ4HCClevelMax
+                 -- * Functions for working with frames
+                 , compress
+                 , compressSz
+                 , decompress
+                 , decompressBufSz
+                 -- * Version info
+                 , lZ4VersionNumber
+                 , lZ4VersionString
+                 ) where
+
+import           Codec.Lz4.Foreign
+import           Control.Monad                (when)
+import           Control.Monad.ST.Lazy        (runST)
+import qualified Control.Monad.ST.Lazy        as LazyST
+import qualified Control.Monad.ST.Lazy.Unsafe as LazyST
+import qualified Data.ByteString              as BS
+import qualified Data.ByteString.Internal     as BS
+import qualified Data.ByteString.Lazy         as BSL
+import qualified Data.ByteString.Unsafe       as BS
+import           Foreign.C.String             (CString)
+import           Foreign.C.Types              (CInt)
+import           Foreign.Ptr                  (castPtr, nullPtr)
+import           Foreign.ForeignPtr           (ForeignPtr, castForeignPtr,
+                                               mallocForeignPtrBytes,
+                                               newForeignPtr, withForeignPtr)
+import           Foreign.Marshal.Alloc        (alloca)
+import           Foreign.Storable             (peek, poke)
+import           System.IO.Unsafe             (unsafePerformIO)
+
+#include <lz4frame.h>
+
+check :: LZ4FErrorCode -> IO ()
+check err = when (lZ4FIsError err) $ error (lZ4FGetErrorName err)
+
+-- | Lazily decompress a frame
+decompress :: BSL.ByteString -> BSL.ByteString
+decompress = decompressBufSz (64 * 1014)
+
+-- | @since 0.1.3.0
+decompressBufSz :: Int -- ^ Size of the output buffer
+                -> BSL.ByteString
+                -> BSL.ByteString
+decompressBufSz bufSz bs = runST $ do
+
+    let bss = BSL.toChunks bs
+
+    (ctx, buf) <- LazyST.unsafeIOToST $ do
+        (err, preCtx) <- lZ4FCreateDecompressionContext lZ4FGetVersion
+        ctx <- castForeignPtr <$> newForeignPtr lZ4FFreeCompressionContext (castPtr preCtx)
+        check err
+        dstBuf <- mallocForeignPtrBytes bufSz
+        pure (ctx, dstBuf)
+
+    BSL.fromChunks <$> loop ctx buf bss
+
+    where loop :: LzDecompressionCtxPtr -> ForeignPtr a -> [BS.ByteString] -> LazyST.ST s [BS.ByteString]
+          loop _ _ [] = pure []
+          loop ctx buf (b:bs') = do
+                (nxt, res) <- stepChunk ctx buf b
+                case nxt of
+                    Nothing   -> (res:) <$> loop ctx buf bs'
+                    Just next -> (res:) <$> loop ctx buf (next:bs')
+
+
+          stepChunk :: LzDecompressionCtxPtr -> ForeignPtr a -> BS.ByteString -> LazyST.ST s (Maybe BS.ByteString, BS.ByteString)
+          stepChunk !ctx !dst b = LazyST.unsafeIOToST $
+            BS.unsafeUseAsCStringLen b $ \(buf, sz) ->
+                withForeignPtr dst $ \d ->
+                    alloca $ \dSzPtr ->
+                        alloca $ \szPtr -> do
+                            poke dSzPtr (fromIntegral bufSz)
+                            poke szPtr (fromIntegral sz)
+                            res <- lZ4FDecompress ctx d dSzPtr buf szPtr nullPtr
+                            check res
+                            bRead <- peek szPtr
+                            bWritten <- peek dSzPtr
+                            outBs <- BS.packCStringLen (castPtr d, fromIntegral bWritten)
+                            let remBs = if fromIntegral bRead == sz
+                                then Nothing
+                                else Just (BS.drop (fromIntegral bRead) b)
+                            pure (remBs, outBs)
+
+-- | Lazily compress a frame.
+compress :: BSL.ByteString -> BSL.ByteString
+compress = compressSz 0
+
+-- | @since 0.1.4.0
+compressSz :: Int -> BSL.ByteString -> BSL.ByteString
+compressSz lvl bs = runST $ do
+
+    let bss = BSL.toChunks bs
+
+    (ctx, pref, header) <- initCtx
+    rest <- loop ctx pref bss
+
+    pure $ BSL.fromChunks (header:rest)
+
+    where initCtx :: LazyST.ST s (LzCtxPtr, LzPreferencesPtr, BS.ByteString)
+          initCtx = LazyST.unsafeIOToST $ do
+                (err, preCtx) <- lZ4FCreateCompressionContext lZ4FGetVersion
+                ctx <- castForeignPtr <$> newForeignPtr lZ4FFreeCompressionContext (castPtr preCtx)
+                check err
+                dst <- mallocForeignPtrBytes lZ4FHeaderSizeMax
+                pref <- mallocForeignPtrBytes {# sizeof LZ4F_preferences_t #}
+                preferencesPtr pref lvl
+                header <- withForeignPtr dst $ \d -> do
+                    res <- lZ4FCompressBegin ctx d lZ4FHeaderSizeMax pref
+                    check res
+                    BS.packCStringLen (castPtr d, fromIntegral res)
+                pure (ctx, pref, header)
+
+          loop :: LzCtxPtr -> LzPreferencesPtr -> [BS.ByteString] -> LazyST.ST s [BS.ByteString]
+          loop ctx pref []      = pure <$> finish ctx pref
+          loop ctx pref (b:bs') = (:) <$> update ctx pref b <*> loop ctx pref bs'
+
+          finish :: LzCtxPtr -> LzPreferencesPtr -> LazyST.ST s BS.ByteString
+          finish ctx pref = LazyST.unsafeIOToST $ do
+            let expectedSz = lZ4FCompressBound 0 pref
+            dst <- mallocForeignPtrBytes (fromIntegral expectedSz)
+            withForeignPtr dst $ \d -> do
+                res <- lZ4FCompressEnd ctx d expectedSz nullPtr
+                check res
+                BS.packCStringLen (castPtr d, fromIntegral res)
+
+          update :: LzCtxPtr -> LzPreferencesPtr -> BS.ByteString -> LazyST.ST s BS.ByteString
+          update !ctx !pref b = LazyST.unsafeIOToST $
+            BS.unsafeUseAsCStringLen b $ \(buf, sz) -> do
+                let expectedSz = lZ4FCompressBound (fromIntegral sz) pref
+                dst <- mallocForeignPtrBytes (fromIntegral expectedSz)
+                withForeignPtr dst $ \d -> do
+                    res <- lZ4FCompressUpdate ctx d expectedSz buf (fromIntegral sz) nullPtr
+                    check res
+                    BS.packCStringLen (castPtr d, fromIntegral res)
+
+{-# NOINLINE compressBlock #-}
+compressBlock :: BS.ByteString -> BS.ByteString
+compressBlock = unsafePerformIO . compressBlockGeneric lZ4CompressDefault
+
+{-# NOINLINE compressBlockHC #-}
+-- | @since 0.1.1.0
+compressBlockHC :: Int -- ^ Compression level (must be less than 'lZ4HCClevelMax')
+                -> BS.ByteString
+                -> BS.ByteString
+compressBlockHC lvl = unsafePerformIO . compressBlockGeneric (\src dst ssz dsz -> lZ4CompressHC src dst ssz dsz (fromIntegral lvl))
+
+compressBlockGeneric :: (CString -> CString -> CInt -> CInt -> IO CInt) -> BS.ByteString -> IO BS.ByteString
+compressBlockGeneric cfun bs =
+    BS.unsafeUseAsCStringLen bs $ \(buf, sz) -> do
+        let resSz = lZ4CompressBound (fromIntegral sz)
+        dst <- mallocForeignPtrBytes (fromIntegral resSz)
+        withForeignPtr dst $ \d -> do
+            bWritten <- cfun buf d (fromIntegral sz) resSz
+            when (bWritten == 0) $ error "Compression error"
+            pure $ BS.fromForeignPtr (castForeignPtr dst) 0 (fromIntegral bWritten)
+
+{-# NOINLINE decompressBlockSz #-}
+-- | Decompress a block. The size of the uncompressed data must be known.
+decompressBlockSz :: BS.ByteString
+                  -> Int -- ^ Decompressed size
+                  -> BS.ByteString
+decompressBlockSz bs expectedSz = unsafePerformIO $
+    BS.unsafeUseAsCStringLen bs $ \(buf, sz) -> do
+        dst <- mallocForeignPtrBytes expectedSz
+        withForeignPtr dst $ \d -> do
+            bWritten <- lZ4DecompressSafe buf d (fromIntegral sz) (fromIntegral expectedSz)
+            when (bWritten < 0) $ error "Decompression error"
+        pure $ BS.fromForeignPtr (castForeignPtr dst) 0 expectedSz
+
+cint :: Enum a => a -> CInt
+cint = fromIntegral . fromEnum
+
+preferencesPtr :: LzPreferencesPtr -> Int -> IO ()
+preferencesPtr fp i =
+    withForeignPtr fp $ \p -> do
+
+        {# set LZ4F_preferences_t.frameInfo.blockSizeID #} p (cint Lz4fDefault)
+        {# set LZ4F_preferences_t.frameInfo.blockMode #} p (cint Lz4fBlocklinked)
+        {# set LZ4F_preferences_t.frameInfo.contentChecksumFlag #} p (cint Lz4fNocontentchecksum)
+        {# set LZ4F_preferences_t.frameInfo.frameType #} p (cint Lz4fFrame)
+        {# set LZ4F_preferences_t.frameInfo.contentSize #} p 0
+        {# set LZ4F_preferences_t.frameInfo.dictID #} p 0
+        {# set LZ4F_preferences_t.frameInfo.blockChecksumFlag #} p (cint Lz4fNoblockchecksum)
+
+        {# set LZ4F_preferences_t.compressionLevel #} p (fromIntegral i)
+        {# set LZ4F_preferences_t.autoFlush #} p 0
+        {# set LZ4F_preferences_t.favorDecSpeed #} p 0
diff --git a/src/Codec/Lz4.hs b/src/Codec/Lz4.hs
deleted file mode 100644
--- a/src/Codec/Lz4.hs
+++ /dev/null
@@ -1,169 +0,0 @@
-{-# LANGUAGE BangPatterns #-}
-
--- | The functions in this module throw exceptions on error.
---
--- 'decompress' and 'compress' are fully lazy, i.e. memory efficient.
-module Codec.Lz4 ( -- * Functions for working with blocks
-                   compressBlock
-                 , decompressBlockSz
-                 , lZ4MaxInputSize
-                 , compressBlockHC
-                 , lZ4HCClevelMax
-                 -- * Functions for working with frames
-                 , compress
-                 , decompress
-                 , decompressBufSz
-                 -- * Version info
-                 , lZ4VersionNumber
-                 , lZ4VersionString
-                 ) where
-
-import           Codec.Lz4.Foreign
-import           Control.Monad                (when)
-import           Control.Monad.ST.Lazy        (runST)
-import qualified Control.Monad.ST.Lazy        as LazyST
-import qualified Control.Monad.ST.Lazy.Unsafe as LazyST
-import qualified Data.ByteString              as BS
-import qualified Data.ByteString.Internal     as BS
-import qualified Data.ByteString.Lazy         as BSL
-import qualified Data.ByteString.Unsafe       as BS
-import           Foreign.C.String             (CString)
-import           Foreign.C.Types              (CInt)
-import           Foreign.ForeignPtr           (ForeignPtr, castForeignPtr,
-                                               mallocForeignPtrBytes,
-                                               newForeignPtr, withForeignPtr)
-import           Foreign.Marshal.Alloc        (alloca)
-import           Foreign.Ptr                  (castPtr, nullPtr)
-import           Foreign.Storable             (peek, poke)
-import           System.IO.Unsafe             (unsafePerformIO)
-
-check :: LZ4FErrorCode -> IO ()
-check err = when (lZ4FIsError err) $ error (lZ4FGetErrorName err)
-
--- | Lazily decompress a frame
-decompress :: BSL.ByteString -> BSL.ByteString
-decompress = decompressBufSz (64 * 1014)
-
--- | @since 0.1.3.0
-decompressBufSz :: Int -- ^ Size of the output buffer
-                -> BSL.ByteString
-                -> BSL.ByteString
-decompressBufSz bufSz bs = runST $ do
-
-    let bss = BSL.toChunks bs
-
-    (ctx, buf) <- LazyST.unsafeIOToST $ do
-        (err, preCtx) <- lZ4FCreateDecompressionContext lZ4FGetVersion
-        check err
-        ctx <- castForeignPtr <$> newForeignPtr lZ4FFreeCompressionContext (castPtr preCtx)
-        dstBuf <- mallocForeignPtrBytes bufSz
-        pure (ctx, dstBuf)
-
-    BSL.fromChunks <$> loop ctx buf bss
-
-    where loop :: LzDecompressionCtxPtr -> ForeignPtr a -> [BS.ByteString] -> LazyST.ST s [BS.ByteString]
-          loop _ _ [] = pure []
-          loop ctx buf (b:bs') = do
-                (nxt, res) <- stepChunk ctx buf b
-                case nxt of
-                    Nothing   -> (res:) <$> loop ctx buf bs'
-                    Just next -> (res:) <$> loop ctx buf (next:bs')
-
-
-          stepChunk :: LzDecompressionCtxPtr -> ForeignPtr a -> BS.ByteString -> LazyST.ST s (Maybe BS.ByteString, BS.ByteString)
-          stepChunk !ctx !dst b = LazyST.unsafeIOToST $
-            BS.unsafeUseAsCStringLen b $ \(buf, sz) ->
-                withForeignPtr dst $ \d ->
-                    alloca $ \dSzPtr ->
-                        alloca $ \szPtr -> do
-                            poke dSzPtr (fromIntegral bufSz)
-                            poke szPtr (fromIntegral sz)
-                            res <- lZ4FDecompress ctx d dSzPtr buf szPtr nullPtr
-                            check res
-                            bRead <- peek szPtr
-                            bWritten <- peek dSzPtr
-                            outBs <- BS.packCStringLen (castPtr d, fromIntegral bWritten)
-                            let remBs = if fromIntegral bRead == sz
-                                then Nothing
-                                else Just (BS.drop (fromIntegral bRead) b)
-                            pure (remBs, outBs)
-
--- | Lazily compress a frame.
-compress :: BSL.ByteString -> BSL.ByteString
-compress bs = runST $ do
-
-    let bss = BSL.toChunks bs
-
-    (ctx, header) <- initCtx
-    rest <- loop ctx bss
-
-    pure $ BSL.fromChunks (header:rest)
-
-    where initCtx :: LazyST.ST s (LzCtxPtr, BS.ByteString)
-          initCtx = LazyST.unsafeIOToST $ do
-                (err, preCtx) <- lZ4FCreateCompressionContext lZ4FGetVersion
-                check err
-                ctx <- castForeignPtr <$> newForeignPtr lZ4FFreeCompressionContext (castPtr preCtx)
-                dst <- mallocForeignPtrBytes lZ4FHeaderSizeMax
-                header <- withForeignPtr dst $ \d -> do
-                    res <- lZ4FCompressBegin ctx d lZ4FHeaderSizeMax nullPtr
-                    check res
-                    BS.packCStringLen (castPtr d, fromIntegral res)
-                pure (ctx, header)
-
-          loop :: LzCtxPtr -> [BS.ByteString] -> LazyST.ST s [BS.ByteString]
-          loop ctx []      = pure <$> finish ctx
-          loop ctx (b:bs') = (:) <$> update ctx b <*> loop ctx bs'
-
-          finish :: LzCtxPtr -> LazyST.ST s BS.ByteString
-          finish ctx = LazyST.unsafeIOToST $ do
-            let expectedSz = lZ4FCompressBound 0 nullPtr
-            dst <- mallocForeignPtrBytes (fromIntegral expectedSz)
-            withForeignPtr dst $ \d -> do
-                res <- lZ4FCompressEnd ctx d expectedSz nullPtr
-                check res
-                BS.packCStringLen (castPtr d, fromIntegral res)
-
-          update :: LzCtxPtr -> BS.ByteString -> LazyST.ST s BS.ByteString
-          update !ctx b = LazyST.unsafeIOToST $
-            BS.unsafeUseAsCStringLen b $ \(buf, sz) -> do
-                let expectedSz = lZ4FCompressBound (fromIntegral sz) nullPtr
-                dst <- mallocForeignPtrBytes (fromIntegral expectedSz)
-                withForeignPtr dst $ \d -> do
-                    res <- lZ4FCompressUpdate ctx d expectedSz buf (fromIntegral sz) nullPtr
-                    check res
-                    BS.packCStringLen (castPtr d, fromIntegral res)
-
-{-# NOINLINE compressBlock #-}
-compressBlock :: BS.ByteString -> BS.ByteString
-compressBlock = unsafePerformIO . compressBlockGeneric lZ4CompressDefault
-
-{-# NOINLINE compressBlockHC #-}
--- | @since 0.1.1.0
-compressBlockHC :: Int -- ^ Compression level (must be less than 'lZ4HCClevelMax')
-                -> BS.ByteString
-                -> BS.ByteString
-compressBlockHC lvl = unsafePerformIO . compressBlockGeneric (\src dst ssz dsz -> lZ4CompressHC src dst ssz dsz (fromIntegral lvl))
-
-compressBlockGeneric :: (CString -> CString -> CInt -> CInt -> IO CInt) -> BS.ByteString -> IO BS.ByteString
-compressBlockGeneric cfun bs =
-    BS.unsafeUseAsCStringLen bs $ \(buf, sz) -> do
-        let resSz = lZ4CompressBound (fromIntegral sz)
-        dst <- mallocForeignPtrBytes (fromIntegral resSz)
-        withForeignPtr dst $ \d -> do
-            bWritten <- cfun buf d (fromIntegral sz) resSz
-            when (bWritten == 0) $ error "Compression error"
-            pure $ BS.fromForeignPtr (castForeignPtr dst) 0 (fromIntegral bWritten)
-
-{-# NOINLINE decompressBlockSz #-}
--- | Decompress a block. The size of the uncompressed data must be known.
-decompressBlockSz :: BS.ByteString
-                  -> Int -- ^ Decompressed size
-                  -> BS.ByteString
-decompressBlockSz bs expectedSz = unsafePerformIO $
-    BS.unsafeUseAsCStringLen bs $ \(buf, sz) -> do
-        dst <- mallocForeignPtrBytes expectedSz
-        withForeignPtr dst $ \d -> do
-            bWritten <- lZ4DecompressSafe buf d (fromIntegral sz) (fromIntegral expectedSz)
-            when (bWritten < 0) $ error "Decompression error"
-        pure $ BS.fromForeignPtr (castForeignPtr dst) 0 expectedSz
diff --git a/src/Codec/Lz4/Foreign.chs b/src/Codec/Lz4/Foreign.chs
--- a/src/Codec/Lz4/Foreign.chs
+++ b/src/Codec/Lz4/Foreign.chs
@@ -1,3 +1,5 @@
+{-# LANGUAGE CApiFFI #-}
+
 module Codec.Lz4.Foreign ( -- * Blocks
                            lZ4VersionNumber
                          , lZ4VersionString
@@ -16,10 +18,10 @@
                          , lZ4FCompressUpdate
                          , lZ4FCompressEnd
                          , lZ4FCreateDecompressionContext
-                         , lZ4FFreeDecompressionContext
                          , lZ4FDecompress
                          , lZ4MaxInputSize
                          , lZ4CompressHC
+                         -- * Macros
                          , lZ4HCClevelMax
                          -- * Types
                          , LZ4FErrorCode
@@ -27,6 +29,12 @@
                          , LzCtxPtr
                          , LzDecompressionCtx
                          , LzDecompressionCtxPtr
+                         , LzPreferencesPtr
+                         , BlockMode (Lz4fBlocklinked)
+                         , BlockSize (Lz4fDefault)
+                         , ContentChecksum (Lz4fNocontentchecksum)
+                         , BlockChecksum (Lz4fNoblockchecksum)
+                         , FrameType (Lz4fFrame)
                          ) where
 
 import Data.Coerce (coerce)
@@ -40,12 +48,18 @@
 #include <lz4frame.h>
 #include <lz4hc.h>
 
+{# enum LZ4F_blockSizeID_t as BlockSize {underscoreToCase} #}
+{# enum LZ4F_blockMode_t as BlockMode {underscoreToCase} #}
+{# enum LZ4F_contentChecksum_t as ContentChecksum {underscoreToCase} #}
+{# enum LZ4F_blockChecksum_t as BlockChecksum {underscoreToCase} #}
+{# enum LZ4F_frameType_t as FrameType {underscoreToCase} #}
+
 {# fun pure LZ4_versionNumber as ^ {} -> `CInt' #}
 -- | @since 0.1.1.0
 {# fun pure LZ4_versionString as ^ {} -> `String' #}
 
-{# fun LZ4_compress_default as ^ { `CString', `CString', `CInt' , `CInt' } -> `CInt' #}
-{# fun LZ4_decompress_safe as ^ { `CString', `CString', `CInt' , `CInt' } -> `CInt' #}
+{# fun unsafe LZ4_compress_default as ^ { `CString', `CString', `CInt' , `CInt' } -> `CInt' #}
+{# fun unsafe LZ4_decompress_safe as ^ { `CString', `CString', `CInt' , `CInt' } -> `CInt' #}
 
 {# fun pure LZ4_compressBound as ^ { `CInt' } -> `CInt' #}
 
@@ -61,16 +75,16 @@
 
 {# pointer *LZ4F_cctx as LzCtxPtr foreign finalizer LZ4F_freeCompressionContext as ^ -> LzCtx #}
 
-{# fun LZ4F_createCompressionContext as ^ { alloca- `Ptr LzCtx' peek*, `CUInt' } -> `LZ4FErrorCode' #}
+{# fun unsafe LZ4F_createCompressionContext as ^ { alloca- `Ptr LzCtx' peek*, `CUInt' } -> `LZ4FErrorCode' #}
 
 data LzPreferences
 
-{# pointer *LZ4F_preferences_t as LzPreferencesPtr -> LzPreferences #}
+{# pointer *LZ4F_preferences_t as LzPreferencesPtr foreign -> LzPreferences #}
 
 lZ4FHeaderSizeMax :: Integral a => a
 lZ4FHeaderSizeMax = {# const LZ4F_HEADER_SIZE_MAX #}
 
-{# fun LZ4F_compressBegin as ^ { `LzCtxPtr', castPtr `Ptr a', coerce `CSize', `LzPreferencesPtr' } -> `CSize' coerce #}
+{# fun unsafe LZ4F_compressBegin as ^ { `LzCtxPtr', castPtr `Ptr a', coerce `CSize', `LzPreferencesPtr' } -> `CSize' coerce #}
 
 {# fun pure LZ4F_compressBound as ^ { coerce `CSize', `LzPreferencesPtr' } -> `CSize' coerce #}
 
@@ -78,7 +92,7 @@
 
 {# pointer *LZ4F_compressOptions_t as LzCompressOptionsPtr -> LzCompressOptions #}
 
-{# fun LZ4F_compressUpdate as ^
+{# fun unsafe LZ4F_compressUpdate as ^
     { `LzCtxPtr'
     , castPtr `Ptr a'
     , coerce `CSize'
@@ -88,20 +102,20 @@
     } -> `CSize' coerce
   #}
 
-{# fun LZ4F_compressEnd as ^
+{# fun unsafe LZ4F_compressEnd as ^
     { `LzCtxPtr', castPtr `Ptr a', coerce `CSize', `LzCompressOptionsPtr' } -> `CSize' coerce #}
 
 data LzDecompressionCtx
 
 {# pointer *LZ4F_dctx as LzDecompressionCtxPtr foreign finalizer LZ4F_freeDecompressionContext as ^ -> LzDecompressionCtx #}
 
-{# fun LZ4F_createDecompressionContext as ^ { alloca- `Ptr LzDecompressionCtx' peek*, `CUInt' } -> `LZ4FErrorCode' #}
+{# fun unsafe LZ4F_createDecompressionContext as ^ { alloca- `Ptr LzDecompressionCtx' peek*, `CUInt' } -> `LZ4FErrorCode' #}
 
 data LzDecompressOptions
 
 {# pointer *LZ4F_decompressOptions_t as LzDecompressOptionsPtr -> LzDecompressOptions #}
 
-{# fun LZ4F_decompress as ^
+{# fun unsafe LZ4F_decompress as ^
     { `LzDecompressionCtxPtr'
     , castPtr `Ptr a'
     , castPtr `Ptr CSize'
@@ -115,7 +129,7 @@
 lZ4MaxInputSize :: Integral a => a
 lZ4MaxInputSize = {# const LZ4_MAX_INPUT_SIZE #}
 
-{# fun LZ4_compress_HC as ^
+{# fun unsafe LZ4_compress_HC as ^
     { `CString', `CString', `CInt', `CInt', `CInt' } -> `CInt' #}
 
 -- | @since 0.1.1.0
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -6,6 +6,13 @@
 import           Test.Tasty
 import           Test.Tasty.HUnit
 
+testCompression :: FilePath -> TestTree
+testCompression fp = testCase ("Highly compressed frames " ++ fp) $ do
+    contents <- BSL.readFile "cbits/lz4.c"
+    let sz0 = BSL.length (compressSz 0 contents)
+        sz1 = BSL.length (compressSz 6 contents)
+    assertBool "higher compression means smaller frames" $ sz1 < sz0
+
 testRoundtripBlocks :: FilePath -> TestTree
 testRoundtripBlocks fp = testCase ("Roundtrip " ++ fp) $ do
     contents <- BS.readFile fp
@@ -23,7 +30,7 @@
 main =
 
     defaultMain $
-        testGroup "lz4" [testBlocks, testFrames]
+        testGroup "lz4" [testBlocks, testFrames, testHC]
 
     where
         testBlocks = testGroup "Block compression"
@@ -32,4 +39,7 @@
             ]
         testFrames = testGroup "Frame compression"
             [ testRoundtripFrames "cbits/lz4.c"
+            ]
+        testHC = testGroup "Compression levels"
+            [ testCompression "cbits/lz4frame.c"
             ]
