Commit Graph

1228 Commits

Author SHA1 Message Date
Yorick Peterse c98ba21a87 Ruby generator support for mass assignments 2015-08-19 20:14:21 +02:00
Yorick Peterse 53c142ca26 Prefix compiler predicate methods with on_ 2015-08-19 20:14:21 +02:00
Yorick Peterse bea7e19d57 Cast compiled predicate indexes to integers 2015-08-19 20:14:21 +02:00
Yorick Peterse 1f2545c03b Renamed xpath_number? to number? 2015-08-19 20:14:21 +02:00
Yorick Peterse 0e9f533358 Use return_nodeset? vs USE_NODESET 2015-08-19 20:14:21 +02:00
Yorick Peterse 9000d5efdb XPath::Compiler#compile specifies blocks for paths
Previously the on_path method itself would specify a block to use when a
node was matched. This makes it impossible to customize this behaviour
which is needed when a path is used in an operator or predicate. By
letting the #compile() method specify the block other handlers can
overwrite it.
2015-08-19 20:14:21 +02:00
Yorick Peterse e549b28ca4 Removed useless YARD docs from the XPath compiler
The method names already explain their intent.
2015-08-19 20:14:21 +02:00
Yorick Peterse 20c6bbdaa8 Removed left-over comment in the XPath compiler 2015-08-19 20:14:21 +02:00
Yorick Peterse 616fd42600 XPath compiler support for the "attribute" axis 2015-08-19 20:14:21 +02:00
Yorick Peterse 4f03bf19c1 XPath compiler support for "ancestor" 2015-08-19 20:14:21 +02:00
Yorick Peterse f96b30fb1f Tag the XPath compiler as private 2015-08-19 20:14:21 +02:00
Yorick Peterse f7484f1c8d Use each_ancestor for ancestor-or-self in XPath
The XPath compiler now uses XML::Node#each_ancestor for compiling the
"ancestor-or-self" axis.
2015-08-19 20:14:20 +02:00
Yorick Peterse 52741a3b78 Added XML::Node#each_ancestor
This method can be used to walk through the ancestor tree of a Node.
2015-08-19 20:14:20 +02:00
Yorick Peterse db39b25546 XPath compiler support for ancestor-or-self
This also comes with some changes to the specs as the old behaviour of
the Evaluator was incorrect. The Evaluator would bail after matching a
single node but instead it's meant to continue until it runs out of
parent nodes.
2015-08-19 20:14:20 +02:00
Yorick Peterse d8fbaf75d8 Ruby generator support for while loops 2015-08-19 20:14:20 +02:00
Yorick Peterse 9925b2a9c9 XPath compiler support for predicates
This currently supports index predicates (e.g. "foo[10]") and predicates
using paths (e.g. foo[bar/baz]). The usage of boolean operators and more
complex expressions has not yet been tested as these are not yet
supported in the first place.
2015-08-19 20:14:20 +02:00
Yorick Peterse 7fdf8d7460 Rewrote XPath compiler predicate specs 2015-08-19 20:14:20 +02:00
Yorick Peterse 6f6151fd52 Added Ruby generator support for Symbols 2015-08-19 20:14:20 +02:00
Yorick Peterse 05a57a807e XPath compiler support for variable references 2015-08-19 20:14:20 +02:00
Yorick Peterse cf2405998b Ruby::Generator support for #[] methods 2015-08-19 20:14:20 +02:00
Yorick Peterse 17f6b3a3bb Added simple method docs for the XPath compiler 2015-08-19 20:14:20 +02:00
Yorick Peterse 2c1b4e7cbc Support for generating "else" statements 2015-08-19 20:14:20 +02:00
Yorick Peterse ac6c0d806e Updated XPath variables spec to use the compiler 2015-08-19 20:14:20 +02:00
Yorick Peterse ebcadd1cb8 XPath compiler support for absolute paths/integers 2015-08-19 20:14:20 +02:00
Yorick Peterse 94f7f85dc3 Added XML::Document#root_node 2015-08-19 20:14:20 +02:00
Yorick Peterse a7744b7a5c Use the XPath compiler for XPath/CSS specs 2015-08-19 20:14:20 +02:00
Yorick Peterse 3300a6df49 Added XPath::Compiler.compile_with_cache 2015-08-19 20:14:20 +02:00
Yorick Peterse 6d01adafc7 XPath compiler now actually returns a Proc 2015-08-19 20:14:20 +02:00
Yorick Peterse d11a754946 Basic support for compiling XPath to Ruby
This currently comes with exactly 0% test coverage. Once I've
implemented all required handler methods I'll be updating the current
evaluator tests to use the compiler instead. This removes the need for
writing an entirely new set of tests.

