packages feed

ppad-base64-0.1.0: lib/Data/ByteString/Base64/Arm.hs

{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE BangPatterns #-}

-- |
-- Module: Data.ByteString.Base64.Arm
-- Copyright: (c) 2026 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- ARM NEON support for base64 encoding and decoding.

module Data.ByteString.Base64.Arm (
    base64_arm_available
  , encode
  , decode
  ) where

import qualified Data.Bits as B
import Data.Bits ((.&.))
import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BI
import Data.Word (Word8)
import Foreign.C.Types (CInt(..), CSize(..))
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr (Ptr, plusPtr)
import Foreign.Storable (peekElemOff)
import System.IO.Unsafe (unsafeDupablePerformIO)

-- ffi ------------------------------------------------------------------------

foreign import ccall unsafe "base64_encode_arm"
  c_base64_encode :: Ptr Word8 -> Ptr Word8 -> CSize -> IO ()

foreign import ccall unsafe "base64_decode_arm"
  c_base64_decode :: Ptr Word8 -> Ptr Word8 -> CSize -> CSize -> IO CInt

foreign import ccall unsafe "base64_arm_available"
  c_base64_arm_available :: IO CInt

-- utilities ------------------------------------------------------------------

fi :: (Integral a, Num b) => a -> b
fi = fromIntegral
{-# INLINE fi #-}

-- api ------------------------------------------------------------------------

-- | Are ARM NEON extensions available?
base64_arm_available :: Bool
base64_arm_available =
  unsafeDupablePerformIO c_base64_arm_available /= 0
{-# NOINLINE base64_arm_available #-}

-- | Encode a base256 'ByteString' as base64 using NEON.
encode :: BS.ByteString -> BS.ByteString
encode (BI.PS sfp soff l) =
  BI.unsafeCreate ((l + 2) `quot` 3 * 4) $ \dst ->
    withForeignPtr sfp $ \sp0 ->
      c_base64_encode (sp0 `plusPtr` soff) dst (fi l)

-- | Decode a base64 'ByteString' to base256 using NEON.  Returns
--   'Nothing' on malformed input.
decode :: BS.ByteString -> Maybe BS.ByteString
decode (BI.PS sfp soff l)
  | l == 0          = Just BS.empty
  | l .&. 0x03 /= 0 = Nothing
  | otherwise = unsafeDupablePerformIO $
      withForeignPtr sfp $ \sp0 -> do
        let !sp = sp0 `plusPtr` soff :: Ptr Word8
        c_pre <- peekElemOff sp (l - 2)
        c_end <- peekElemOff sp (l - 1)
        let !pad_pre = c_pre == 0x3D
            !pad_end = c_end == 0x3D
        if pad_pre && not pad_end
          then pure Nothing
          else do
            let !pad = (if pad_pre then 2 else if pad_end then 1 else 0)
                     :: Int
                !nfull  = l `B.shiftR` 2
                !outlen = nfull * 3 - pad
            fp <- BI.mallocByteString outlen
            ok <- withForeignPtr fp $ \dst ->
              c_base64_decode sp dst (fi l) (fi outlen)
            pure $! if ok /= 0
              then Just (BI.PS fp 0 outlen)
              else Nothing