packages feed

ppad-base16-0.3.0: lib/Data/ByteString/Base16/Arm.hs

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

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

module Data.ByteString.Base16.Arm (
    base16_arm_available
  , encode
  , decode
  ) where

import qualified Data.Bits as B
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 System.IO.Unsafe (unsafeDupablePerformIO)

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

foreign import ccall unsafe "base16_encode_arm"
  c_base16_encode :: Ptr Word8 -> Ptr Word8 -> CSize -> IO ()

foreign import ccall unsafe "base16_decode_arm"
  c_base16_decode :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt

foreign import ccall unsafe "base16_arm_available"
  c_base16_arm_available :: IO CInt

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

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

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

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

-- | Encode a base256 'ByteString' as base16 using NEON.
encode :: BS.ByteString -> BS.ByteString
encode (BI.PS sfp soff l) =
  BI.unsafeCreate (l `B.shiftL` 1) $ \dst ->
    withForeignPtr sfp $ \sp0 ->
      c_base16_encode (sp0 `plusPtr` soff) dst (fi l)

-- | Decode a base16 'ByteString' to base256 using NEON.  Returns
--   'Nothing' on odd-length or otherwise invalid input.
decode :: BS.ByteString -> Maybe BS.ByteString
decode (BI.PS sfp soff l)
  | B.testBit l 0 = Nothing
  | otherwise = unsafeDupablePerformIO $ do
      let !n = l `B.shiftR` 1
      fp <- BI.mallocByteString n
      ok <- withForeignPtr fp  $ \dst ->
            withForeignPtr sfp $ \sp0 ->
              c_base16_decode (sp0 `plusPtr` soff) dst (fi n)
      pure $! if ok /= 0 then Just (BI.PS fp 0 n) else Nothing