lz4-bytes-0.1.1.0: src/Lz4/Frame.hs
{-# language BangPatterns #-}
{-# language BinaryLiterals #-}
{-# language BlockArguments #-}
{-# language DeriveAnyClass #-}
{-# language DerivingStrategies #-}
{-# language MagicHash #-}
{-# language MultiWayIf #-}
{-# language NumericUnderscores #-}
{-# language UnboxedTuples #-}
{-# language UnliftedFFITypes #-}
-- | Compress a contiguous sequence of bytes into an LZ4 frame
-- containing a single block.
module Lz4.Frame
( -- * Compression
compressHighlyU
) where
import Lz4.Internal (requiredBufferSize,c_hs_compress_HC)
import Control.Monad.ST (runST)
import Control.Monad.ST.Run (runByteArrayST)
import Data.Bytes.Types (Bytes(Bytes))
import Data.Int (Int32)
import Data.Primitive (MutableByteArray(..),ByteArray(..))
import Data.Word (Word8)
import GHC.Exts (ByteArray#,MutableByteArray#)
import GHC.IO (unsafeIOToST)
import GHC.ST (ST(ST))
import qualified Control.Exception
import qualified Data.Primitive as PM
import qualified GHC.Exts as Exts
import qualified Data.Primitive.ByteArray.LittleEndian as LE
-- | Use HC compression to produce a frame with a single block.
-- All optional fields (checksums, content sizes, and dictionary IDs)
-- are omitted.
--
-- Note: Currently, this produces incorrect output when the size of
-- the input to be compressed is greater than 4MiB. The only way
-- to correct this function is to make it not compress large input.
-- This can be done by setting the high bit of the size. This needs
-- to be tested though since it is an uncommon code path.
compressHighlyU ::
Int -- ^ Compression level (Use 9 if uncertain)
-> Bytes -- ^ Bytes to compress
-> ByteArray
compressHighlyU !lvl (Bytes (ByteArray arr) off len) = runST do
let maxSz = requiredBufferSize len + 15
dst@(MutableByteArray dst# ) <- PM.newByteArray maxSz
-- -- First 4 bytes: magic identifier
PM.writeByteArray dst 0 (0x04 :: Word8)
PM.writeByteArray dst 1 (0x22 :: Word8)
PM.writeByteArray dst 2 (0x4D :: Word8)
PM.writeByteArray dst 3 (0x18 :: Word8)
-- Next 3 bytes: frame descriptor
PM.writeByteArray dst 4 (0b0110_0000 :: Word8)
if | len <= 65536 -> do
PM.writeByteArray dst 5 (0b0100_0000 :: Word8)
PM.writeByteArray dst 6 (0x82 :: Word8)
| len <= 262144 -> do
PM.writeByteArray dst 5 (0b0101_0000 :: Word8)
PM.writeByteArray dst 6 (0xFB :: Word8)
| len <= 1048576 -> do
PM.writeByteArray dst 5 (0b0110_0000 :: Word8)
PM.writeByteArray dst 6 (0x51 :: Word8)
| otherwise -> do
PM.writeByteArray dst 5 (0b0111_0000 :: Word8)
PM.writeByteArray dst 6 (0x73 :: Word8)
actualSz <- unsafeIOToST (c_hs_compress_HC arr off dst# 11 len maxSz lvl)
LE.writeUnalignedByteArray dst 7 (fromIntegral actualSz :: Int32)
PM.writeByteArray dst (actualSz + 11) (0x00 :: Word8)
PM.writeByteArray dst (actualSz + 12) (0x00 :: Word8)
PM.writeByteArray dst (actualSz + 13) (0x00 :: Word8)
PM.writeByteArray dst (actualSz + 14) (0x00 :: Word8)
PM.shrinkMutableByteArray dst (actualSz + 15)
PM.unsafeFreezeByteArray dst