tinyXml-0.1.0.0: src/Text/Xml/Tiny/Internal.hs
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DisambiguateRecordFields, NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE CPP #-}
-- | This module contains types and functions that are mostly
-- intended for the test suite, and should be considered internal
-- for all other purposes
module Text.Xml.Tiny.Internal
(
-- * XML Nodes
Node(..), Attribute(..)
-- * ParseDetails
, ParseDetails(..), AttributeParseDetails(..)
-- * Slices
, Slice(..)
, fromOpen, fromOpenClose, fromIndexPtr
, empty
, start, end
, take, drop, null
, vector, render, toList
-- * Errors
, SrcLoc(..), Error(..), ErrorType(..)
) where
import Control.Exception
import Data.ByteString.Char8 (ByteString)
import Data.ByteString.Internal (ByteString(..))
import Data.List (genericTake)
import Data.Vector.Storable (Vector, (!))
import qualified Data.Vector.Storable as V
import Data.Word
import GHC.Stack hiding (SrcLoc)
import Foreign.ForeignPtr (ForeignPtr)
import Foreign
import Prelude hiding (length, null, take, drop)
import Text.Printf
import Config
-- A subset of a vector defined by an offset and length
data Slice =
Slice { offset, length :: !Int32 }
deriving (Eq,Ord,Show)
sInt :: Int
sInt = sizeOf(0::Int32)
instance Storable Slice where
sizeOf _ = sInt * 2
alignment _ = alignment (0 :: Int64)
peek p = Slice <$> peekByteOff p 0 <*> peekByteOff p sInt
poke p Slice{..} = pokeByteOff p 0 offset >> pokeByteOff p sInt length
{-# INLINE empty #-}
empty :: Slice
empty = Slice 0 0
fromOpen:: Config => Integral a => a -> Slice
fromOpen o = Slice (fromIntegral o) 0
{-# INLINE fromOpenClose #-}
fromOpenClose :: Config => (Integral a1, Integral a) => a -> a1 -> Slice
fromOpenClose (fromIntegral->open) (fromIntegral->close) = Slice open (close-open)
{-# INLINE null #-}
null :: Config => Slice -> Bool
null (Slice _ l) = l == 0
{-# INLINE take #-}
take :: Config => Integral a => a -> Slice -> Slice
take !(fromIntegral -> i) (Slice o l) = assert (l>=i) $ Slice o i
{-# INLINE drop #-}
drop :: Config => Integral t => t -> Slice -> Slice
drop !(fromIntegral -> i) (Slice o l) = assert (l>=i || error(printf "drop %d (Slice %d %d)" i o l)) $ Slice (o+i) (l-i)
-- | Inclusive
start :: Config => Slice -> Int32
start = offset
-- | Non inclusive
end :: Config => Slice -> Int32
end (Slice o l) = o + l
-- | Returns a list of indexes
toList :: Config => Slice -> [Int]
toList (Slice o l) = genericTake l [ fromIntegral o ..]
{-# INLINE fromIndexPtr #-}
-- | Apply a slice to a foreign ptr of word characters and wrap as a bytestring
fromIndexPtr :: Config => Slice -> ForeignPtr Word8 -> ByteString
fromIndexPtr (Slice o l) fptr = PS fptr (fromIntegral o) (fromIntegral l)
-- | Apply a slice to a bytestring
render :: Config => Slice -> ByteString -> ByteString
render(Slice o l) _ | trace (printf "Render slice %d %d" o l) False = undefined
render(Slice o l) _ | assert (o >= 0 && l >= 0) False = undefined
render(Slice o l) (PS fptr _ _) = PS fptr (fromIntegral o) (fromIntegral l)
-- | Apply a slice to a vector
vector :: Config => Storable a => Slice -> Vector a -> [a]
vector s v = [ v ! i
| i <- toList s
, assert (i < V.length v) True ]
-- * XML Nodes
-- | A parsed XML node
data Node =
Node{ attributesV :: !(Vector AttributeParseDetails) -- ^ All the attributes in the document
, nodesV :: !(Vector ParseDetails) -- ^ All the nodes in the document
, source :: !ByteString -- ^ The document bytes
, slices :: !ParseDetails -- ^ Details for this node
}
-- | A parsed XML attribute
data Attribute = Attribute { attributeName, attributeValue :: !ByteString } deriving (Eq, Show)
data ParseDetails =
ParseDetails
{ name :: {-# UNPACK #-} !Slice -- ^ bytestring slice
, inner :: {-# UNPACK #-} !Slice -- ^ bytestring slice
, outer :: {-# UNPACK #-} !Slice -- ^ bytestring slice
, attributes :: {-# UNPACK #-} !Slice -- ^ ParseDetailsAttribute slice
, nodeContents :: {-# UNPACK #-} !Slice -- ^ ParseDetails slice of children
}
-- | An incompletely defined set of parse details
| ProtoParseDetails { name, attributes :: !Slice, innerStart, outerStart :: !Int32 }
deriving (Show)
-- | Assumes that a name can never be the empty slice
instance Storable ParseDetails where
sizeOf _ = sizeOf empty * 5
alignment _ = alignment(0::Int)
poke !q (ParseDetails a b c d e) = let p = castPtr q in pokeElemOff p 0 a >> pokeElemOff p 1 b >> pokeElemOff p 2 c >> pokeElemOff p 3 d >> pokeElemOff p 4 e
poke !q (ProtoParseDetails (Slice no nl) (Slice ao al) i o) = do
let !p = castPtr q
pokeElemOff p 0 no
pokeElemOff p 1 (0::Int32)
pokeElemOff p 2 nl
pokeElemOff p 3 ao
pokeElemOff p 4 al
pokeElemOff p 5 i
pokeElemOff p 6 o
peek q = do
let !p = castPtr q
!header <- peekElemOff p 1
if header == (0::Int32)
then protoNode <$> peekElemOff p 0 <*> peekElemOff p 2 <*> peekElemOff p 3 <*> peekElemOff p 4 <*> peekElemOff p 5 <*> peekElemOff p 6
else let !p = castPtr q in ParseDetails <$> peekElemOff p 0 <*> peekElemOff p 1 <*> peekElemOff p 2 <*> peekElemOff p 3 <*> peekElemOff p 4
where
protoNode no nl ao al = ProtoParseDetails (Slice no nl) (Slice ao al)
data AttributeParseDetails =
AttributeParseDetails
{ nameA :: {-# UNPACK #-} !Slice,
value :: {-# UNPACK #-} !Slice
}
deriving (Eq, Show)
instance Storable AttributeParseDetails where
sizeOf _ = sizeOf empty * 2
alignment _ = alignment (0 :: Int)
peek !q = do
let !p = castPtr q :: Ptr Slice
!a <- peekElemOff p 0
!b <- peekElemOff p 1
return (AttributeParseDetails a b)
poke !q (AttributeParseDetails a b)= do
let !p = castPtr q :: Ptr Slice
pokeElemOff p 0 a
pokeElemOff p 1 b
-- * Error types
newtype SrcLoc = SrcLoc Int deriving Show
data Error = Error ErrorType CallStack
data ErrorType =
UnterminatedComment SrcLoc
| UnterminatedTag String SrcLoc
| ClosingTagMismatch String SrcLoc
| JunkAtTheEnd Slice SrcLoc
| UnexpectedEndOfStream
| BadAttributeForm SrcLoc
| BadTagForm SrcLoc
| UnfinishedComment SrcLoc
| Garbage SrcLoc
| InvalidNullName SrcLoc
deriving Show
#if __GLASGOW_HASKELL__ < 800
prettyCallStack = show
#endif
instance Exception Error
instance Show Error where
show (Error etype cs) = show etype ++ prettyCallStack cs