1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
{-# LANGUAGE FlexibleContexts #-}
module Data.Text.Punycode.Encode (encode) where
import Control.Monad.State hiding (state)
import Control.Monad.Writer
import qualified Data.ByteString as BS
import Data.Char
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import Data.Word
import Data.Text.Punycode.Shared
data PunycodeState = PunycodeState { n :: Int
, delta :: Int
, bias :: Int
, h :: Int
}
-- | Encode a string into its ascii form
encode :: T.Text -> BS.ByteString
encode = execWriter . initialWriter
initialWriter :: MonadWriter BS.ByteString m => T.Text -> m ()
initialWriter input = do
tell basics
when (b > 0) $ tell $ BS.singleton $ fromIntegral $ ord '-'
evalStateT (inner3 (map ord $ T.unpack input) b) $ PunycodeState { n = initial_n
, delta = 0
, bias = initial_bias
, h = b
}
where basics = TE.encodeUtf8 $ T.filter isBasic input
b = BS.length basics
inner3 :: (MonadState PunycodeState m, MonadWriter BS.ByteString m) => [Int] -> Int -> m ()
inner3 input b = do
state <- get
helper state
where helper state
| h' < length input = do
put $ state {n = m, delta = delta'}
mapM_ (inner2 b) input
state' <- get
put $ state' {delta = (delta state') + 1, n = (n state') + 1}
inner3 input b
| otherwise = return ()
where m = minimum $ filter (>= n') input
n' = n state
h' = h state
delta' = (delta state) + (m - n') * (h' + 1)
inner2 :: (MonadState PunycodeState m, MonadWriter BS.ByteString m) => Int -> Int -> m ()
inner2 b c = do
state <- get
helper state
where helper state
| c == n' = do
q <- inner delta' base bias'
tell $ BS.singleton $ baseToAscii q
put $ state {bias = adapt delta' (h' + 1) (h' == b), delta = 0, h = (h state) + 1}
| otherwise = put $ state {delta = delta'}
where delta' = (delta state) + d
where d
| c < n' = 1
| otherwise = 0
n' = n state
bias' = bias state
h' = h state
inner :: (MonadWriter BS.ByteString m) => Int -> Int -> Int -> m Int
inner q k bias'
| q < t = return q
| otherwise = do
tell $ BS.singleton $ baseToAscii $ t + ((q - t) `mod` (base - t))
inner ((q - t) `div` (base - t)) (k + base) bias'
where t
| k <= bias' + tmin = tmin
| k >= bias' + tmax = tmax
| otherwise = k - bias'
baseToAscii :: Int -> Word8
baseToAscii i
| i < 26 = fromIntegral $ i + (ord 'a')
| otherwise = fromIntegral $ (i - 26) + (ord '0')
|