packages feed

hexml 0.3.4 → 0.3.5

raw patch · 7 files changed

+60/−37 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGES.txt view
@@ -1,5 +1,7 @@ Changelog for Hexml +0.3.5, released 2024-08-25+    #1, add support for <![CDATA[ ... ]]> 0.3.4, released 2018-09-10     #15, licensed under BSD-3-Clause OR Apache-2.0 0.3.3, released 2017-10-27
LICENSE view
@@ -1,4 +1,4 @@-Copyright Neil Mitchell 2016-2018.+Copyright Neil Mitchell 2016-2024.  Licensed under either of: 
README.md view
@@ -1,4 +1,4 @@-# Hexml [![Hackage version](https://img.shields.io/hackage/v/hexml.svg?label=Hackage)](https://hackage.haskell.org/package/hexml) [![Stackage version](https://www.stackage.org/package/hexml/badge/nightly?label=Stackage)](https://www.stackage.org/package/hexml) [![Linux Build Status](https://img.shields.io/travis/ndmitchell/hexml/master.svg?label=Linux%20build)](https://travis-ci.org/ndmitchell/hexml) [![Windows Build Status](https://img.shields.io/appveyor/ci/ndmitchell/hexml/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/ndmitchell/hexml)+# Hexml [![Hackage version](https://img.shields.io/hackage/v/hexml.svg?label=Hackage)](https://hackage.haskell.org/package/hexml) [![Stackage version](https://www.stackage.org/package/hexml/badge/nightly?label=Stackage)](https://www.stackage.org/package/hexml) [![Build status](https://img.shields.io/github/actions/workflow/status/ndmitchell/hexml/ci.yml?branch=master)](https://github.com/ndmitchell/hexml/actions)  An XML DOM-style parser, that only parses a subset of XML, but is designed to be fast. In particular: @@ -8,8 +8,10 @@  The name "hexml" is a combination of "Hex" (a curse) and "XML". The "X" should not be capitalised because the parser is more curse and less XML. -Hexml may be suitable if you want to quickly parse XML, from known sources, and a full XML parser has been shown to be a bottleneck. As an alternative to hexml, which supports things like entities but is still pretty fast, see [Pugixml](http://pugixml.org/) (with a [Haskell binding](https://hackage.haskell.org/package/pugixml)).+Hexml may be suitable if you want to quickly parse XML, from known sources, and a full XML parser has been shown to be a bottleneck. As an alternative to hexml, which supports things like entities but is still pretty fast, see [Pugixml](http://pugixml.org/) (with a [Haskell binding](https://hackage.haskell.org/package/pugixml) - but be aware the Haskell binding [can segfault](https://github.com/philopon/pugixml-hs/issues/5)).  Hexml is tested with [AFL](http://lcamtuf.coredump.cx/afl/).  If you want lenses for Hexml, see [hexml-lens](http://hackage.haskell.org/package/hexml-lens).++The optimisation work around Hexml spawned [Xeno](http://hackage.haskell.org/package/xeno), a Haskell-only alternative to Hexml. There is a [talk](https://ndmitchell.com/#hexml_12_oct_2017) covering the performance tricks of Hexml and Xeno.
cbits/hexml.c view
@@ -40,7 +40,7 @@     // have a cursor at the end, which is stack scoped, so children write out, then I do     // when you commit, you copy over from end to front -    // nodes +    // nodes     int size;     int used_front; // front entries, stored for good     int used_back; // back entries, stack based, copied into front@@ -466,26 +466,44 @@             // have found a </             break;         }-        else if (peek_at(d, 1) == '!' && peek_at(d, 2) == '-' && peek_at(d, 3) == '-')+        else if (peek_at(d, 1) == '!')         {-            skip(d, 3);-            // you can't reuse the two '-' characters for the closing as well-            if (peek_at(d, 0) == '\0' || peek_at(d, 1) == '\0')-            {-                set_error(d, "Didn't get a closing comment");-                return start_end(0, 0);-            }-            skip(d, 2);-            for (;;)+            if (peek_at(d, 2) == '-' && peek_at(d, 3) == '-')             {-                if (!find(d, '>'))+                skip(d, 3);+                // you can't reuse the two '-' characters for the closing as well+                if (peek_at(d, 0) == '\0' || peek_at(d, 1) == '\0')                 {                     set_error(d, "Didn't get a closing comment");                     return start_end(0, 0);                 }-                skip(d, 1);-                if (peek_at(d, -3) == '-' && peek_at(d, -2) == '-')-                    break;+                skip(d, 2);+                for (;;)+                {+                    if (!find(d, '>'))+                    {+                        set_error(d, "Didn't get a closing comment");+                        return start_end(0, 0);+                    }+                    skip(d, 1);+                    if (peek_at(d, -3) == '-' && peek_at(d, -2) == '-')+                        break;+                }+            } else if (d->end - d->cursor >= 9 && memcmp(d->cursor + 2, "[CDATA[", 7) == 0) {+                skip(d, 9);+                for (;;)+                {+                    if (!find(d, '>'))+                    {+                        set_error(d, "Didn't close CDATA");+                        return start_end(0, 0);+                    }+                    skip(d, 1);+                    if (peek_at(d, -3) == ']' && peek_at(d, -2) == ']')+                        break;+                }+            } else {+                parse_tag(d);             }         }         else@@ -539,7 +557,7 @@     d->nodes.nodes[0].outer = start_length(0, slen);     d->nodes.nodes[0].inner = start_length(0, slen);     d->nodes.nodes[0].attrs = start_length(0, 0);-    +     // Introduce an intermediate result, otherwise behaviour is undefined     // because there is no guaranteed ordering between LHS and RHS evaluation     str content = parse_content(d);
hexml.cabal view
@@ -1,20 +1,20 @@-cabal-version:      >= 1.18+cabal-version:      1.18 build-type:         Simple name:               hexml-version:            0.3.4+version:            0.3.5 license:            BSD3 x-license:          BSD-3-Clause OR Apache-2.0 license-file:       LICENSE category:           XML author:             Neil Mitchell <ndmitchell@gmail.com> maintainer:         Neil Mitchell <ndmitchell@gmail.com>-copyright:          Neil Mitchell 2016-2018+copyright:          Neil Mitchell 2016-2024 synopsis:           XML subset DOM parser description:     An XML DOM-style parser, that only parses a subset of XML, but is designed to be fast. homepage:           https://github.com/ndmitchell/hexml#readme bug-reports:        https://github.com/ndmitchell/hexml/issues-tested-with:        GHC==8.4.3, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4, GHC==7.6.3+tested-with:        GHC==9.6, GHC==9.4, GHC==9.2, GHC==9.0, GHC==8.10, GHC==8.8 extra-doc-files:     README.md     CHANGES.txt
src/Main.hs view
@@ -20,6 +20,7 @@     ,(False, "<test")     ,(True, "<?xml version=\"1.1\"?>\n<greeting>Hello, world!</greeting>")     ,(True, "<foo bar.baz=\"qux\"></foo>")+    ,(True, "<test><![CDATA[foo<x>]]</y>]><z>]baz]]></test>")     ]  main :: IO ()@@ -44,12 +45,12 @@     attributes (head $ children doc) === [Attribute "id" "1", Attribute "extra" "2"]     map (`attributeBy` "id") (childrenBy doc "test") === map (fmap (Attribute "id")) [Just "1", Just "2", Just "4", Nothing] -    Right _ <- return $ parse $ "<test " <> BS.unwords [BS.pack $ "x" ++ show i ++ "='value'" | i <- [1..10000]] <> " />"-    Right _ <- return $ parse $ BS.unlines $ replicate 10000 "<test x='value' />"+    Right _ <- pure $ parse $ "<test " <> BS.unwords [BS.pack $ "x" ++ show i ++ "='value'" | i <- [1..10000]] <> " />"+    Right _ <- pure $ parse $ BS.unlines $ replicate 10000 "<test x='value' />"      let attrs = ["usd:jpy","test","extra","more","stuff","jpy:usd","xxx","xxxx"]-    Right doc <- return $ parse $ "<test " <> BS.unwords [x <> "='" <> x <> "'" | x <- attrs] <> ">middle</test>"-    [c] <- return $ childrenBy doc "test"+    Right doc <- pure $ parse $ "<test " <> BS.unwords [x <> "='" <> x <> "'" | x <- attrs] <> ">middle</test>"+    [c] <- pure $ childrenBy doc "test"     forM_ attrs $ \a -> attributeBy c a === Just (Attribute a a)     forM_ ["missing","gone","nothing"] $ \a -> attributeBy c a === Nothing     putStrLn "\nSuccess"@@ -80,5 +81,5 @@                     | otherwise = error "Invalid name"         validAttr x | BS.notElem '\"' x = x                     | otherwise = error "Invalid attribute"-        validStr x | BS.notElem '<' x || BS.isInfixOf "<!--" x = x+        validStr x | BS.notElem '<' x || BS.isInfixOf "<!--" x || BS.isInfixOf "<![CDATA[" x = x                    | otherwise = error $ show ("Invalid string", x)
src/Text/XML/Hexml.hs view
@@ -90,11 +90,11 @@         if err /= nullPtr then do             bs <- BS.packCString =<< hexml_document_error doc             hexml_document_free doc-            return $ Left bs+            pure $ Left bs          else do             node <- hexml_document_node doc             doc <- newForeignPtr hexml_document_free_funptr doc-            return $ Right $ Node src0 doc node+            pure $ Right $ Node src0 doc node  -- | Given a node, rerender it to something with an equivalent parse tree. --   Mostly useful for debugging - if you want the real source document use 'outer' instead.@@ -103,7 +103,7 @@     i <- hexml_node_render d n nullPtr 0     res <- BS.create (fromIntegral i) $ \ptr -> void $ hexml_node_render d n (castPtr ptr) i     touchBS src-    return res+    pure res  applyStr :: BS.ByteString -> Str -> BS.ByteString applyStr bs Str{..} = BS.take (fromIntegral strLength) $ BS.drop (fromIntegral strStart) bs@@ -118,7 +118,7 @@ attrPeek src doc a = unsafePerformIO $ withForeignPtr doc $ \_ -> do     name <- applyStr src <$> peekElemOff (castPtr a) 0     val  <- applyStr src <$> peekElemOff (castPtr a) 1-    return $ Attribute name val+    pure $ Attribute name val  -- | Get the name of a node, e.g. @\<test /\>@ produces @\"test\"@. name :: Node -> BS.ByteString@@ -142,7 +142,7 @@ contents :: Node -> [Either BS.ByteString Node] contents n@(Node src _ _) = f (strStart inner) outers     where-        f i [] = string i (strEnd inner) ++ []+        f i [] = string i (strEnd inner)         f i ((x, n):xs) = string i (strStart x) ++ Right n : f (strEnd x) xs          string start end | start == end = []@@ -156,7 +156,7 @@     alloca $ \count -> do         res <- hexml_node_children d n count         count <- fromIntegral <$> peek count-        return [Node src doc $ plusPtr res $ i*szNode | i <- [0..count-1]]+        pure [Node src doc $ plusPtr res $ i*szNode | i <- [0..count-1]]  -- | Get the attributes of this node. attributes :: Node -> [Attribute]@@ -164,7 +164,7 @@     alloca $ \count -> do         res <- hexml_node_attributes d n count         count <- fromIntegral <$> peek count-        return [attrPeek src doc $ plusPtr res $ i*szAttr | i <- [0..count-1]]+        pure [attrPeek src doc $ plusPtr res $ i*szAttr | i <- [0..count-1]]  -- | Get the direct children of this node which have a specific name. --   A more efficient version of:@@ -177,7 +177,7 @@             BS.unsafeUseAsCStringLen str $ \(bs, len) -> do                 r <- hexml_node_child d n old bs $ fromIntegral len                 touchBS src-                return $ if r == nullPtr then [] else Node src doc r : go r+                pure $ if r == nullPtr then [] else Node src doc r : go r  -- | Get the first attribute of this node which has a specific name, if there is one. --   A more efficient version of:@@ -188,7 +188,7 @@     BS.unsafeUseAsCStringLen str $ \(bs, len) -> do         r <- hexml_node_attribute d n bs $ fromIntegral len         touchBS src-        return $ if r == nullPtr then Nothing else Just $ attrPeek src doc r+        pure $ if r == nullPtr then Nothing else Just $ attrPeek src doc r  -- | Find the starting location of a node, the @<@ character. --   The first character will be reported as @(line 1,column 1)@, because thats