{-# 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