ip-0.8: test/IPv4Text1.hs
module IPv4Text1 where
import Net.Types (IPv4(..))
import Data.Text (Text)
import qualified Net.IPv4 as IPv4
import qualified Data.Text as Text
import Text.Read (readMaybe)
import Data.Bits ((.&.),(.|.),shiftR,shiftL,complement)
import Data.Monoid ((<>))
import Data.Text.Lazy.Builder.Int (decimal)
import Data.Text.Internal (Text(..))
import Data.Word
import Data.ByteString (ByteString)
import Control.Monad.ST
import Data.Text.Encoding (encodeUtf8)
import qualified Data.ByteString.Char8 as BC8
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Unsafe as ByteString
import qualified Data.Text.Lazy as LText
import qualified Data.Text.Lazy.Builder as TBuilder
import qualified Data.Text.Array as TArray
------------------------
-- This implementation operates directly on
-- a mutable array. It is the fastest one so far.
-- On a 2012 laptop, it can serialize an IPv4
-- address to Text in 35ns. It is somewhat wasteful
-- of space. It allocates a full 30 bytes in case it
-- needs it, but it may need as few as 14 bytes.
------------------------
encode :: IPv4 -> Text
encode (IPv4 w) =
let w1 = fromIntegral $ 255 .&. shiftR w 24
w2 = fromIntegral $ 255 .&. shiftR w 16
w3 = fromIntegral $ 255 .&. shiftR w 8
w4 = fromIntegral $ 255 .&. w
dot = 46
(arr,len) = runST $ do
marr <- TArray.new 15
i1 <- putAndCount 0 w1 marr
let n1 = i1
n1' = i1 + 1
TArray.unsafeWrite marr n1 dot
i2 <- putAndCount n1' w2 marr
let n2 = i2 + n1'
n2' = n2 + 1
TArray.unsafeWrite marr n2 dot
i3 <- putAndCount n2' w3 marr
let n3 = i3 + n2'
n3' = n3 + 1
TArray.unsafeWrite marr n3 dot
i4 <- putAndCount n3' w4 marr
theArr <- TArray.unsafeFreeze marr
return (theArr,i4 + n3')
in Text arr 0 len
putAndCount :: Int -> Word8 -> TArray.MArray s -> ST s Int
putAndCount pos w marr
| w < 10 = TArray.unsafeWrite marr pos (i2w w) >> return 1
| w < 100 = write2 pos w >> return 2
| otherwise = write3 pos w >> return 3
where
write2 off i0 = do
let i = fromIntegral i0; j = i + i
TArray.unsafeWrite marr off $ get2 j
TArray.unsafeWrite marr (off + 1) $ get2 (j + 1)
write3 off i0 = do
let i = fromIntegral i0; j = i + i + i
TArray.unsafeWrite marr off $ get3 j
TArray.unsafeWrite marr (off + 1) $ get3 (j + 1)
TArray.unsafeWrite marr (off + 2) $ get3 (j + 2)
get2 = fromIntegral . ByteString.unsafeIndex twoDigits
get3 = fromIntegral . ByteString.unsafeIndex threeDigits
zero,dot :: Word16
zero = 48
{-# INLINE zero #-}
dot = 46
{-# INLINE dot #-}
i2w :: (Integral a) => a -> Word16
i2w v = zero + fromIntegral v
{-# INLINE i2w #-}
twoDigits :: ByteString
twoDigits = BC8.pack
"0001020304050607080910111213141516171819\
\2021222324252627282930313233343536373839\
\4041424344454647484950515253545556575859\
\6061626364656667686970717273747576777879\
\8081828384858687888990919293949596979899"
threeDigits :: ByteString
threeDigits =
ByteString.replicate 300 0 <> BC8.pack
"100101102103104105106107108109110111112\
\113114115116117118119120121122123124125\
\126127128129130131132133134135136137138\
\139140141142143144145146147148149150151\
\152153154155156157158159160161162163164\
\165166167168169170171172173174175176177\
\178179180181182183184185186187188189190\
\191192193194195196197198199200201202203\
\204205206207208209210211212213214215216\
\217218219220221222223224225226227228229\
\230231232233234235236237238239240241242\
\243244245246247248249250251252253254255"