Currently the compiler is only capable of compiling basic expressions
such as "foo", "foo/bar" and "foo[@x="y"]/bar".
2015-08-19 20:14:20 +02:00
Yorick Peterse 6daff674d9 Use "parse" instead of "parse_xml" 2015-08-19 20:14:20 +02:00
Yorick Peterse 337d126264 Added Ruby::Generator class
This class will be used to serialize a Ruby AST back to valid Ruby
source code (as a String).
2015-08-19 20:14:20 +02:00
Yorick Peterse 6673f176d8 Added Oga::Ruby::Node
This class will be used for building Ruby ASTs that will be generated
based on XPath expressions.
2015-08-19 20:14:20 +02:00
Yorick Peterse c25879f18e Removed some explicit returns in .rll files 2015-08-19 20:14:19 +02:00
Yorick Peterse 08c965bfbc Basic specs for the XPath compiler 2015-08-19 20:14:19 +02:00
Yorick Peterse f10b7aa065 Super basic boilerplate for the XPath compiler 2015-08-19 20:14:19 +02:00
Yorick Peterse 46f3ec9df5 Configurable samples in the concurrent xpath bench 2015-08-19 13:34:41 +02:00
Yorick Peterse 399403c290 Release 1.2.3 2015-08-19 12:09:14 +02:00
Yorick Peterse 4f94d03a85 Improved NodeSet performance by a factor of 50
This change is broken up in to two parts:

1. Using a Hash to track if a node is already in a NodeSet
2. Only calling take_ownership when an owner is set

== Using a Hash

Previously various methods such as NodeSet#push and NodeSet#unshift
would call Array#include? (on the internal "nodes" Array) to see if a
node is already present in the set. This is quite problematic
performance wise, especially for large NodeSets. In fact, for the
attached benchmark the vast majority of the time was spent in
Array#include? calls.

Because a NodeSet demands ordering of nodes and must be able to access
them by index (something Set can't do without relying on Enumerable), a
Hash is used to separately keep track of what nodes are in a NodeSet.
This means that checking the presence of a node is simply a matter of
checking a Hash key's presence.

== Calling take_ownership

The if-check for the "owner" variable has been moved out of the
"take_ownership" method and into the methods that call "take_ownership".
This ensures the method isn't called in the first place if no owner is
present, at the cost of slightly more code repetition. The same applies
to the "remove_ownership" method.

== Conclusion

The combined result is a speedup of about 50x when running the attached
concurrent_time_bench.rb benchmark.
2015-08-19 01:36:37 +02:00
Yorick Peterse 77c6f3af2a Added Ox and Nokogiri to the Gemfile
This allows these Gems to be used in the benchmarks while also making it
a tad easier to install them.
2015-08-19 01:28:34 +02:00
Yorick Peterse 4ba9af4f74 Fixed oga requires for benchmarking/profiling 2015-08-19 01:27:01 +02:00
Yorick Peterse b8415b7376 Revert "Removed bundler/setup requires"
This reverts commit ed9ead4fc0.
2015-08-19 01:26:03 +02:00
Yorick Peterse f5dae24c39 Added ips benchmark for NodeSet#push 2015-08-19 01:20:27 +02:00
Yorick Peterse ed9ead4fc0 Removed bundler/setup requires
This allows loading of any other Gems (e.g. when testing things out)
without having to add them to the Gemspec/Gemfile.
2015-08-18 22:48:00 +02:00
Yorick Peterse 4346cc6ec3 Added a KAF fixture file
This is an output document from one of the components of the
OpeNER (http://www.opener-project.eu/) project.
2015-08-18 22:32:57 +02:00
Dan Fockler be7bc8f423 make string comparison faster 2015-08-15 06:36:27 -07:00
Dan Fockler fc38b39aa3 make string comparison a bit faster 2015-08-15 06:34:09 -07:00
Daniel Fockler 496811a23f Fixes #127 2015-08-14 16:15:49 -07:00
Yorick Peterse 0977be81bb Release 1.2.2 2015-08-14 14:49:26 +02:00
Yorick Peterse 9b98e75115 Use pop/push vs shift/unshift in each_node
While the performance difference between the old and new approach is
pretty much negligible, it's simply not needed to use #shift/#unshift
here.

Thanks to Mon_Ouie from the #ruby IRC channel for suggesting this.
2015-08-07 14:08:54 +02:00
Yorick Peterse 32b75bf62c Reset the owner ivar in LRU#synchronize
Without this the following could happen:

1. Thread A acquires the lock and sets the ownership to A.
2. Thread A yields and returns
3. Thread B tries to acquire the lock
4. At this exact moment Thread A calls the "synchronize" method again
   and sees that the "owner" variable is still set to Thread A
5. Both thread A and B can now access the underlying data in parallel,
   possibly leading to corrupted objects

This can be demonstrated using the following script:

    require 'oga'

    lru = Oga::LRU.new(64)

    threads = 50.times.map do
      Thread.new do
        loop do
          number = rand(100)

          lru[number] = number
        end
      end
    end

    threads.each(&:join)

Run this for a while on either JRuby or Rubinius and you'll end up with
errors such as "ConcurrencyError: Detected invalid array contents due to
unsynchronized modifications with concurrent users" on JRuby or
"ArgumentError: negative array size" on Rubinius.

Resetting the owner variable ensures the above can never happen. Thanks
to @chrisseaton for bringing this up earlier today.
2015-08-03 21:47:40 +02:00