Bryan O'Sullivan committed bcb96aa

Improve encoding performance by 50%.

Our use of unsafeIndex was slow because we were deconstructing the PS
constructor of "digits" on every iteration through the inner loop. Boo!

By manually allocating a Ptr and marking it as strict, we can get GHC to
hoist the case analysis of the Ptr constructor to the outside of the loop,
and thereby win.

Comments (0)

Files changed (1)


-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE BangPatterns, OverloadedStrings #-}
 -- |
 -- Module      : Data.ByteString.Base16
 import Data.Bits ((.&.), shiftL, shiftR)
 import Data.ByteString.Char8 (empty)
 import Data.ByteString.Internal (ByteString(..), createAndTrim', unsafeCreate)
-import Data.ByteString.Unsafe (unsafeIndex)
 import Data.Word (Word8)
+import Control.Monad (forM_)
+import Foreign.Marshal.Alloc (mallocBytes)
 import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
 import Foreign.Ptr (Ptr, minusPtr, plusPtr)
 import Foreign.Storable (peek, poke)
 import System.IO.Unsafe (unsafePerformIO)
-digits :: ByteString
-digits = "0123456789abcdef"
-{-# NOINLINE digits #-}
 -- | Encode a string into base16 form.  The result will always be a
 -- multiple of 2 bytes in length.
     go s d | s == e = return ()
            | otherwise = do
       x <- peek8 s
-      poke d . unsafeIndex digits $ x `shiftR` 4
-      poke (d `plusPtr` 1) . unsafeIndex digits $ x .&. 0xf
+      poke d =<< (peek (digits `plusPtr` (x `shiftR` 4)) :: IO Word8)
+      poke (d `plusPtr` 1) =<< (peek (digits `plusPtr` (x .&. 0xf)) :: IO Word8)
       go (s `plusPtr` 1) (d `plusPtr` 2)
+  digits :: Ptr Word8
+  !digits = unsafePerformIO $ do
+             ptr <- mallocBytes 16
+             forM_ (zip [0..] ("0123456789abcdef"::String)) $ \(i,c) ->
+               poke (ptr `plusPtr` i) ((fromIntegral (fromEnum c)) :: Word8)
+             return ptr
+  {-# NOINLINE digits #-}
 -- | Decode a string from base16 form. The first element of the
 -- returned tuple contains the decoded data. The second element starts
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.