base32-1.0: src/Data/ByteString/Base32/Internal/Head.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE TypeApplications #-}
-- |
-- Module : Data.ByteString.Base32.Internal.Head
-- Copyright : (c) 2020-2026 Emily Pillmore
-- License : BSD-style
--
-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>
-- Stability : Experimental
-- Portability : portable
--
-- Allocation and entry points for the strict encoding and decoding loops.
--
-- @since 1.0
--
module Data.ByteString.Base32.Internal.Head
( encodeBase32_
, encodeBase32NoPad_
, decodeBase32_
, decodeBase32Typed_
) where
import Prelude hiding (length, null)
import Data.Base32.Types (Base32, extractBase32)
import qualified Data.ByteString as BS
import Data.ByteString (length, null)
import Data.ByteString.Internal
import Data.ByteString.Base32.Internal.Utils (encodedLength)
import Data.ByteString.Base32.Internal.Loop
import Data.ByteString.Base32.Internal.Tail
import Data.Text (Text)
import qualified Data.Text as Text
import Foreign.Ptr
import Foreign.ForeignPtr
import GHC.Exts
import GHC.ForeignPtr
import GHC.Word
import System.IO.Unsafe
-- | Head of the padded base32 encoding loop.
--
-- This function takes an alphabet in the form of an unboxed 'Addr#',
-- allocates the correct number of bytes that will be written, and
-- executes the inner encoding loop against that data.
--
-- @since 1.0
--
encodeBase32_ :: Addr# -> ByteString -> ByteString
encodeBase32_ !lut (BS !sfp !l) = unsafeDupablePerformIO $ do
dfp <- mallocPlainForeignPtrBytes dlen
withForeignPtr dfp $ \dptr ->
withForeignPtr sfp $ \sptr -> do
let !end = plusPtr sptr l
innerLoop lut
(castPtr dptr) sptr
end (loopTail lut dfp dptr end)
where
!dlen = encodedLength l
-- | Head of the unpadded base32 encoding loop.
--
-- This function takes an alphabet in the form of an unboxed 'Addr#',
-- allocates the correct number of bytes that will be written, and
-- executes the inner encoding loop against that data.
--
-- @since 1.0
--
encodeBase32NoPad_ :: Addr# -> ByteString -> ByteString
encodeBase32NoPad_ !lut (BS !sfp !l) = unsafeDupablePerformIO $ do
!dfp <- mallocPlainForeignPtrBytes dlen
withForeignPtr dfp $ \dptr ->
withForeignPtr sfp $ \sptr -> do
let !end = plusPtr sptr l
innerLoop lut
(castPtr dptr) sptr
end (loopTailNoPad lut dfp dptr end)
where
!dlen = encodedLength l
-- | Head of the base32 decoding loop.
--
-- This function takes a base32-decoding lookup table and base32-encoded
-- bytestring, allocates the correct number of bytes that will be written,
-- and executes the inner decoding loop against that data.
--
-- @since 1.0
--
decodeBase32_ :: Ptr Word8 -> ByteString -> IO (Either Text ByteString)
decodeBase32_ table bs
| null bs = pure (Right bs)
| length bs `rem` 8 /= 0 =
pure (Left (Text.pack "Base32 input requires complete quanta"))
| otherwise = decodeComplete table bs
decodeComplete :: Ptr Word8 -> ByteString -> IO (Either Text ByteString)
decodeComplete (Ptr !dtable) (BS !sfp !slen) =
withForeignPtr sfp $ \sptr -> do
dfp <- mallocPlainForeignPtrBytes dlen
withForeignPtr dfp $ \dptr -> do
let !end = plusPtr sptr slen
decodeLoop dtable dfp dptr sptr end
where
!dlen = (slen `quot` 8) * 5
-- | Decode established Base32 provenance without validation.
-- Padding contributes no payload; partial final quanta need no copying.
--
-- @since 1.0
--
decodeBase32Typed_ :: Ptr Word8 -> Base32 alphabet ByteString -> ByteString
decodeBase32Typed_ (Ptr table) encoded = unsafeDupablePerformIO $ do
dfp <- mallocPlainForeignPtrBytes dlen
withForeignPtr dfp $ \dst ->
withForeignPtr sfp $ \src -> decodeLoopTyped table dst src slen
pure (BS dfp dlen)
where
BS sfp slen = BS.dropWhileEnd (== 61) (extractBase32 encoded)
dlen = (slen `quot` 8) * 5 + (slen `rem` 8) * 5 `quot` 8
{-# INLINE decodeBase32Typed_ #-}