module Irc.Identifier
( Identifier
, idDenote
, mkId
, idText
, idTextNorm
, idPrefix
) where
import Control.Monad.ST
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Char
import Data.Foldable
import Data.Function
import Data.Hashable
import Data.Monoid
import Data.Primitive.ByteArray
import Data.String
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import qualified Data.Vector.Primitive as PV
import qualified Data.Primitive.ByteArray as BA
import Data.Primitive.ByteArray (ByteArray)
import Data.Word
data Identifier = Identifier {-# UNPACK #-} !Text
{-# UNPACK #-} !ByteArray
indexWord8 :: ByteArray -> Int -> Word8
indexWord8 = BA.indexByteArray
instance Eq Identifier where
Identifier _ x == Identifier _ y =
BA.sizeofByteArray x == BA.sizeofByteArray y &&
all (\i -> indexWord8 x i == indexWord8 y i)
[0 .. BA.sizeofByteArray x - 1]
instance Show Identifier where
show = show . idText
instance Read Identifier where
readsPrec p x = [ (mkId t, rest) | (t,rest) <- readsPrec p x]
instance Ord Identifier where
compare (Identifier _ x) (Identifier _ y) =
mconcat [ indexWord8 x i `compare` indexWord8 y i | i <- [0..n-1]]
<> (BA.sizeofByteArray x `compare` BA.sizeofByteArray y)
where
n = min (BA.sizeofByteArray x) (BA.sizeofByteArray y)
instance Hashable Identifier where
hashWithSalt salt (Identifier _ b@(ByteArray arr)) =
hashByteArrayWithSalt arr 0 (BA.sizeofByteArray b) salt
instance IsString Identifier where
fromString = mkId . fromString
mkId :: Text -> Identifier
mkId x = Identifier x (ircFoldCase (Text.encodeUtf8 x))
idText :: Identifier -> Text
idText (Identifier x _) = x
idDenote :: Identifier -> ByteArray
idDenote (Identifier _ x) = x
idTextNorm :: Identifier -> Text
idTextNorm (Identifier _ x) =
Text.decodeUtf8
(B.pack [ indexWord8 x i | i <- [0 .. BA.sizeofByteArray x - 1]])
idPrefix :: Identifier -> Identifier -> Bool
idPrefix (Identifier _ x) (Identifier _ y) =
BA.sizeofByteArray x <= BA.sizeofByteArray y &&
all (\i -> indexWord8 x i == indexWord8 y i)
[0 .. BA.sizeofByteArray x - 1]
ircFoldCase :: ByteString -> ByteArray
ircFoldCase bs = runST $
do let n = B.length bs
a <- BA.newByteArray n
for_ [0..n-1] $ \i ->
BA.writeByteArray a i (casemap PV.! fromIntegral (B.index bs i))
BA.unsafeFreezeByteArray a
casemap :: PV.Vector Word8
casemap
= PV.fromList
$ map (fromIntegral . ord)
$ ['\x00'..'`'] ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^" ++ ['\x7f'..'\xff']