diff -Nru ruby-ox-2.11.0/build_test.sh ruby-ox-2.14.9/build_test.sh
--- ruby-ox-2.11.0/build_test.sh 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/build_test.sh 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# 1.8.7-p374\
+
+for ruby in \
+ rbx-2.2.6\
+ 1.9.3-p547\
+ 2.1.5\
+ 2.2.2\
+ 2.3.3\
+ 2.4.1 \
+do
+ echo "\n********************************************************************************"
+ echo "Building $ruby\n"
+ cd ext/ox
+ rbenv local $ruby
+ ruby extconf.rb
+ make clean
+ find . -name "*.rbc" -exec rm {} \;
+ make
+
+ echo "\nRunning tests for $ruby"
+ cd ../../test
+ rbenv local $ruby
+ ./tests.rb
+ cd sax
+ rbenv local $ruby
+ ./sax_test.rb
+ cd ../..
+
+ echo "\n"
+done
+
+PATH=/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin:$PATH
+echo "\n********************************************************************************"
+echo "Building OS X Ruby\n"
+cd ext/ox
+ruby extconf.rb
+make clean
+make
+
+echo "\nRunning tests for OS X Ruby"
+cd ../../test
+./tests.rb
+./sax/sax_test.rb
+cd ..
+
+echo "resetting to 2.4.1\n"
+
+cd ext/ox
+rbenv local 2.4.1
+cd ../../test
+rbenv local 2.4.1
+cd sax
+rbenv local 2.4.1
+cd ../..
+echo "\n"
diff -Nru ruby-ox-2.11.0/CHANGELOG.md ruby-ox-2.14.9/CHANGELOG.md
--- ruby-ox-2.11.0/CHANGELOG.md 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/CHANGELOG.md 2022-02-10 23:24:14.000000000 +0000
@@ -2,7 +2,126 @@
All changes to the Ox gem are documented here. Releases follow semantic versioning.
-## [Unreleased]
+## [2.14.9] - 2022-02-11
+
+### Fixed
+
+- Fixed the `\r` replacement with `\n` with the SAX parser according to https://www.w3.org/TR/2008/REC-xml-20081126/#sec-line-ends.
+
+## [2.14.8] - 2022-02-09
+
+### Fixed
+
+- Renamed internal functions to avoid linking issues where Oj and Ox function names collided.
+
+## [2.14.7] - 2022-02-03
+
+### Fixed
+
+- All classes and symbols are now registered to avoid issues with GC compaction movement.
+- Parsing of any size processing instruction is now allowed. There is no 1024 limit.
+- Fixed the `\r` replacement with `\n` according to https://www.w3.org/TR/2008/REC-xml-20081126/#sec-line-ends.
+
+### Changed
+
+- Symbol and string caching changed but should have no impact on use
+ other than being slightly faster and handles large numbers of cached
+ items more efficiently.
+
+## [2.14.6] - 2021-11-03
+
+### Fixed
+
+- Closing tags in builder are now escapped correctly thanks to ezekg.
+
+## [2.14.5] - 2021-06-04
+
+### Fixed
+
+- Fixed RDoc for for Ox::Builder.
+
+## [2.14.4] - 2021-03-19
+
+### Fixed
+
+- Really fixed code issue around HAVE_RB_ENC_ASSOCIATE.
+
+## [2.14.3] - 2021-03-12
+
+### Fixed
+
+- Code issue around HAVE_RB_ENC_ASSOCIATE fixed.
+
+## [2.14.2] - 2021-03-07
+
+### Fixed
+
+- Attribute keys for setting attributes no longer create seemily
+ duplicates if symbol and string keys are mixed.
+
+## [2.14.1] - 2021-01-11
+
+### Fixed
+
+- In Ruby 3.0 Range objects are frozen. This version allows Ranges to be created on load.
+
+## [2.14.0] - 2020-12-15
+
+### Added
+
+- The `:with_cdata` option added for the hash_load() function.
+
+## [2.13.4] - 2020-09-11
+
+### Fixed
+
+- Fixed one crash that occurred when a corrupted object encoded string was provided.
+
+## [2.13.3] - 2020-09-03
+
+### Changed
+
+- mkmf have macros used instead of ad-hoc determinations.
+
+## [2.13.2] - 2020-02-05
+
+Skip and missed sequence
+
+### Fixed
+
+- Add ' sequence.
+
+- `:skip_off` no longer misses spaces between elements.
+
+## [2.13.1] - 2020-01-30
+
+HTML Sequences
+
+### Added
+
+- All HTML 4 sequence are now supported.
+
+## [2.13.0] - 2020-01-25
+
+HTML Escape Sequences
+
+### Added
+
+- All HTML 4 escape sequences are now parsed.
+
+## [2.12.1] - 2020-01-05
+
+Ruby 2.7.0
+
+### Fixed
+
+- Updated for Ruby 2.7.0. More strict type checking. Function signature changes, and `Object#taint` deprecated.
+
+## [2.12.0] - 2019-12-18
+
+### Added
+
+- Add `no_empty` option to not allow and use instead.
## [2.11.0] - 2019-06-14
diff -Nru ruby-ox-2.11.0/.clang-format ruby-ox-2.14.9/.clang-format
--- ruby-ox-2.11.0/.clang-format 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/.clang-format 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,167 @@
+---
+Language: Cpp
+# BasedOnStyle: Google
+AccessModifierOffset: -1
+AlignAfterOpenBracket: Align
+AlignConsecutiveMacros: false
+AlignConsecutiveAssignments: true
+AlignConsecutiveDeclarations: true
+AlignEscapedNewlines: Left
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: false
+AllowAllConstructorInitializersOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: InlineOnly
+AllowShortLambdasOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 120
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DeriveLineEnding: true
+DerivePointerAlignment: true
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^'
+ Priority: 2
+ SortPriority: 0
+ - Regex: '^<.*\.h>'
+ Priority: 1
+ SortPriority: 0
+ - Regex: '^<.*'
+ Priority: 2
+ SortPriority: 0
+ - Regex: '.*'
+ Priority: 3
+ SortPriority: 0
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+IncludeIsMainSourceRegex: ''
+IndentCaseLabels: false
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Never
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2000
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 100
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 100
+PenaltyReturnTypeOnItsOwnLine: 1
+PointerAlignment: Right
+RawStringFormats:
+ - Language: Cpp
+ Delimiters:
+ - cc
+ - CC
+ - cpp
+ - Cpp
+ - CPP
+ - 'c++'
+ - 'C++'
+ CanonicalDelimiter: ''
+ BasedOnStyle: google
+ - Language: TextProto
+ Delimiters:
+ - pb
+ - PB
+ - proto
+ - PROTO
+ EnclosingFunctions:
+ - EqualsProto
+ - EquivToProto
+ - PARSE_PARTIAL_TEXT_PROTO
+ - PARSE_TEST_PROTO
+ - PARSE_TEXT_PROTO
+ - ParseTextOrDie
+ - ParseTextProtoOrDie
+ CanonicalDelimiter: ''
+ BasedOnStyle: google
+ReflowComments: true
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+SpaceBeforeSquareBrackets: false
+Standard: Auto
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TabWidth: 8
+UseCRLF: false
+UseTab: Never
+...
diff -Nru ruby-ox-2.11.0/contrib/README.md ruby-ox-2.14.9/contrib/README.md
--- ruby-ox-2.11.0/contrib/README.md 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/contrib/README.md 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,8 @@
+# Contributions
+
+This directory is for people who have found useful functions and routines that makes use of or enhances Ox that they
+would like to share with others. Fell free to put in a pull request if you would like to contribute. Place the code and
+the tests in a file with a title that provides some clue as to the contents and I'll pull it in.
+
+
+
diff -Nru ruby-ox-2.11.0/contrib/sax_benchmark.rb ruby-ox-2.14.9/contrib/sax_benchmark.rb
--- ruby-ox-2.11.0/contrib/sax_benchmark.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/contrib/sax_benchmark.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,128 @@
+# All credit to https://github.com/hakanensari
+# Taken from https://gist.github.com/hakanensari/3078932
+
+require 'benchmark'
+require 'pp'
+require 'stringio'
+
+require 'nokogiri'
+require 'ox'
+
+io = StringIO.new %{
+
+
+
+
+ 0816614024
+
+ Gilles Deleuze
+ Felix Guattari
+ Thousand Plateaus
+
+
+
+ 0231081596
+
+ Gilles Deleuze
+ Difference and Repetition
+
+
+
+
+}.strip.gsub />\s+, '><'
+
+class OxHandler < Ox::Sax
+ attr :root
+
+ def initialize
+ @stack = [@node = @root = {}]
+ end
+
+ def attr(key, val)
+ @node[key] = val
+ end
+
+ def end_element(key)
+ child = @stack.pop
+ @node = @stack.last
+
+ case @node[key]
+ when Array
+ @node[key] << child
+ when Hash
+ @node[key] = [@node[key], child]
+ else
+ if child.keys == [:__content__]
+ @node[key] = child[:__content__]
+ else
+ @node[key] = child
+ end
+ end
+ end
+
+ def start_element(key)
+ @stack << @node = {}
+ end
+
+ def text(val)
+ @node[:__content__] = val
+ end
+end
+
+class NokogiriHandler < Nokogiri::XML::SAX::Document
+ attr :root
+
+ def characters(val)
+ (@node['__content__'] ||= '') << val
+ end
+
+ def end_element(key)
+ child = @stack.pop
+ @node = @stack.last
+
+ case @node[key]
+ when Array
+ @node[key] << child
+ when Hash
+ @node[key] = [@node[key], child]
+ else
+ if child.keys == ['__content__']
+ @node[key] = child['__content__']
+ else
+ @node[key] = child
+ end
+ end
+ end
+
+ def start_element(key, attrs = [])
+ @stack << @node = {}
+ attrs.each do |attr|
+ key, val = *attr
+ @node[key] = val
+ end
+ end
+
+ def start_document
+ @stack = [@root = {}]
+ end
+end
+
+n = 10000
+Benchmark.bmbm do |b|
+ b.report('ox') do
+ n.times do
+ io.rewind
+ handler = OxHandler.new
+ Ox.sax_parse handler, io
+ end
+ end
+
+ b.report('nokogiri') do
+ n.times do
+ io.rewind
+ handler = NokogiriHandler.new
+ parser = Nokogiri::XML::SAX::Parser.new handler
+ parser.parse io
+ end
+ end
+end
diff -Nru ruby-ox-2.11.0/contrib/xbuilder.rb ruby-ox-2.14.9/contrib/xbuilder.rb
--- ruby-ox-2.11.0/contrib/xbuilder.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/contrib/xbuilder.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,34 @@
+
+# Contributed by Notezen
+
+require 'ox'
+module Ox
+ module XBuilder
+ # args = attributes and/or children in any order, multiple appearance is possible
+ # @overload build(name,attributes,children)
+ # @param [String] name name of the Element
+ # @param [Hash] attributes
+ # @param [String|Element|Array] children text, child element or array of elements
+ def x(name, *args)
+ n = Element.new(name)
+
+ for arg in args
+ case arg
+ when Hash
+ arg.each { |k,v| n[k.to_s] = v }
+ when Array
+ arg.each { |c| n << c if c}
+ else
+ n << arg if arg
+ end
+ end
+
+ n
+ end
+
+ def x_if(condition, *args)
+ x(*args) if condition
+ end
+
+ end
+end
diff -Nru ruby-ox-2.11.0/contrib/xbuilder_test.rb ruby-ox-2.14.9/contrib/xbuilder_test.rb
--- ruby-ox-2.11.0/contrib/xbuilder_test.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/contrib/xbuilder_test.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,50 @@
+#!/usr/bin/env ruby
+# encoding: UTF-8
+
+# Contributed by Notezen
+
+$: << File.join(File.dirname(__FILE__), "../lib")
+$: << File.join(File.dirname(__FILE__), "../ext")
+$: << File.join(File.dirname(__FILE__), ".")
+
+require 'test/unit'
+require 'ox'
+require 'xbuilder'
+
+class XBuilderTest < ::Test::Unit::TestCase
+ include Ox::XBuilder
+
+ def test_build
+ xml =
+ '
+
+ some text
+
+
+
+
+
+ another text
+'
+
+ children =
+ [
+ x_if(true,'child3'),
+ x('child4','another text','atr4' => 'atr4_value'),
+ x_if(false,'child5')
+ ]
+
+ n=x('parent',
+ x('child1',{'atr1'=>'atr1_value',:atr2 => 'atr2_value'},
+ x('subchild1','some text'),
+ x('subchild2',{'atr3' => 'default_value'},{'atr3' => 'atr3_value'},
+ x('sometag'),
+ x_if(false,'never')
+ )
+ ),
+ children
+ )
+
+ assert_equal(Ox.dump(n),Ox.dump(Ox.parse(xml)))
+ end
+end
diff -Nru ruby-ox-2.11.0/debian/changelog ruby-ox-2.14.9/debian/changelog
--- ruby-ox-2.11.0/debian/changelog 2021-12-03 18:39:31.000000000 +0000
+++ ruby-ox-2.14.9/debian/changelog 2022-03-04 22:20:25.000000000 +0000
@@ -1,14 +1,25 @@
-ruby-ox (2.11.0-2build2) jammy; urgency=medium
+ruby-ox (2.14.9-1) unstable; urgency=medium
- * No-change upload due to ruby3.0 transition, remove ruby2.7 support.
+ * Team upload.
- -- Lucas Kanashiro Fri, 03 Dec 2021 15:39:31 -0300
+ [ Debian Janitor ]
+ * Use secure URI in Homepage field.
+ * Set upstream metadata fields: Bug-Submit.
+
+ [ Leandro Cunha ]
+ * New upstream version.
+ * debian/control:
+ - Bump Standards-Version to 4.6.0.
+ - Removed ruby-interpreter is deprecated reported by Lintian in depends.
+ * debian/watch:
+ - Import tarball from GitHub repository.
+ * debian/copyright:
+ - Add myself.
+ - Update year to author of project.
+ * debian/ruby-ox.install:
+ - Install examples and fix Lintian report.
-ruby-ox (2.11.0-2build1) jammy; urgency=medium
-
- * No-change upload due to ruby3.0 transition.
-
- -- Lucas Kanashiro Wed, 17 Nov 2021 18:40:09 -0300
+ -- Leandro Cunha Fri, 04 Mar 2022 19:20:25 -0300
ruby-ox (2.11.0-2) unstable; urgency=medium
diff -Nru ruby-ox-2.11.0/debian/control ruby-ox-2.14.9/debian/control
--- ruby-ox-2.11.0/debian/control 2021-02-07 20:40:51.000000000 +0000
+++ ruby-ox-2.14.9/debian/control 2022-03-04 22:20:25.000000000 +0000
@@ -5,11 +5,11 @@
Uploaders: Paul van Tilburg
Build-Depends: gem2deb,
debhelper-compat (= 13)
-Standards-Version: 4.5.1
+Standards-Version: 4.6.0
Rules-Requires-Root: no
Vcs-Git: https://salsa.debian.org/ruby-team/ruby-ox.git
Vcs-Browser: https://salsa.debian.org/ruby-team/ruby-ox
-Homepage: http://www.ohler.com/ox
+Homepage: https://www.ohler.com/ox
XS-Ruby-Versions: all
Testsuite: autopkgtest-pkg-ruby
@@ -17,7 +17,7 @@
Architecture: any
Multi-Arch: same
XB-Ruby-Versions: ${ruby:Versions}
-Depends: ruby | ruby-interpreter,
+Depends: ruby,
${misc:Depends},
${shlibs:Depends}
Description: fast XML parser and object serializer
diff -Nru ruby-ox-2.11.0/debian/copyright ruby-ox-2.14.9/debian/copyright
--- ruby-ox-2.11.0/debian/copyright 2021-02-07 20:40:51.000000000 +0000
+++ ruby-ox-2.14.9/debian/copyright 2022-03-04 22:20:25.000000000 +0000
@@ -4,7 +4,7 @@
Source: https://github.com/ohler55/ox
Files: *
-Copyright: 2011, Peter Ohler
+Copyright: 2011-2022, Peter Ohler
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -33,6 +33,7 @@
Files: debian/*
Copyright: 2013, Paul van Tilburg
+ 2022, Leandro Cunha
License: GPL-2+
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
diff -Nru ruby-ox-2.11.0/debian/ruby-ox.install ruby-ox-2.14.9/debian/ruby-ox.install
--- ruby-ox-2.11.0/debian/ruby-ox.install 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/debian/ruby-ox.install 2022-03-04 22:20:25.000000000 +0000
@@ -0,0 +1 @@
+examples/ /usr/share/doc/ruby-ox
diff -Nru ruby-ox-2.11.0/debian/upstream/metadata ruby-ox-2.14.9/debian/upstream/metadata
--- ruby-ox-2.11.0/debian/upstream/metadata 2021-02-07 20:40:51.000000000 +0000
+++ ruby-ox-2.14.9/debian/upstream/metadata 2022-03-04 22:20:25.000000000 +0000
@@ -1,4 +1,5 @@
Archive: GitHub
Bug-Database: https://github.com/ohler55/ox/issues
+Bug-Submit: https://github.com/ohler55/ox/issues/new
Repository: https://github.com/ohler55/ox.git
Repository-Browse: https://github.com/ohler55/ox
diff -Nru ruby-ox-2.11.0/debian/watch ruby-ox-2.14.9/debian/watch
--- ruby-ox-2.11.0/debian/watch 2021-02-07 20:40:51.000000000 +0000
+++ ruby-ox-2.14.9/debian/watch 2022-03-04 22:20:25.000000000 +0000
@@ -1,2 +1,3 @@
version=4
-https://gemwatch.debian.net/ox .*/ox-(.*).tar.gz
+opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/ox-$1\.tar\.gz/ \
+ https://github.com/ohler55/ox/tags .*/v?(\d\S+)\.tar\.gz
diff -Nru ruby-ox-2.11.0/examples/gen.rb ruby-ox-2.14.9/examples/gen.rb
--- ruby-ox-2.11.0/examples/gen.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/examples/gen.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,50 @@
+#!/usr/bin/env ruby
+
+# This example demonstrates loading an XML modifying it and then dumping it.
+
+# Use the current repo if run from the examples directory.
+ox_dir = File.dirname(File.dirname(File.expand_path(__FILE__)))
+$LOAD_PATH << File.join(ox_dir, 'ext')
+$LOAD_PATH << File.join(ox_dir, 'lib')
+
+require 'ox'
+
+xml = %{
+
+
+ mutant
+
+ 5
+
+ dog
+
+
+
+}
+
+# Load the XML into a set of Ox::Nodes.
+doc = Ox.load(xml, mode: :generic)
+
+# Once an Ox::Document is loaded it can be inspected and modified. A Doc has a
+# root. Calling doc.root will give a node that is the root of the XML which is
+# the Park.Animal element.
+root = doc.root
+puts "root element name: #{root.name}"
+
+# The Ox::Element.locate method can be used similar to XPath. It does not have
+# all the features of XPath but it does help dig into an XML. Look for any
+# descendent of the root that has a type attribute and return those attribute
+# values.
+puts "descendent type attribute value: #{root.locate('*/@type')}"
+
+# Delete 'i' element by iterating over the root's nodes and look for one named
+# friends. The locate method could also be used.
+root.nodes.each { |n|
+ if n.name == 'friends'
+ n.nodes.delete_if { |child| child.name == 'i' }
+ end
+}
+
+# Lets take a look at the changes by dumping the doc.
+xml2 = Ox.dump(doc)
+puts "modified XML: #{xml2}"
diff -Nru ruby-ox-2.11.0/examples/hashi.rb ruby-ox-2.14.9/examples/hashi.rb
--- ruby-ox-2.11.0/examples/hashi.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/examples/hashi.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,38 @@
+#!/usr/bin/env ruby
+
+# This example demonstrates the use of Ox.load using the :hash and
+# :hash_no_attrs modes.
+
+# Use the current repo if run from the examples directory.
+ox_dir = File.dirname(File.dirname(File.expand_path(__FILE__)))
+$LOAD_PATH << File.join(ox_dir, 'ext')
+$LOAD_PATH << File.join(ox_dir, 'lib')
+
+require 'ox'
+
+# load or use this sample string.
+xml = %{
+
+
+ mutant
+
+ 5
+
+ dog
+
+
+
+}
+
+doc = Ox.load(xml, mode: :hash)
+puts "as hash with Symbol element names: #{doc}"
+
+# Load the XML and convert to a Hash. By default element names are
+# symbolized. By using the :symbolize_keys option and setting it to false the
+# element names will be strings.
+doc = Ox.load(xml, mode: :hash, symbolize_keys: false)
+puts "as hash with String element names: #{doc}"
+
+# The :hash_no_attrs mode leaves attributes out of the resulting Hash.
+doc = Ox.load(xml, mode: :hash_no_attrs)
+puts "as hash_no_attrs: #{doc}"
diff -Nru ruby-ox-2.11.0/examples/obj.rb ruby-ox-2.14.9/examples/obj.rb
--- ruby-ox-2.11.0/examples/obj.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/examples/obj.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+
+# This example demonstrates encoding and decoding a Ruby object.
+
+# Use the current repo if run from the examples directory.
+ox_dir = File.dirname(File.dirname(File.expand_path(__FILE__)))
+$LOAD_PATH << File.join(ox_dir, 'ext')
+$LOAD_PATH << File.join(ox_dir, 'lib')
+
+require 'ox'
+
+# Define a class that will be used for instances that are encoded and decoded.
+class Classy
+ def initialize(a, b)
+ @a = a
+ @b = b
+ end
+
+ def to_s
+ "Classy a: #{@a}, b: #{@b}"
+ end
+end
+
+obj = Classy.new(23, ['abc', {x: true}])
+
+doc = Ox.dump(obj, mode: :object)
+
+# The encoded format is not important other and should ot be generated by
+# hand. It is of interest only for the curious.
+puts "encoded object:\n#{doc}"
+
+# Now convert back to a Ruby object.
+obj2 = Ox.load(doc, mode: :object)
+
+# Looks the same, print it out to check.
+puts "decoded object: #{obj2}"
diff -Nru ruby-ox-2.11.0/examples/sax_ractor.rb ruby-ox-2.14.9/examples/sax_ractor.rb
--- ruby-ox-2.11.0/examples/sax_ractor.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/examples/sax_ractor.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,194 @@
+#!/usr/bin/env ruby
+
+require 'ox'
+require 'pathname'
+
+# Silence Ractor warning in Ruby 3.0.x
+Warning[:experimental] = false
+abort("This Ractor example requires at least Ruby 3.0") if RUBY_VERSION.start_with?("2")
+
+# Example/test script for `Ractor`-based `Ox::Sax` parsing.
+# In the Real World™ we probably wouldn't create a single-use `Ractor` for
+# every argument, but this is primarily a test of `rb_ext_ractor_safe` for Ox.
+
+
+# Miniature example Ractor-based `shared-mime-info` Ox handler à la `CHECKING::YOU::OUT`:
+# https://github.com/okeeblow/DistorteD/tree/NEW-SENSATION/CHECKING-YOU-OUT
+class Saxtor < Ox::Sax
+
+ # We will fill this `Struct` as we parse,
+ # yield it if its `ietf` matches our `needle`,
+ # and throw it away otherwise.
+ CYO = Struct.new(:ietf, :globs, :description) do
+ def initialize(ietf = nil, globs = Array.new, description = nil)
+ super(ietf, globs, description)
+ end
+ def to_s # Pretty print
+ "#{self[:description]} (#{self[:ietf]}) [#{
+ self[:globs]&.map(&File.method(:extname)).join(?,)
+ }]"
+ end
+ def inspect; "#"; end
+ end
+
+ # Set up our parsing environment and open a file handle for our XML.
+ def initialize(parent, haystack)
+ @parse_stack = Array::new # Track our current Element as we parse.
+ @parent = parent # `Ractor` that instantiated us.
+ @haystack = File.open(haystack, File::Constants::RDONLY)
+ @haystack.advise(:sequential)
+ end
+
+ # Stratch `Struct`.
+ def cyo
+ @cyo ||= CYO.new
+ end
+
+ # Wax on…
+ def start_element(name)
+ @parse_stack.push(name)
+ case @parse_stack.last
+ when :"mime-type" then @cyo = nil # Clear out leftovers between types.
+ end
+ end
+
+ # …wax off.
+ def end_element(name)
+ case @parse_stack.last
+ when :"mime-type" then
+ # Save the scratch `Struct` if we matched our needle while building it.
+ @out = cyo.dup if @i_can_haz
+ @i_can_haz = false
+ end
+ @parse_stack.pop
+ end
+
+ # Element attribute callback — Ox::Sax::Value version
+ def attr_value(name, value)
+ case [@parse_stack.last, name]
+ in :"mime-type", :type then
+ cyo[:ietf] = value.as_s
+ # If we found our needle then we will yield the scratch `CYO` instead of `nil`.
+ @i_can_haz = true if value.as_s == @needle
+ in :glob, :pattern then
+ cyo[:globs].append(value.as_s)
+ else nil
+ end
+ end
+
+ # Element text content callback, e.g. for TEXT
+ # This part. --------^
+ def text(element_text)
+ case @parse_stack.last
+ when :comment then
+ # This will end up being the `last` locale (probably `zh_TW`)
+ # because I don't want to implement locale checking for a test script lol
+ cyo[:description] = element_text
+ end
+ end
+
+ # Start our search for a given `needle` in our open `haystack`.
+ def awen(needle, **kwargs)
+ @needle = needle # What IETF Media-Type should we find? (e.g. `'image/jpeg'`)
+ @i_can_haz = false # Did we find our `needle`? (obviously not yet)
+ @haystack.rewind # Pon de Replay
+
+ # Do the thing.
+ Ox.sax_parse(
+ self, # Instance of a class that responds to `Ox::Sax`'s callback messages.
+ @haystack, # IO stream or String of XML to parse. Won't close File handles automatically.
+ **{
+ convert_special: true, # [boolean] Convert encoded entities back to their unencoded form, e.g. `"<"` to `"<"`.
+ skip: :skip_off, # [:skip_none|:skip_return|:skip_white|:skip_off] (from Element text/value) Strip CRs, whitespace, or nothing.
+ smart: false, # [boolean] Toggle Ox's built-in hints for HTML parsing: https://github.com/ohler55/ox/blob/master/ext/ox/sax_hint.c
+ strip_namespace: true, # [nil|String|true|false] (from Element names) Strip no namespaces, all namespaces, or a specific namespace.
+ symbolize: true, # [boolean] Fill callback method `name` arguments with Symbols instead of with Strings.
+ intern_string_values: true, # [boolean] Intern (freeze and deduplicate) String return values.
+ }.update(kwargs),
+ )
+
+ # Let our parent `#take` our needle-equivalent `CYO`, or `nil`.
+ Ractor.yield(@out)
+ end # def awen
+
+end # class Saxtor
+
+# Fancy "usage" help `String` fragment to concat with specific error messages.
+usage = <<-PLZ
+
+Usage: `sax_ractor.rb [SHARED-MIME-INFO_XML_PATH] [IETF_MEDIA_TYPES]…`
+
+Common file paths:
+
+- FreeBSD:
+ `${LOCALBASE}/share/mime/packages/freedesktop.org.xml` (probably `/usr/local`)
+ https://www.freshports.org/misc/shared-mime-info/
+
+- Linux:
+ `/usr/share/mime/packages/freedesktop.org.xml`
+
+- macOS:
+ `/opt/homebrew/share/mime/packages/freedesktop.org.xml` (Homebrew)
+ `/opt/local/share/mime/packages/freedesktop.org.xml` (MacPorts)
+ https://formulae.brew.sh/formula/shared-mime-info
+PLZ
+
+# Bail out if we were given a nonexistant file.
+abort("Please provide the path to a `shared-mime-info` XML package \
+and some media-type query arguments (e.g. 'image/jpeg')".concat(usage)) unless ARGV.size > 0
+haystack = Pathname.new(ARGV.first)
+abort("#{haystack} does not exist") unless haystack.exist? and haystack.file?
+
+# *Judicator Aldaris voice* "YOU HAVE NOT ENOUGH ARGUMENTS."
+abort("Please provide some media-type query arguments (e.g. 'image/jpeg')".concat(usage)) unless ARGV.size > 1
+
+# We can use `Ractor::make_shareable()` for larger traversable data structures,
+# but freezing should be enough to share a `Pathname`.
+# Resolve symlinks etc with `#realpath` before we freeze.
+haystack = haystack.realpath.freeze
+needles = ARGV[1...]
+
+
+# Hamburger Style.
+puts "Parallel Ractors"
+# Create one `Ractor` for every given media-type argument
+moo = ['Heifer', 'Cow', 'Bull', 'Steer'].tally
+head_count = needles.size - 1
+herd = (0..head_count).map {
+ # Give our worker `Ractor` a name, otherwise its `#name` will return `nil`.
+ individual = moo.keys.sample
+ moo[individual] += 1
+ Ractor.new(haystack, name: "#{individual} #{moo[individual] - 1}") { |haystack|
+ # Initialize an `Ox::Sax` handler for our given source file.
+ handler = Saxtor::new(Ractor.current, haystack)
+
+ # Now we can `#send` a needle to this `Ractor` and make it search the haystack!
+ while ietf_string = Ractor.receive
+ Ractor.yield(handler.awen(ietf_string))
+ end
+ }
+}
+
+# Send our arguments to our herd in a 1:1 mapping
+(0..head_count).each { herd[_1].send(needles[_1]) }
+
+# Wait for every `Ractor` to have a result, and then pretty print all of them :)
+pp (0..head_count).map {
+ [herd[_1], herd[_1].take]
+}.map {
+ "#{_1.name} gave us #{_2 || 'nothing'}"
+}
+
+
+# Hotdog Style.
+puts
+puts "Serial Ractor"
+# Create a single `Ractor` and send every media-type to it in series.
+only_one_ox = Ractor.new(haystack, name: "ONLY ONE OX") { |haystack|
+ handler = Saxtor::new(Ractor.current, haystack)
+ while ietf_string = Ractor.receive
+ handler.awen(ietf_string)
+ end
+}
+(0..head_count).each { only_one_ox.send(needles[_1]) }
+pp "#{only_one_ox.name} gave us #{(0..head_count).map { only_one_ox.take }}"
diff -Nru ruby-ox-2.11.0/examples/saxy.html ruby-ox-2.14.9/examples/saxy.html
--- ruby-ox-2.11.0/examples/saxy.html 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/examples/saxy.html 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,22 @@
+
+
+
+ Saxy
+
+
+
+
+
+
+
First Line
+
+
One Level Deep
+
+
+
+
+
Deeper
+
+
+
+
diff -Nru ruby-ox-2.11.0/examples/saxy_html.rb ruby-ox-2.14.9/examples/saxy_html.rb
--- ruby-ox-2.11.0/examples/saxy_html.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/examples/saxy_html.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,113 @@
+#!/usr/bin/env ruby
+
+# This example demonstrates the use of the Ox.sax_html parser and the
+# Ox.Builder. The parser is used to parse and HTML file and add a
+# `class="ppp"` to each '
' element start.
+#
+# The approach taken is to build while parsing. An HTML parse is started and a
+# builder call is made on each parser callback. If the element is a 'p' then
+# the class attribute is added. All others remain the same.
+
+# Use the current repo if run from the examples directory.
+ox_dir = File.dirname(File.dirname(File.expand_path(__FILE__)))
+$LOAD_PATH << File.join(ox_dir, 'ext')
+$LOAD_PATH << File.join(ox_dir, 'lib')
+
+require 'ox'
+
+# First create a handler for the SAX callbacks. The class instances include a
+# builder that builds as parsing takes place.
+class Saxy < Ox::Sax
+ VOID_ELEMENTS = [ :area, :base, :br, :col, :embed, :hr, :img, :input, :link, :meta, :param, :source, :track, :wbr ]
+
+ def initialize
+ super
+ # The build is created with an indentation of 2 but that can be changed to
+ # the desired indentation.
+ @builder = Ox::Builder.new(:indent => 2)
+ # element_name and attributes are used for deferred writing of the element
+ # start.
+ @element_name = nil
+ @attrs = {}
+ end
+
+ def to_s
+ @builder.to_s
+ end
+
+ # The builder creates element starts with attributes but the parser uses a
+ # seprate call for attributes and element starts. To deal with the
+ # difference keep track of the start name and attributes as they are
+ # added. When another callback other than attributes is called write any
+ # pending element start.
+ def push_element
+ unless @element_name.nil?
+ # Add the class attribute if the element is a
element.
+ @attrs[:class] = 'ppp' if :p == @element_name
+
+ # Check @void_elements to determine how the element start would be
+ # written. HTML includes void elements that are self closing so those
+ # should be handled correctly.
+ if VOID_ELEMENTS.include?(@element_name)
+ @builder.void_element(@element_name, @attrs)
+ else
+ @builder.element(@element_name, @attrs)
+ end
+ # Reset the element name.
+ @element_name = nil
+ @attrs = {}
+ end
+ end
+
+ def start_element(name)
+ push_element
+ @element_name = name
+ end
+
+ def attr(name, value)
+ @attrs[name] = value
+ end
+
+ def doctype(value)
+ push_element
+ @builder.doctype(value)
+ end
+
+ def comment(value)
+ push_element
+ @builder.comment(value)
+ end
+
+ def text(value)
+ push_element
+ @builder.text(value)
+ end
+
+ def end_element(name)
+ push_element
+ @builder.pop() unless VOID_ELEMENTS.include?(name)
+ end
+
+ # Just in case there is a parse error this will display the error along with
+ # where the error occurred in the XML file.
+ def error(message, line, column)
+ puts "*-*-* error at #{line}:#{column}: #{message}"
+ end
+end
+
+# Load the XML file. The Ox.sax_html also handles IO objects.
+xml = File.read('saxy.html')
+# Create an instance of the handler.
+handler = Saxy.new()
+
+Ox.sax_html(handler, xml)
+
+# For debugging uncomment these lines.
+#puts "******************** original *************************\n#{xml}"
+#puts "******************** modifified ***********************\n#{handler.to_s}"
+
+# For benchmarks these lines should be repeated to parse and to generate a
+# modified XML string.
+#handler = Saxy.new()
+#Ox.sax_html(handler, xml)
+#handler.to_s
diff -Nru ruby-ox-2.11.0/examples/saxy.rb ruby-ox-2.14.9/examples/saxy.rb
--- ruby-ox-2.11.0/examples/saxy.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/examples/saxy.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,52 @@
+#!/usr/bin/env ruby
+
+# This example demonstrates the use of the Ox.sax_parse method. An XML string
+# is parsed and a list of element names if built.
+
+# Use the current repo if run from the examples directory.
+ox_dir = File.dirname(File.dirname(File.expand_path(__FILE__)))
+$LOAD_PATH << File.join(ox_dir, 'ext')
+$LOAD_PATH << File.join(ox_dir, 'lib')
+
+require 'ox'
+
+# First create a handler for the SAX callbacks. A Hash is used to collect the
+# element names. This is a quick way to make sure the collected names are
+# unique. Only the start_element is implemented as that is all that is needed
+# to collect names. There is no need to inherit from Ox::Sax but tht class
+# includes the private version of all the methods that can be made publis.
+class Saxy
+ def initialize
+ #super
+ @names = {}
+ end
+
+ def names
+ @names.keys
+ end
+
+ def start_element(name)
+ @names[name] = nil
+ end
+
+end
+
+# The XML can be a string or a IO instance.
+xml = %{
+
+
+ mutant
+
+ 5
+
+ dog
+
+
+
+}
+# Create an instance of the handler.
+handler = Saxy.new()
+
+Ox.sax_html(handler, xml)
+
+puts "element names: #{handler.names}"
diff -Nru ruby-ox-2.11.0/ext/ox/builder.c ruby-ox-2.14.9/ext/ox/builder.c
--- ruby-ox-2.11.0/ext/ox/builder.c 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/builder.c 2022-02-10 23:24:14.000000000 +0000
@@ -8,6 +8,8 @@
#include
#include
+#include "ruby.h"
+#include "ruby/encoding.h"
#include "ox.h"
#include "buf.h"
#include "err.h"
@@ -17,7 +19,7 @@
typedef struct _element {
char *name;
char buf[64];
- int len;
+ long len;
bool has_child;
bool non_text_child;
} *Element;
@@ -124,7 +126,7 @@
char buf[256];
char *end = buf + sizeof(buf) - 1;
char *bp = buf;
- int i = size;
+ size_t i = size;
int fcnt;
for (; '\0' != *str && 0 < i; i--, str++) {
@@ -183,7 +185,7 @@
static void
append_sym_str(Builder b, VALUE v) {
const char *s;
- int len;
+ long len;
switch (rb_type(v)) {
case T_STRING:
@@ -219,7 +221,9 @@
}
static int
-append_attr(VALUE key, VALUE value, Builder b) {
+append_attr(VALUE key, VALUE value, VALUE bv) {
+ Builder b = (Builder)bv;
+
buf_append(&b->buf, ' ');
b->col++;
b->pos++;
@@ -280,7 +284,7 @@
append_indent(b);
}
buf_append_string(&b->buf, "", 2);
- buf_append_string(&b->buf, e->name, e->len);
+ append_string(b, e->name, e->len, xml_element_chars, false);
buf_append(&b->buf, '>');
b->col += e->len + 3;
b->pos += e->len + 3;
@@ -329,9 +333,7 @@
rstr = rb_str_new(b->buf.head, buf_len(&b->buf));
if ('\0' != *b->encoding) {
-#if HAS_ENCODING_SUPPORT
rb_enc_associate(rstr, rb_enc_find(b->encoding));
-#endif
}
return rstr;
}
@@ -600,7 +602,7 @@
Builder b = (Builder)DATA_PTR(self);
Element e;
const char *name;
- int len;
+ long len;
if (1 > argc) {
rb_raise(ox_arg_error_class, "missing element name");
@@ -662,7 +664,7 @@
builder_void_element(int argc, VALUE *argv, VALUE self) {
Builder b = (Builder)DATA_PTR(self);
const char *name;
- int len;
+ long len;
if (1 > argc) {
rb_raise(ox_arg_error_class, "missing element name");
@@ -892,8 +894,7 @@
*
* Closes the current element.
*/
-static VALUE
-builder_pop(VALUE self) {
+static VALUE builder_pop(VALUE self) {
pop((Builder)DATA_PTR(self));
return Qnil;
@@ -915,7 +916,12 @@
*
* An XML builder.
*/
-void ox_init_builder(VALUE ox) {
+void
+ox_init_builder(VALUE ox) {
+#if 0
+ // Just for rdoc.
+ ox = rb_define_module("Ox");
+#endif
builder_class = rb_define_class_under(ox, "Builder", rb_cObject);
rb_define_module_function(builder_class, "new", builder_new, -1);
rb_define_module_function(builder_class, "file", builder_file, -1);
diff -Nru ruby-ox-2.11.0/ext/ox/cache.c ruby-ox-2.14.9/ext/ox/cache.c
--- ruby-ox-2.11.0/ext/ox/cache.c 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/cache.c 2022-02-10 23:24:14.000000000 +0000
@@ -1,160 +1,338 @@
-/* cache.c
- * Copyright (c) 2011, Peter Ohler
- * All rights reserved.
- */
+// Copyright (c) 2011, 2021 Peter Ohler. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for license details.
+#if HAVE_PTHREAD_MUTEX_INIT
+#include
+#endif
#include
-#include
-#include
-#include
-#include
-#include
-#include
#include "cache.h"
-struct _cache {
- /* The key is a length byte followed by the key as a string. If the key is longer than 254 characters then the
- length is 255. The key can be for a premature value and in that case the length byte is greater than the length
- of the key. */
- char *key;
- VALUE value;
- struct _cache *slots[16];
-};
-
-static void slot_print(Cache cache, unsigned int depth);
-
-static char* form_key(const char *s) {
- size_t len = strlen(s);
- char *d = ALLOC_N(char, len + 2);
-
- *(uint8_t*)d = (255 <= len) ? 255 : len;
- memcpy(d + 1, s, len + 1);
-
- return d;
-}
-
-void
-ox_cache_new(Cache *cache) {
- *cache = ALLOC(struct _cache);
- (*cache)->key = 0;
- (*cache)->value = Qundef;
- memset((*cache)->slots, 0, sizeof((*cache)->slots));
+// The stdlib calloc, realloc, and free are used instead of the Ruby ALLOC,
+// ALLOC_N, REALLOC, and xfree since the later could trigger a GC which will
+// either corrupt memory or if the mark function locks will deadlock.
+
+#define REHASH_LIMIT 4
+#define MIN_SHIFT 8
+#define REUSE_MAX 8192
+
+#if HAVE_PTHREAD_MUTEX_INIT
+#define CACHE_LOCK(c) pthread_mutex_lock(&((c)->mutex))
+#define CACHE_UNLOCK(c) pthread_mutex_unlock(&((c)->mutex))
+#else
+#define CACHE_LOCK(c) rb_mutex_lock((c)->mutex)
+#define CACHE_UNLOCK(c) rb_mutex_unlock((c)->mutex)
+#endif
+
+// almost the Murmur hash algorithm
+#define M 0x5bd1e995
+
+typedef struct _slot {
+ struct _slot *next;
+ VALUE val;
+ uint64_t hash;
+ volatile uint32_t use_cnt;
+ uint8_t klen;
+ char key[CACHE_MAX_KEY];
+} * Slot;
+
+typedef struct _cache {
+ volatile Slot *slots;
+ volatile size_t cnt;
+ VALUE (*form)(const char *str, size_t len);
+ uint64_t size;
+ uint64_t mask;
+ VALUE (*intern)(struct _cache *c, const char *key, size_t len, const char **keyp);
+ volatile Slot reuse;
+ size_t rcnt;
+#if HAVE_PTHREAD_MUTEX_INIT
+ pthread_mutex_t mutex;
+#else
+ VALUE mutex;
+#endif
+ uint8_t xrate;
+ bool mark;
+} * Cache;
+
+static uint64_t hash_calc(const uint8_t *key, size_t len) {
+ const uint8_t *end = key + len;
+ const uint8_t *endless = key + (len & 0xFFFFFFFC);
+ uint64_t h = (uint64_t)len;
+ uint64_t k;
+
+ while (key < endless) {
+ k = (uint64_t)*key++;
+ k |= (uint64_t)*key++ << 8;
+ k |= (uint64_t)*key++ << 16;
+ k |= (uint64_t)*key++ << 24;
+
+ k *= M;
+ k ^= k >> 24;
+ h *= M;
+ h ^= k * M;
+ }
+ if (1 < end - key) {
+ uint16_t k16 = (uint16_t)*key++;
+
+ k16 |= (uint16_t)*key++ << 8;
+ h ^= k16 << 8;
+ }
+ if (key < end) {
+ h ^= *key;
+ }
+ h *= M;
+ h ^= h >> 13;
+ h *= M;
+ h ^= h >> 15;
+
+ return h;
}
-VALUE
-ox_cache_get(Cache cache, const char *key, VALUE **slot, const char **keyp) {
- unsigned char *k = (unsigned char*)key;
- Cache *cp;
-
- for (; '\0' != *k; k++) {
- cp = cache->slots + (unsigned int)(*k >> 4); /* upper 4 bits */
- if (0 == *cp) {
- ox_cache_new(cp);
- }
- cache = *cp;
- cp = cache->slots + (unsigned int)(*k & 0x0F); /* lower 4 bits */
- if (0 == *cp) { /* nothing on this tree so set key and value as a premature key/value pair */
- ox_cache_new(cp);
- cache = *cp;
- cache->key = form_key(key);
- break;
- } else {
- int depth = (int)(k - (unsigned char*)key + 1);
-
- cache = *cp;
-
- if ('\0' == *(k + 1)) { /* exact match */
- if (0 == cache->key) { /* nothing in this spot so take it */
- cache->key = form_key(key);
- break;
- } else if ((depth == *cache->key || 255 < depth) && 0 == strcmp(key, cache->key + 1)) { /* match */
- break;
- } else { /* have to move the current premature key/value deeper */
- unsigned char *ck = (unsigned char*)(cache->key + depth + 1);
- Cache orig = *cp;
-
- cp = (*cp)->slots + (*ck >> 4);
- ox_cache_new(cp);
- cp = (*cp)->slots + (*ck & 0x0F);
- ox_cache_new(cp);
- (*cp)->key = cache->key;
- (*cp)->value = cache->value;
- orig->key = form_key(key);
- orig->value = Qundef;
- }
- } else { /* not exact match but on the path */
- if (0 != cache->key) { /* there is a key/value here already */
- if (depth == *cache->key || (255 <= depth && 0 == strncmp(cache->key, key, depth) && '\0' == cache->key[depth])) { /* key belongs here */
- continue;
- } else {
- unsigned char *ck = (unsigned char*)(cache->key + depth + 1);
- Cache orig = *cp;
-
- cp = (*cp)->slots + (*ck >> 4);
- ox_cache_new(cp);
- cp = (*cp)->slots + (*ck & 0x0F);
- ox_cache_new(cp);
- (*cp)->key = cache->key;
- (*cp)->value = cache->value;
- orig->key = 0;
- orig->value = Qundef;
- }
- }
- }
- }
- }
- *slot = &cache->value;
- if (0 != keyp) {
- if (0 == cache->key) {
- printf("*** Error: failed to set the key for '%s'\n", key);
- *keyp = 0;
- } else {
- *keyp = cache->key + 1;
- }
- }
- return cache->value;
-}
-
-void
-ox_cache_print(Cache cache) {
- /*printf("-------------------------------------------\n");*/
- slot_print(cache, 0);
-}
-
-static void
-slot_print(Cache c, unsigned int depth) {
- char indent[256];
- Cache *cp;
- unsigned int i;
-
- if (sizeof(indent) - 1 < depth) {
- depth = ((int)sizeof(indent) - 1);
- }
- memset(indent, ' ', depth);
- indent[depth] = '\0';
- for (i = 0, cp = c->slots; i < 16; i++, cp++) {
- if (0 == *cp) {
- /*printf("%s%02u:\n", indent, i);*/
+static void rehash(Cache c) {
+ uint64_t osize;
+ Slot *end;
+ Slot *sp;
+
+ osize = c->size;
+ c->size = osize * 4;
+ c->mask = c->size - 1;
+ c->slots = realloc((void *)c->slots, sizeof(Slot) * c->size);
+ memset((Slot *)c->slots + osize, 0, sizeof(Slot) * osize * 3);
+ end = (Slot *)c->slots + osize;
+ for (sp = (Slot *)c->slots; sp < end; sp++) {
+ Slot s = *sp;
+ Slot next = NULL;
+
+ *sp = NULL;
+ for (; NULL != s; s = next) {
+ uint64_t h = s->hash & c->mask;
+ Slot *bucket = (Slot *)c->slots + h;
+
+ next = s->next;
+ s->next = *bucket;
+ *bucket = s;
+ }
+ }
+}
+
+static VALUE ox_lockless_intern(Cache c, const char *key, size_t len, const char **keyp) {
+ uint64_t h = hash_calc((const uint8_t *)key, len);
+ Slot *bucket = (Slot *)c->slots + (h & c->mask);
+ Slot b;
+ volatile VALUE rkey;
+
+ while (REUSE_MAX < c->rcnt) {
+ if (NULL != (b = c->reuse)) {
+ c->reuse = b->next;
+ free(b);
+ c->rcnt--;
} else {
- if (0 == (*cp)->key && Qundef == (*cp)->value) {
- printf("%s%02u:\n", indent, i);
- } else {
- const char *vs;
- const char *clas;
-
- if (Qundef == (*cp)->value) {
- vs = "undefined";
- clas = "";
- } else {
- VALUE rs = rb_funcall2((*cp)->value, rb_intern("to_s"), 0, 0);
+ // An accounting error occured somewhere so correct it.
+ c->rcnt = 0;
+ }
+ }
+ for (b = *bucket; NULL != b; b = b->next) {
+ if ((uint8_t)len == b->klen && 0 == strncmp(b->key, key, len)) {
+ b->use_cnt += 16;
+ if (NULL != keyp) {
+ *keyp = b->key;
+ }
+ return b->val;
+ }
+ }
+ rkey = c->form(key, len);
+ if (NULL == (b = c->reuse)) {
+ b = calloc(1, sizeof(struct _slot));
+ } else {
+ c->reuse = b->next;
+ c->rcnt--;
+ }
+ b->hash = h;
+ memcpy(b->key, key, len);
+ b->klen = (uint8_t)len;
+ b->key[len] = '\0';
+ b->val = rkey;
+ b->use_cnt = 4;
+ b->next = *bucket;
+ *bucket = b;
+ c->cnt++; // Don't worry about wrapping. Worse case is the entry is removed and recreated.
+ if (NULL != keyp) {
+ *keyp = b->key;
+ }
+ if (REHASH_LIMIT < c->cnt / c->size) {
+ rehash(c);
+ }
+ return rkey;
+}
+
+static VALUE ox_locking_intern(Cache c, const char *key, size_t len, const char **keyp) {
+ uint64_t h;
+ Slot *bucket;
+ Slot b;
+ uint64_t old_size;
+ volatile VALUE rkey;
+
+ CACHE_LOCK(c);
+ while (REUSE_MAX < c->rcnt) {
+ if (NULL != (b = c->reuse)) {
+ c->reuse = b->next;
+ free(b);
+ c->rcnt--;
+ } else {
+ // An accounting error occured somewhere so correct it.
+ c->rcnt = 0;
+ }
+ }
+ h = hash_calc((const uint8_t *)key, len);
+ bucket = (Slot *)c->slots + (h & c->mask);
+ for (b = *bucket; NULL != b; b = b->next) {
+ if ((uint8_t)len == b->klen && 0 == strncmp(b->key, key, len)) {
+ b->use_cnt += 4;
+ if (NULL != keyp) {
+ *keyp = b->key;
+ }
+ CACHE_UNLOCK(c);
+
+ return b->val;
+ }
+ }
+ old_size = c->size;
+ // The creation of a new value may trigger a GC which be a problem if the
+ // cache is locked so make sure it is unlocked for the key value creation.
+ if (NULL != (b = c->reuse)) {
+ c->reuse = b->next;
+ c->rcnt--;
+ }
+ CACHE_UNLOCK(c);
+ if (NULL == b) {
+ b = calloc(1, sizeof(struct _slot));
+ }
+ rkey = c->form(key, len);
+ b->hash = h;
+ memcpy(b->key, key, len);
+ b->klen = (uint8_t)len;
+ b->key[len] = '\0';
+ b->val = rkey;
+ b->use_cnt = 16;
+
+ // Lock again to add the new entry.
+ CACHE_LOCK(c);
+ if (old_size != c->size) {
+ h = hash_calc((const uint8_t *)key, len);
+ bucket = (Slot *)c->slots + (h & c->mask);
+ }
+ b->next = *bucket;
+ *bucket = b;
+ c->cnt++; // Don't worry about wrapping. Worse case is the entry is removed and recreated.
+ if (NULL != keyp) {
+ *keyp = b->key;
+ }
+ if (REHASH_LIMIT < c->cnt / c->size) {
+ rehash(c);
+ }
+ CACHE_UNLOCK(c);
- vs = StringValuePtr(rs);
- clas = rb_class2name(rb_obj_class((*cp)->value));
+ return rkey;
+}
+
+Cache ox_cache_create(size_t size, VALUE (*form)(const char *str, size_t len), bool mark, bool locking) {
+ Cache c = calloc(1, sizeof(struct _cache));
+ int shift = 0;
+
+ for (; REHASH_LIMIT < size; size /= 2, shift++) {
+ }
+ if (shift < MIN_SHIFT) {
+ shift = MIN_SHIFT;
+ }
+#if HAVE_PTHREAD_MUTEX_INIT
+ pthread_mutex_init(&c->mutex, NULL);
+#else
+ c->mutex = rb_mutex_new();
+#endif
+ c->size = 1 << shift;
+ c->mask = c->size - 1;
+ c->slots = calloc(c->size, sizeof(Slot));
+ c->form = form;
+ c->xrate = 1; // low
+ c->mark = mark;
+ if (locking) {
+ c->intern = ox_locking_intern;
+ } else {
+ c->intern = ox_lockless_intern;
+ }
+ return c;
+}
+
+void ox_cache_free(Cache c) {
+ uint64_t i;
+
+ for (i = 0; i < c->size; i++) {
+ Slot next;
+ Slot s;
+
+ for (s = c->slots[i]; NULL != s; s = next) {
+ next = s->next;
+ free(s);
+ }
+ }
+ free((void *)c->slots);
+ free(c);
+}
+
+void ox_cache_mark(Cache c) {
+ uint64_t i;
+
+#if !HAVE_PTHREAD_MUTEX_INIT
+ rb_gc_mark(c->mutex);
+#endif
+ if (0 == c->cnt) {
+ return;
+ }
+ for (i = 0; i < c->size; i++) {
+ Slot s;
+ Slot prev = NULL;
+ Slot next;
+
+ for (s = c->slots[i]; NULL != s; s = next) {
+ next = s->next;
+ if (0 == s->use_cnt) {
+ if (NULL == prev) {
+ c->slots[i] = next;
+ } else {
+ prev->next = next;
}
- printf("%s%02u: %s = %s (%s)\n", indent, i, (*cp)->key, vs, clas);
+ c->cnt--;
+ s->next = c->reuse;
+ c->reuse = s;
+ c->rcnt++;
+ continue;
+ }
+ switch (c->xrate) {
+ case 0: break;
+ case 2: s->use_cnt -= 2; break;
+ case 3: s->use_cnt /= 2; break;
+ default: s->use_cnt--; break;
+ }
+ if (c->mark) {
+ rb_gc_mark(s->val);
+ }
+ prev = s;
+ }
+ }
+}
+
+VALUE
+ox_cache_intern(Cache c, const char *key, size_t len, const char **keyp) {
+ if (CACHE_MAX_KEY <= len) {
+ if (NULL != keyp) {
+ volatile VALUE rkey = c->form(key, len);
+
+ if (SYMBOL_P(rkey)) {
+ *keyp = rb_id2name(rb_sym2id(rkey));
}
- slot_print(*cp, depth + 2);
+ return rkey;
}
+ return c->form(key, len);
}
+ return c->intern(c, key, len, keyp);
}
diff -Nru ruby-ox-2.11.0/ext/ox/cache.h ruby-ox-2.14.9/ext/ox/cache.h
--- ruby-ox-2.11.0/ext/ox/cache.h 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/cache.h 2022-02-10 23:24:14.000000000 +0000
@@ -1,19 +1,19 @@
-/* cache.h
- * Copyright (c) 2011, Peter Ohler
- * All rights reserved.
- */
+// Copyright (c) 2021 Peter Ohler. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for license details.
#ifndef OX_CACHE_H
#define OX_CACHE_H
-#include "ruby.h"
+#include
+#include
-typedef struct _cache *Cache;
+#define CACHE_MAX_KEY 35
-extern void ox_cache_new(Cache *cache);
+struct _cache;
-extern VALUE ox_cache_get(Cache cache, const char *key, VALUE **slot, const char **keyp);
-
-extern void ox_cache_print(Cache cache);
+extern struct _cache *ox_cache_create(size_t size, VALUE (*form)(const char *str, size_t len), bool mark, bool locking);
+extern void ox_cache_free(struct _cache *c);
+extern void ox_cache_mark(struct _cache *c);
+extern VALUE ox_cache_intern(struct _cache *c, const char *key, size_t len, const char **keyp);
#endif /* OX_CACHE_H */
diff -Nru ruby-ox-2.11.0/ext/ox/dump.c ruby-ox-2.14.9/ext/ox/dump.c
--- ruby-ox-2.11.0/ext/ox/dump.c 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/dump.c 2022-02-10 23:24:14.000000000 +0000
@@ -54,7 +54,7 @@
static void dump_gen_doc(VALUE obj, int depth, Out out);
static void dump_gen_element(VALUE obj, int depth, Out out);
static void dump_gen_instruct(VALUE obj, int depth, Out out);
-static int dump_gen_attr(VALUE key, VALUE value, Out out);
+static int dump_gen_attr(VALUE key, VALUE value, VALUE ov);
static int dump_gen_nodes(VALUE obj, int depth, Out out);
static void dump_gen_val_node(VALUE obj, int depth,
const char *pre, size_t plen,
@@ -67,12 +67,12 @@
static void dump_value(Out out, const char *value, size_t size);
static void dump_str_value(Out out, const char *value, size_t size, const char *table);
-static int dump_var(ID key, VALUE value, Out out);
+static int dump_var(ID key, VALUE value, VALUE ov);
static void dump_num(Out out, VALUE obj);
static void dump_date(Out out, VALUE obj);
static void dump_time_thin(Out out, VALUE obj);
static void dump_time_xsd(Out out, VALUE obj);
-static int dump_hash(VALUE key, VALUE value, Out out);
+static int dump_hash(VALUE key, VALUE value, VALUE ov);
static int is_xml_friendly(const uchar *str, int len, const char *table);
@@ -301,7 +301,14 @@
fill_attr(out, 'i', s, end - s);
}
if (e->closed) {
- *out->cur++ = '/';
+ if (out->opts->no_empty) {
+ *out->cur++ = '>';
+ *out->cur++ = '<';
+ *out->cur++ = '/';
+ *out->cur++ = e->type;
+ } else {
+ *out->cur++ = '/';
+ }
}
*out->cur++ = '>';
*out->cur = '\0';
@@ -448,17 +455,14 @@
dump_time_thin(Out out, VALUE obj) {
char buf[64];
char *b = buf + sizeof(buf) - 1;
-#if HAS_RB_TIME_TIMESPEC
+#if HAVE_RB_TIME_TIMESPEC
struct timespec ts = rb_time_timespec(obj);
time_t sec = ts.tv_sec;
long nsec = ts.tv_nsec;
#else
time_t sec = NUM2LONG(rb_funcall2(obj, ox_tv_sec_id, 0, 0));
-#if HAS_NANO_TIME
long nsec = NUM2LONG(rb_funcall2(obj, ox_tv_nsec_id, 0, 0));
-#else
- long nsec = NUM2LONG(rb_funcall2(obj, ox_tv_usec_id, 0, 0)) * 1000;
-#endif
+ //long nsec = NUM2LONG(rb_funcall2(obj, ox_tv_usec_id, 0, 0)) * 1000;
#endif
char *dot = b - 10;
long size;
@@ -507,17 +511,14 @@
static void
dump_time_xsd(Out out, VALUE obj) {
struct tm *tm;
-#if HAS_RB_TIME_TIMESPEC
+#if HAVE_RB_TIME_TIMESPEC
struct timespec ts = rb_time_timespec(obj);
time_t sec = ts.tv_sec;
long nsec = ts.tv_nsec;
#else
time_t sec = NUM2LONG(rb_funcall2(obj, ox_tv_sec_id, 0, 0));
-#if HAS_NANO_TIME
long nsec = NUM2LONG(rb_funcall2(obj, ox_tv_nsec_id, 0, 0));
-#else
- long nsec = NUM2LONG(rb_funcall2(obj, ox_tv_usec_id, 0, 0)) * 1000;
-#endif
+ //long nsec = NUM2LONG(rb_funcall2(obj, ox_tv_usec_id, 0, 0)) * 1000;
#endif
int tzhour, tzmin;
char tzsign = '+';
@@ -527,7 +528,7 @@
}
/* 2010-07-09T10:47:45.895826+09:00 */
tm = localtime(&sec);
-#if HAS_TM_GMTOFF
+#if HAVE_ST_TM_GMTOFF
if (0 > tm->tm_gmtoff) {
tzsign = '-';
tzhour = (int)(tm->tm_gmtoff / -3600);
@@ -802,7 +803,7 @@
}
case T_STRUCT:
{
-#if HAS_RSTRUCT
+#ifdef RSTRUCT_GET
VALUE clas;
if (0 != out->circ_cache && check_circular(out, obj, &e)) {
@@ -824,7 +825,7 @@
} else {
char num_buf[16];
int d2 = depth + 1;
-#if UNIFY_FIXNUM_AND_BIGNUM
+#ifdef RUBY_INTEGER_UNIFICATION
long i;
long cnt = NUM2LONG(rb_struct_size(obj));
#else // UNIFY_FIXNUM_AND_INTEGER
@@ -837,7 +838,7 @@
out->w_start(out, &e);
for (i = 0; i < cnt; i++) {
- VALUE v = RSTRUCT_GET(obj, i);
+ VALUE v = RSTRUCT_GET(obj, (int)(i));
dump_obj(rb_intern(ulong2str(i, num_buf + sizeof(num_buf) - 1)), v, d2, out);
}
out->w_end(out, &e);
@@ -870,7 +871,7 @@
dump_gen_element(obj, depth + 1, out);
out->w_end(out, &e);
} else { /* Object */
-#if HAS_IVAR_HELPERS
+#if HAVE_RB_IVAR_FOREACH
e.type = (Qtrue == rb_obj_is_kind_of(obj, rb_eException)) ? ExceptionCode : ObjectCode;
cnt = (int)rb_ivar_count(obj);
e.closed = (0 >= cnt);
@@ -998,7 +999,9 @@
}
static int
-dump_var(ID key, VALUE value, Out out) {
+dump_var(ID key, VALUE value, VALUE ov) {
+ Out out = (Out)ov;
+
if (T_DATA == rb_type(value) && key == ox_mesg_id) {
/* There is a secret recipe that keeps Exception mesg attributes as a
* T_DATA until it is needed. The safe way around this hack is to call
@@ -1015,7 +1018,9 @@
}
static int
-dump_hash(VALUE key, VALUE value, Out out) {
+dump_hash(VALUE key, VALUE value, VALUE ov) {
+ Out out = (Out)ov;
+
dump_obj(0, key, out->depth, out);
dump_obj(0, value, out->depth, out);
@@ -1105,6 +1110,11 @@
*out->cur++ = '<';
*out->cur++ = '/';
fill_value(out, name, nlen);
+ } else if (out->opts->no_empty) {
+ *out->cur++ = '>';
+ *out->cur++ = '<';
+ *out->cur++ = '/';
+ fill_value(out, name, nlen);
} else {
*out->cur++ = '/';
}
@@ -1186,16 +1196,13 @@
}
static int
-dump_gen_attr(VALUE key, VALUE value, Out out) {
+dump_gen_attr(VALUE key, VALUE value, VALUE ov) {
+ Out out = (Out)ov;
+
const char *ks;
size_t klen;
size_t size;
-#if HAS_PRIVATE_ENCODING
- // There seems to be a bug in jruby for converting symbols to strings and preserving the encoding. This is a work
- // around.
- ks = rb_str_ptr(rb_String(key));
-#else
switch (rb_type(key)) {
case T_SYMBOL:
ks = rb_id2name(SYM2ID(key));
@@ -1208,7 +1215,6 @@
ks = StringValuePtr(key);
break;
}
-#endif
klen = strlen(ks);
value = rb_String(value);
size = 4 + klen + RSTRING_LEN(value);
diff -Nru ruby-ox-2.11.0/ext/ox/encode.h ruby-ox-2.14.9/ext/ox/encode.h
--- ruby-ox-2.11.0/ext/ox/encode.h 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/encode.h 1970-01-01 00:00:00.000000000 +0000
@@ -1,26 +0,0 @@
-/* encode.h
- * Copyright (c) 2011, Peter Ohler
- * All rights reserved.
- */
-
-#ifndef __OX_ENCODE_H__
-#define __OX_ENCODE_H__
-
-#include "ruby.h"
-#if HAS_ENCODING_SUPPORT
-#include "ruby/encoding.h"
-#endif
-
-static inline VALUE
-ox_encode(VALUE rstr) {
-#if HAS_ENCODING_SUPPORT
- rb_enc_associate(rstr, ox_utf8_encoding);
-#else
- if (Qnil != ox_utf8_encoding) {
- rstr = rb_funcall(ox_utf8_encoding, ox_iconv_id, 1, rstr);
- }
-#endif
- return rstr;
-}
-
-#endif /* __OX_ENCODE_H__ */
diff -Nru ruby-ox-2.11.0/ext/ox/extconf.rb ruby-ox-2.14.9/ext/ox/extconf.rb
--- ruby-ox-2.11.0/ext/ox/extconf.rb 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/extconf.rb 2022-02-10 23:24:14.000000000 +0000
@@ -6,7 +6,6 @@
parts = RUBY_DESCRIPTION.split(' ')
type = parts[0].downcase()
type = 'ree' if 'ruby' == type && RUBY_DESCRIPTION.include?('Ruby Enterprise Edition')
-is_windows = RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/
platform = RUBY_PLATFORM
version = RUBY_VERSION.split('.')
puts ">>>>> Creating Makefile for #{type} version #{RUBY_VERSION} on #{platform} <<<<<"
@@ -18,41 +17,8 @@
'RUBY_VERSION_MAJOR' => version[0],
'RUBY_VERSION_MINOR' => version[1],
'RUBY_VERSION_MICRO' => version[2],
- 'HAS_RB_TIME_TIMESPEC' => ('ruby' == type && ('1.9.3' == RUBY_VERSION)) ? 1 : 0,
- #'HAS_RB_TIME_TIMESPEC' => ('ruby' == type && ('1.9.3' == RUBY_VERSION || '2' <= version[0])) ? 1 : 0,
- 'HAS_TM_GMTOFF' => ('ruby' == type && (('1' == version[0] && '9' == version[1]) || '2' <= version[0]) &&
- !(platform.include?('cygwin') || platform.include?('solaris') || platform.include?('linux') || RUBY_PLATFORM =~ /(win|w)32$/)) ? 1 : 0,
- 'HAS_ENCODING_SUPPORT' => (('ruby' == type || 'rubinius' == type || 'macruby' == type) &&
- (('1' == version[0] && '9' == version[1]) || '2' <= version[0])) ? 1 : 0,
- 'HAS_ONIG' => (('ruby' == type || 'jruby' == type || 'rubinius' == type) &&
- (('1' == version[0] && '9' == version[1]) || '2' <= version[0])) ? 1 : 0,
- 'HAS_PRIVATE_ENCODING' => ('jruby' == type && '1' == version[0] && '9' == version[1]) ? 1 : 0,
- 'HAS_NANO_TIME' => ('ruby' == type && ('1' == version[0] && '9' == version[1]) || '2' <= version[0]) ? 1 : 0,
- 'HAS_RSTRUCT' => ('ruby' == type || 'ree' == type) ? 1 : 0,
- 'HAS_IVAR_HELPERS' => ('ruby' == type && !is_windows && (('1' == version[0] && '9' == version[1]) || '2' <= version[0])) ? 1 : 0,
- 'HAS_PROC_WITH_BLOCK' => ('ruby' == type && ('1' == version[0] && '9' == version[1]) || '2' <= version[0]) ? 1 : 0,
- 'HAS_GC_GUARD' => ('jruby' != type && 'rubinius' != type) ? 1 : 0,
- 'HAS_BIGDECIMAL' => ('jruby' != type) ? 1 : 0,
- 'HAS_TOP_LEVEL_ST_H' => ('ree' == type || ('ruby' == type && '1' == version[0] && '8' == version[1])) ? 1 : 0,
- 'NEEDS_UIO' => (RUBY_PLATFORM =~ /(win|w)32$/) ? 0 : 1,
- 'HAS_DATA_OBJECT_WRAP' => ('ruby' == type && '2' == version[0] && '3' <= version[1]) ? 1 : 0,
- 'UNIFY_FIXNUM_AND_BIGNUM' => ('ruby' == type && '2' == version[0] && '4' <= version[1]) ? 1 : 0,
}
-if RUBY_PLATFORM =~ /(win|w)32$/ || RUBY_PLATFORM =~ /solaris2\.10/
- dflags['NEEDS_STPCPY'] = nil
-end
-
-if ['i386-darwin10.0.0', 'x86_64-darwin10.8.0'].include? RUBY_PLATFORM
- dflags['NEEDS_STPCPY'] = nil
- dflags['HAS_IVAR_HELPERS'] = 0 if ('ruby' == type && '1.9.1' == RUBY_VERSION)
-elsif 'x86_64-linux' == RUBY_PLATFORM && '1.9.3' == RUBY_VERSION && '2011-10-30' == RUBY_RELEASE_DATE
- begin
- dflags['NEEDS_STPCPY'] = nil if `more /etc/issue`.include?('CentOS release 5.4')
- rescue Exception
- end
-end
-
dflags.each do |k,v|
if v.nil?
$CPPFLAGS += " -D#{k}"
@@ -62,6 +28,25 @@
end
$CPPFLAGS += ' -Wall'
#puts "*** $CPPFLAGS: #{$CPPFLAGS}"
+CONFIG['warnflags'].slice!(/ -Wsuggest-attribute=format/)
+CONFIG['warnflags'].slice!(/ -Wdeclaration-after-statement/)
+CONFIG['warnflags'].slice!(/ -Wmissing-noreturn/)
+
+have_func('rb_time_timespec')
+have_func('rb_struct_alloc_noinit')
+have_func('rb_obj_encoding')
+have_func('rb_ivar_foreach')
+have_func('rb_ext_ractor_safe', 'ruby.h')
+have_func('pthread_mutex_init')
+have_func('rb_enc_interned_str')
+have_func('rb_time_nano_new')
+have_func('index')
+
+have_header('ruby/st.h')
+have_header('sys/uio.h')
+
+have_struct_member('struct tm', 'tm_gmtoff')
+
create_makefile(extension_name)
%x{make clean}
diff -Nru ruby-ox-2.11.0/ext/ox/gen_load.c ruby-ox-2.14.9/ext/ox/gen_load.c
--- ruby-ox-2.11.0/ext/ox/gen_load.c 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/gen_load.c 2022-02-10 23:24:14.000000000 +0000
@@ -10,7 +10,9 @@
#include
#include "ruby.h"
+#include "ruby/encoding.h"
#include "ox.h"
+#include "intern.h"
static void instruct(PInfo pi, const char *target, Attr attrs, const char *content);
static void create_doc(PInfo pi);
@@ -72,7 +74,7 @@
helper_stack_init(&pi->helpers);
doc = rb_obj_alloc(ox_document_clas);
-#if HAS_GC_GUARD
+#ifdef RB_GC_GUARD
RB_GC_GUARD(doc);
#endif
nodes = rb_ary_new();
@@ -100,7 +102,6 @@
sym = rb_funcall(pi->options->attr_key_mod, ox_call_id, 1, rb_str_new2(attrs->name));
rb_hash_aset(ah, sym, rb_str_new2(attrs->value));
} else if (Yes == pi->options->sym_keys) {
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
VALUE rstr = rb_str_new2(attrs->name);
@@ -109,42 +110,19 @@
} else {
sym = ID2SYM(rb_intern(attrs->name));
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- VALUE rstr = rb_str_new2(attrs->name);
-
- rb_funcall(rstr, ox_force_encoding_id, 1, pi->options->rb_enc);
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
- } else {
- sym = ID2SYM(rb_intern(attrs->name));
- }
-#else
sym = ID2SYM(rb_intern(attrs->name));
-#endif
rb_hash_aset(ah, sym, rb_str_new2(attrs->value));
} else {
volatile VALUE rstr = rb_str_new2(attrs->name);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(rstr, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(rstr, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
rb_hash_aset(ah, rstr, rb_str_new2(attrs->value));
}
-#if HAS_ENCODING_SUPPORT
if (0 == strcmp("encoding", attrs->name)) {
pi->options->rb_enc = rb_enc_find(attrs->value);
}
-#elif HAS_PRIVATE_ENCODING
- if (0 == strcmp("encoding", attrs->name)) {
- pi->options->rb_enc = rb_str_new2(attrs->value);
- }
-#endif
}
nodes = rb_ary_new();
rb_ivar_set(doc, ox_attributes_id, ah);
@@ -212,15 +190,9 @@
VALUE n = rb_obj_alloc(ox_doctype_clas);
VALUE s = rb_str_new2(docType);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
rb_ivar_set(n, ox_at_value_id, s);
if (helper_stack_empty(&pi->helpers)) { /* top level object */
create_doc(pi);
@@ -233,15 +205,9 @@
VALUE n = rb_obj_alloc(ox_comment_clas);
VALUE s = rb_str_new2(comment);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
rb_ivar_set(n, ox_at_value_id, s);
if (helper_stack_empty(&pi->helpers)) { /* top level object */
create_doc(pi);
@@ -254,15 +220,9 @@
VALUE n = rb_obj_alloc(ox_cdata_clas);
VALUE s = rb_str_new2(cdata);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
rb_ivar_set(n, ox_at_value_id, s);
if (helper_stack_empty(&pi->helpers)) { /* top level object */
create_doc(pi);
@@ -274,15 +234,9 @@
add_text(PInfo pi, char *text, int closed) {
VALUE s = rb_str_new2(text);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
if (helper_stack_empty(&pi->helpers)) { /* top level object */
create_doc(pi);
}
@@ -297,15 +251,9 @@
if (Qnil != pi->options->element_key_mod) {
s = rb_funcall(pi->options->element_key_mod, ox_call_id, 1, s);
}
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
e = rb_obj_alloc(ox_element_clas);
rb_ivar_set(e, ox_at_value_id, s);
if (0 != attrs->name) {
@@ -317,57 +265,14 @@
if (Qnil != pi->options->attr_key_mod) {
sym = rb_funcall(pi->options->attr_key_mod, ox_call_id, 1, rb_str_new2(attrs->name));
} else if (Yes == pi->options->sym_keys) {
- VALUE *slot;
-
- if (Qundef == (sym = ox_cache_get(ox_symbol_cache, attrs->name, &slot, 0))) {
-#if HAS_ENCODING_SUPPORT
- if (0 != pi->options->rb_enc) {
- VALUE rstr = rb_str_new2(attrs->name);
-
- rb_enc_associate(rstr, pi->options->rb_enc);
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
- } else {
- sym = ID2SYM(rb_intern(attrs->name));
- }
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- VALUE rstr = rb_str_new2(attrs->name);
-
- rb_funcall(rstr, ox_force_encoding_id, 1, pi->options->rb_enc);
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
- } else {
- sym = ID2SYM(rb_intern(attrs->name));
- }
-#else
- sym = ID2SYM(rb_intern(attrs->name));
-#endif
- // Needed for Ruby 2.2 to get around the GC of symbols
- // created with to_sym which is needed for encoded symbols.
- rb_ary_push(ox_sym_bank, sym);
- *slot = sym;
- }
+ sym = ox_sym_intern(attrs->name, strlen(attrs->name), NULL);
} else {
- sym = rb_str_new2(attrs->name);
-#if HAS_ENCODING_SUPPORT
- if (0 != pi->options->rb_enc) {
- rb_enc_associate(sym, pi->options->rb_enc);
- }
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(sym, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
+ sym = ox_str_intern(attrs->name, strlen(attrs->name), NULL);
}
s = rb_str_new2(attrs->value);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
rb_hash_aset(ah, sym, s);
}
rb_ivar_set(e, ox_attributes_id, ah);
@@ -403,21 +308,12 @@
if (0 != content) {
c = rb_str_new2(content);
}
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
if (0 != content) {
rb_enc_associate(c, pi->options->rb_enc);
}
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- if (0 != content) {
- rb_funcall(c, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
- }
-#endif
inst = rb_obj_alloc(ox_instruct_clas);
rb_ivar_set(inst, ox_at_value_id, s);
if (0 != content) {
@@ -427,60 +323,18 @@
for (; 0 != attrs->name; attrs++) {
volatile VALUE sym;
- VALUE *slot;
if (Qnil != pi->options->attr_key_mod) {
sym = rb_funcall(pi->options->attr_key_mod, ox_call_id, 1, rb_str_new2(attrs->name));
} else if (Yes == pi->options->sym_keys) {
- if (Qundef == (sym = ox_cache_get(ox_symbol_cache, attrs->name, &slot, 0))) {
-#if HAS_ENCODING_SUPPORT
- if (0 != pi->options->rb_enc) {
- VALUE rstr = rb_str_new2(attrs->name);
-
- rb_enc_associate(rstr, pi->options->rb_enc);
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
- } else {
- sym = ID2SYM(rb_intern(attrs->name));
- }
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- VALUE rstr = rb_str_new2(attrs->name);
-
- rb_funcall(rstr, ox_force_encoding_id, 1, pi->options->rb_enc);
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
- } else {
- sym = ID2SYM(rb_intern(attrs->name));
- }
-#else
- sym = ID2SYM(rb_intern(attrs->name));
-#endif
- // Needed for Ruby 2.2 to get around the GC of symbols
- // created with to_sym which is needed for encoded symbols.
- rb_ary_push(ox_sym_bank, sym);
- *slot = sym;
- }
+ sym = ox_sym_intern(attrs->name, strlen(attrs->name), NULL);
} else {
- sym = rb_str_new2(attrs->name);
-#if HAS_ENCODING_SUPPORT
- if (0 != pi->options->rb_enc) {
- rb_enc_associate(sym, pi->options->rb_enc);
- }
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(sym, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
+ sym = ox_str_intern(attrs->name, strlen(attrs->name), NULL);
}
s = rb_str_new2(attrs->value);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
rb_hash_aset(ah, sym, s);
}
rb_ivar_set(inst, ox_attributes_id, ah);
diff -Nru ruby-ox-2.11.0/ext/ox/hash_load.c ruby-ox-2.14.9/ext/ox/hash_load.c
--- ruby-ox-2.11.0/ext/ox/hash_load.c 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/hash_load.c 2022-02-10 23:24:14.000000000 +0000
@@ -13,12 +13,14 @@
#include "ruby.h"
#include "ox.h"
+#define MARK_INC 256
+
// The approach taken for the hash and has_no_attrs parsing is to push just
// the key on to the stack and then decide what to do on the way up/out.
static VALUE
create_top(PInfo pi) {
- volatile VALUE top = rb_hash_new();;
+ volatile VALUE top = rb_hash_new();
helper_stack_push(&pi->helpers, 0, top, HashCode);
pi->obj = top;
@@ -27,20 +29,58 @@
}
static void
-add_text(PInfo pi, char *text, int closed) {
+mark_value(PInfo pi, VALUE val) {
+ if (NULL == pi->marked) {
+ pi->marked = ALLOC_N(VALUE, MARK_INC);
+ pi->mark_size = MARK_INC;
+ } else if (pi->mark_size <= pi->mark_cnt) {
+ pi->mark_size += MARK_INC;
+ pi->marked = REALLOC_N(pi->marked, VALUE, pi->mark_size);
+ }
+ pi->marked[pi->mark_cnt] = val;
+ pi->mark_cnt++;
+}
+
+static bool
+marked(PInfo pi, VALUE val) {
+ if (NULL != pi->marked) {
+ VALUE *vp = pi->marked + pi->mark_cnt - 1;
+
+ for (; pi->marked <= vp; vp--) {
+ if (val == *vp) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void
+unmark(PInfo pi, VALUE val) {
+ if (NULL != pi->marked) {
+ VALUE *vp = pi->marked + pi->mark_cnt - 1;
+ int i;
+
+ for (i = 0; pi->marked <= vp; vp--, i++) {
+ if (val == *vp) {
+ for (; 0 < i; i--, vp++) {
+ *vp = *(vp + 1);
+ }
+ pi->mark_cnt--;
+ break;
+ }
+ }
+ }
+}
+
+static void
+add_str(PInfo pi, VALUE s) {
Helper parent = helper_stack_peek(&pi->helpers);
- volatile VALUE s = rb_str_new2(text);
volatile VALUE a;
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(s, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(s, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
switch (parent->type) {
case NoCode:
parent->obj = s;
@@ -60,6 +100,16 @@
}
static void
+add_text(PInfo pi, char *text, int closed) {
+ add_str(pi, rb_str_new2(text));
+}
+
+static void
+add_cdata(PInfo pi, const char *text, size_t len) {
+ add_str(pi, rb_str_new(text, len));
+}
+
+static void
add_element(PInfo pi, const char *ename, Attr attrs, int hasChildren) {
if (helper_stack_empty(&pi->helpers)) {
create_top(pi);
@@ -79,20 +129,14 @@
key = rb_str_new2(attrs->name);
}
val = rb_str_new2(attrs->value);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(val, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(val, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
rb_hash_aset(h, key, val);
}
a = rb_ary_new();
rb_ary_push(a, h);
- rb_obj_taint(a); // flag indicating it is a unit, kind of a hack but it works
+ mark_value(pi, a);
helper_stack_push(&pi->helpers, rb_intern(ename), a, ArrayCode);
} else {
helper_stack_push(&pi->helpers, rb_intern(ename), Qnil, NoCode);
@@ -108,15 +152,14 @@
}
static int
-untaint_hash_cb(VALUE key, VALUE value, VALUE x) {
- if (Qtrue == rb_obj_tainted(value)) {
- rb_obj_untaint(value);
- }
+umark_hash_cb(VALUE key, VALUE value, VALUE x) {
+ unmark((PInfo)x, value);
+
return ST_CONTINUE;
}
static void
-end_element_core(PInfo pi, const char *ename, bool check_taint) {
+end_element_core(PInfo pi, const char *ename, bool check_marked) {
Helper e = helper_stack_pop(&pi->helpers);
Helper parent = helper_stack_peek(&pi->helpers);
volatile VALUE pobj = parent->obj;
@@ -161,8 +204,8 @@
if (Qundef == found) {
rb_hash_aset(pobj, key, e->obj);
} else if (RUBY_T_ARRAY == rb_type(found)) {
- if (check_taint && Qtrue == rb_obj_tainted(found)) {
- rb_obj_untaint(found);
+ if (check_marked && marked(pi, found)) {
+ unmark(pi, found);
a = rb_ary_new();
rb_ary_push(a, found);
rb_ary_push(a, e->obj);
@@ -171,16 +214,16 @@
rb_ary_push(found, e->obj);
}
} else { // something there other than an array
- if (check_taint && Qtrue == rb_obj_tainted(e->obj)) {
- rb_obj_untaint(e->obj);
+ if (check_marked && marked(pi, e->obj)) {
+ unmark(pi, e->obj);
}
a = rb_ary_new();
rb_ary_push(a, found);
rb_ary_push(a, e->obj);
rb_hash_aset(pobj, key, a);
}
- if (check_taint && RUBY_T_HASH == rb_type(e->obj)) {
- rb_hash_foreach(e->obj, untaint_hash_cb, Qnil);
+ if (check_marked && NULL != pi->marked && RUBY_T_HASH == rb_type(e->obj)) {
+ rb_hash_foreach(e->obj, umark_hash_cb, (VALUE)pi);
}
}
@@ -196,9 +239,7 @@
static void
finish(PInfo pi) {
- if (Qnil != pi->obj && RUBY_T_HASH == rb_type(pi->obj)) {
- rb_hash_foreach(pi->obj, untaint_hash_cb, Qnil);
- }
+ xfree(pi->marked);
}
struct _parseCallbacks _ox_hash_callbacks = {
@@ -214,6 +255,19 @@
ParseCallbacks ox_hash_callbacks = &_ox_hash_callbacks;
+struct _parseCallbacks _ox_hash_cdata_callbacks = {
+ NULL,
+ NULL,
+ NULL,
+ add_cdata,
+ add_text,
+ add_element,
+ end_element,
+ finish,
+};
+
+ParseCallbacks ox_hash_cdata_callbacks = &_ox_hash_cdata_callbacks;
+
struct _parseCallbacks _ox_hash_no_attrs_callbacks = {
NULL,
NULL,
@@ -226,3 +280,16 @@
};
ParseCallbacks ox_hash_no_attrs_callbacks = &_ox_hash_no_attrs_callbacks;
+
+struct _parseCallbacks _ox_hash_no_attrs_cdata_callbacks = {
+ NULL,
+ NULL,
+ NULL,
+ add_cdata,
+ add_text,
+ add_element_no_attrs,
+ end_element_no_attrs,
+ NULL,
+};
+
+ParseCallbacks ox_hash_no_attrs_cdata_callbacks = &_ox_hash_no_attrs_cdata_callbacks;
diff -Nru ruby-ox-2.11.0/ext/ox/intern.c ruby-ox-2.14.9/ext/ox/intern.c
--- ruby-ox-2.11.0/ext/ox/intern.c 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/intern.c 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,153 @@
+// Copyright (c) 2011, 2021 Peter Ohler. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for license details.
+
+#include "intern.h"
+
+#include
+
+#include "cache.h"
+#include "ox.h"
+
+// These are statics but in an attempt to stop the cross linking or maybe
+// something in Ruby they all have been given an ox prefix.
+static struct _cache *ox_str_cache = NULL;
+static VALUE ox_str_cache_obj;
+
+static struct _cache *ox_sym_cache = NULL;
+static VALUE ox_sym_cache_obj;
+
+static struct _cache *ox_attr_cache = NULL;
+static VALUE ox_attr_cache_obj;
+
+static struct _cache *ox_id_cache = NULL;
+static VALUE ox_id_cache_obj;
+
+static VALUE form_str(const char *str, size_t len) {
+ return rb_str_freeze(rb_utf8_str_new(str, len));
+}
+
+static VALUE form_sym(const char *str, size_t len) {
+ return rb_to_symbol(rb_str_freeze(rb_utf8_str_new(str, len)));
+}
+
+static VALUE form_attr(const char *str, size_t len) {
+ char buf[256];
+
+ if (sizeof(buf) - 2 <= len) {
+ char *b = ALLOC_N(char, len + 2);
+ ID id;
+
+ if ('~' == *str) {
+ memcpy(b, str + 1, len - 1);
+ b[len - 1] = '\0';
+ len -= 2;
+ } else {
+ *b = '@';
+ memcpy(b + 1, str, len);
+ b[len + 1] = '\0';
+ }
+ id = rb_intern3(buf, len + 1, rb_utf8_encoding());
+ xfree(b);
+ return id;
+ }
+ if ('~' == *str) {
+ memcpy(buf, str + 1, len - 1);
+ buf[len - 1] = '\0';
+ len -= 2;
+ } else {
+ *buf = '@';
+ memcpy(buf + 1, str, len);
+ buf[len + 1] = '\0';
+ }
+ return (VALUE)rb_intern3(buf, len + 1, rb_utf8_encoding());
+}
+
+static VALUE form_id(const char *str, size_t len) {
+ return (VALUE)rb_intern3(str, len, rb_utf8_encoding());
+}
+
+void ox_hash_init() {
+ VALUE cache_class = rb_define_class_under(Ox, "Cache", rb_cObject);
+
+ ox_str_cache = ox_cache_create(0, form_str, true, false);
+ ox_str_cache_obj = Data_Wrap_Struct(cache_class, ox_cache_mark, ox_cache_free, ox_str_cache);
+ rb_gc_register_address(&ox_str_cache_obj);
+
+ ox_sym_cache = ox_cache_create(0, form_sym, true, false);
+ ox_sym_cache_obj = Data_Wrap_Struct(cache_class, ox_cache_mark, ox_cache_free, ox_sym_cache);
+ rb_gc_register_address(&ox_sym_cache_obj);
+
+ ox_attr_cache = ox_cache_create(0, form_attr, false, false);
+ ox_attr_cache_obj = Data_Wrap_Struct(cache_class, ox_cache_mark, ox_cache_free, ox_attr_cache);
+ rb_gc_register_address(&ox_attr_cache_obj);
+
+ ox_id_cache = ox_cache_create(0, form_id, false, false);
+ ox_id_cache_obj = Data_Wrap_Struct(cache_class, ox_cache_mark, ox_cache_free, ox_id_cache);
+ rb_gc_register_address(&ox_id_cache_obj);
+}
+
+VALUE
+ox_str_intern(const char *key, size_t len, const char **keyp) {
+ // For huge cache sizes over half a million the rb_enc_interned_str
+ // performs slightly better but at more "normal" size of a several
+ // thousands the cache intern performs about 20% better.
+#if HAVE_RB_ENC_INTERNED_STR && 0
+ return rb_enc_interned_str(key, len, rb_utf8_encoding());
+#else
+ return ox_cache_intern(ox_str_cache, key, len, keyp);
+#endif
+}
+
+VALUE
+ox_sym_intern(const char *key, size_t len, const char **keyp) {
+ return ox_cache_intern(ox_sym_cache, key, len, keyp);
+}
+
+ID ox_attr_intern(const char *key, size_t len) {
+ return ox_cache_intern(ox_attr_cache, key, len, NULL);
+}
+
+ID ox_id_intern(const char *key, size_t len) {
+ return ox_cache_intern(ox_id_cache, key, len, NULL);
+}
+
+char *ox_strndup(const char *s, size_t len) {
+ char *d = ALLOC_N(char, len + 1);
+
+ memcpy(d, s, len);
+ d[len] = '\0';
+
+ return d;
+}
+
+VALUE
+ox_utf8_name(const char *str, size_t len, rb_encoding *encoding, const char **strp) {
+ return ox_str_intern(str, len, strp);
+}
+
+VALUE
+ox_utf8_sym(const char *str, size_t len, rb_encoding *encoding, const char **strp) {
+ return ox_sym_intern(str, len, strp);
+}
+
+VALUE
+ox_enc_sym(const char *str, size_t len, rb_encoding *encoding, const char **strp) {
+ VALUE sym = rb_str_new2(str);
+
+ rb_enc_associate(sym, encoding);
+ if (NULL != strp) {
+ *strp = StringValuePtr(sym);
+ }
+ return rb_to_symbol(sym);
+}
+
+VALUE
+ox_enc_name(const char *str, size_t len, rb_encoding *encoding, const char **strp) {
+ VALUE sym = rb_str_new2(str);
+
+ rb_enc_associate(sym, encoding);
+ if (NULL != strp) {
+ *strp = StringValuePtr(sym);
+ }
+ return sym;
+}
diff -Nru ruby-ox-2.11.0/ext/ox/intern.h ruby-ox-2.14.9/ext/ox/intern.h
--- ruby-ox-2.11.0/ext/ox/intern.h 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/intern.h 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,25 @@
+// Copyright (c) 2011, 2021 Peter Ohler. All rights reserved.
+// Licensed under the MIT License. See LICENSE file in the project root for license details.
+#ifndef OX_INTERN_H
+#define OX_INTERN_H
+
+#include
+#include
+
+struct _parseInfo;
+
+extern void ox_hash_init();
+
+extern VALUE ox_str_intern(const char *key, size_t len, const char **keyp);
+extern VALUE ox_sym_intern(const char *key, size_t len, const char **keyp);
+extern ID ox_attr_intern(const char *key, size_t len);
+extern ID ox_id_intern(const char *key, size_t len);
+
+extern char *ox_strndup(const char *s, size_t len);
+
+extern VALUE ox_utf8_name(const char *str, size_t len, rb_encoding *encoding, const char **strp);
+extern VALUE ox_utf8_sym(const char *str, size_t len, rb_encoding *encoding, const char **strp);
+extern VALUE ox_enc_sym(const char *str, size_t len, rb_encoding *encoding, const char **strp);
+extern VALUE ox_enc_name(const char *str, size_t len, rb_encoding *encoding, const char **strp);
+
+#endif /* OX_INTERN_H */
diff -Nru ruby-ox-2.11.0/ext/ox/obj_load.c ruby-ox-2.14.9/ext/ox/obj_load.c
--- ruby-ox-2.11.0/ext/ox/obj_load.c 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/obj_load.c 2022-02-10 23:24:14.000000000 +0000
@@ -11,8 +11,11 @@
#include
#include "ruby.h"
+#include "ruby/encoding.h"
+
#include "base64.h"
#include "ox.h"
+#include "intern.h"
static void instruct(PInfo pi, const char *target, Attr attrs, const char *content);
static void add_text(PInfo pi, char *text, int closed);
@@ -39,10 +42,10 @@
struct _parseCallbacks _ox_obj_callbacks = {
- instruct, /* instruct, */
- 0, /* add_doctype, */
- 0, /* add_comment, */
- 0, /* add_cdata, */
+ instruct, // instruct,
+ 0, // add_doctype,
+ 0, // add_comment,
+ 0, // add_cdata,
add_text,
add_element,
end_element,
@@ -53,56 +56,6 @@
extern ParseCallbacks ox_gen_callbacks;
-
-inline static VALUE
-str2sym(const char *str, void *encoding) {
- VALUE sym;
-
-#ifdef HAVE_RUBY_ENCODING_H
- if (0 != encoding) {
- VALUE rstr = rb_str_new2(str);
-
- rb_enc_associate(rstr, (rb_encoding*)encoding);
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
- } else {
- sym = ID2SYM(rb_intern(str));
- }
-#else
- sym = ID2SYM(rb_intern(str));
-#endif
- return sym;
-}
-
-inline static ID
-name2var(const char *name, void *encoding) {
- VALUE *slot;
- ID var_id = 0;
-
- if ('0' <= *name && *name <= '9') {
- var_id = INT2NUM(atoi(name));
- } else if (Qundef == (var_id = ox_cache_get(ox_attr_cache, name, &slot, 0))) {
-#ifdef HAVE_RUBY_ENCODING_H
- if (0 != encoding) {
- volatile VALUE rstr = rb_str_new2(name);
- volatile VALUE sym;
-
- rb_enc_associate(rstr, (rb_encoding*)encoding);
- sym = rb_funcall(rstr, ox_to_sym_id, 0);
- // Needed for Ruby 2.2 to get around the GC of symbols
- // created with to_sym which is needed for encoded symbols.
- rb_ary_push(ox_sym_bank, sym);
- var_id = SYM2ID(sym);
- } else {
- var_id = rb_intern(name);
- }
-#else
- var_id = rb_intern(name);
-#endif
- *slot = var_id;
- }
- return var_id;
-}
-
inline static VALUE
resolve_classname(VALUE mod, const char *class_name, Effort effort, VALUE base_class) {
VALUE clas;
@@ -125,7 +78,7 @@
break;
case StrictEffort:
default:
- /* raise an error if name is not defined */
+ // raise an error if name is not defined
clas = rb_const_get_at(mod, ci);
break;
}
@@ -143,7 +96,6 @@
}
}
-#if HAS_RSTRUCT
inline static VALUE
structname2obj(const char *name) {
VALUE ost;
@@ -159,16 +111,12 @@
}
}
ost = rb_const_get(ox_struct_class, rb_intern(s));
- /* use encoding as the indicator for Ruby 1.8.7 or 1.9.x */
-#if HAS_ENCODING_SUPPORT
- return rb_struct_alloc_noinit(ost);
-#elif HAS_PRIVATE_ENCODING
+#if HAVE_RB_STRUCT_ALLOC_NOINIT
return rb_struct_alloc_noinit(ost);
#else
return rb_struct_new(ost);
#endif
}
-#endif
inline static VALUE
parse_ulong(const char *s, PInfo pi) {
@@ -185,7 +133,7 @@
return ULONG2NUM(n);
}
-/* 2010-07-09T10:47:45.895826162+09:00 */
+// 2010-07-09T10:47:45.895826162+09:00
inline static VALUE
parse_time(const char *text, VALUE clas) {
VALUE t;
@@ -205,7 +153,7 @@
VALUE *slot;
VALUE clas;
- if (Qundef == (clas = ox_cache_get(ox_class_cache, name, &slot, 0))) {
+ if (Qundef == (clas = slot_cache_get(ox_class_cache, name, &slot, 0))) {
char class_name[1024];
char *s;
const char *n = name;
@@ -230,6 +178,7 @@
*s = '\0';
if (Qundef != (clas = resolve_classname(clas, class_name, pi->options->effort, base_class))) {
*slot = clas;
+ rb_gc_register_address(slot);
}
}
return clas;
@@ -239,7 +188,12 @@
get_var_sym_from_attrs(Attr a, void *encoding) {
for (; 0 != a->name; a++) {
if ('a' == *a->name && '\0' == *(a->name + 1)) {
- return name2var(a->value, encoding);
+ const char *val = a->value;
+
+ if ('0' <= *val && *val <= '9') {
+ return INT2NUM(atoi(val));
+ }
+ return ox_id_intern(val, strlen(val));
}
}
return 0;
@@ -255,7 +209,6 @@
return Qundef;
}
-#if HAS_RSTRUCT
static VALUE
get_struct_from_attrs(Attr a) {
for (; 0 != a->name; a++) {
@@ -265,7 +218,6 @@
}
return Qundef;
}
-#endif
static VALUE
get_class_from_attrs(Attr a, PInfo pi, VALUE base_class) {
@@ -363,7 +315,7 @@
int options = 0;
te = text + strlen(text) - 1;
-#if HAS_ONIG
+#ifdef ONIG_OPTION_IGNORECASE
for (; text < te && '/' != *te; te--) {
switch (*te) {
case 'i': options |= ONIG_OPTION_IGNORECASE; break;
@@ -379,19 +331,11 @@
static void
instruct(PInfo pi, const char *target, Attr attrs, const char *content) {
if (0 == strcmp("xml", target)) {
-#if HAS_ENCODING_SUPPORT
for (; 0 != attrs->name; attrs++) {
if (0 == strcmp("encoding", attrs->name)) {
pi->options->rb_enc = rb_enc_find(attrs->value);
}
}
-#elif HAS_PRIVATE_ENCODING
- for (; 0 != attrs->name; attrs++) {
- if (0 == strcmp("encoding", attrs->name)) {
- pi->options->rb_enc = rb_str_new2(attrs->value);
- }
- }
-#endif
}
}
@@ -417,15 +361,9 @@
case NoCode:
case StringCode:
h->obj = rb_str_new2(text);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(h->obj, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (Qnil != pi->options->rb_enc) {
- rb_funcall(h->obj, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
if (0 != pi->circ_array) {
circ_array_set(pi->circ_array, h->obj, (unsigned long)pi->id);
}
@@ -459,20 +397,8 @@
h->obj = rb_float_new(strtod(text, 0));
break;
case SymbolCode:
- {
- VALUE sym;
- VALUE *slot;
-
- if (Qundef == (sym = ox_cache_get(ox_symbol_cache, text, &slot, 0))) {
- sym = str2sym(text, (void*)pi->options->rb_enc);
- // Needed for Ruby 2.2 to get around the GC of symbols created with
- // to_sym which is needed for encoded symbols.
- rb_ary_push(ox_sym_bank, sym);
- *slot = sym;
- }
- h->obj = sym;
+ h->obj = ox_sym_intern(text, strlen(text), NULL);
break;
- }
case DateCode:
{
VALUE args[1];
@@ -494,15 +420,9 @@
from_base64(text, (uchar*)str);
v = rb_str_new(str, str_size);
-#if HAS_ENCODING_SUPPORT
if (0 != pi->options->rb_enc) {
rb_enc_associate(v, pi->options->rb_enc);
}
-#elif HAS_PRIVATE_ENCODING
- if (0 != pi->options->rb_enc) {
- rb_funcall(v, ox_force_encoding_id, 1, pi->options->rb_enc);
- }
-#endif
if (0 != pi->circ_array) {
circ_array_set(pi->circ_array, v, (unsigned long)h->obj);
}
@@ -511,20 +431,11 @@
}
case Symbol64Code:
{
- VALUE sym;
- VALUE *slot;
unsigned long str_size = b64_orig_size(text);
char *str = ALLOCA_N(char, str_size + 1);
from_base64(text, (uchar*)str);
- if (Qundef == (sym = ox_cache_get(ox_symbol_cache, str, &slot, 0))) {
- sym = str2sym(str, (void*)pi->options->rb_enc);
- // Needed for Ruby 2.2 to get around the GC of symbols created with
- // to_sym which is needed for encoded symbols.
- rb_ary_push(ox_sym_bank, sym);
- *slot = sym;
- }
- h->obj = sym;
+ h->obj = ox_sym_intern(str, strlen(str), NULL);
break;
}
case RegexpCode:
@@ -542,11 +453,7 @@
h->obj = rb_cstr_to_inum(text, 10, 1);
break;
case BigDecimalCode:
-#if HAS_BIGDECIMAL
h->obj = rb_funcall(rb_cObject, ox_bigdecimal_id, 1, rb_str_new2(text));
-#else
- h->obj = Qnil;
-#endif
break;
default:
h->obj = Qnil;
@@ -580,7 +487,7 @@
printf("%s%s\n", indent, buf);
}
}
- if (helper_stack_empty(&pi->helpers)) { /* top level object */
+ if (helper_stack_empty(&pi->helpers)) { // top level object
if (0 != (id = get_id_from_attrs(pi, attrs))) {
pi->circ_array = circ_array_new();
}
@@ -601,7 +508,7 @@
h->obj = Qfalse;
break;
case StringCode:
- /* h->obj will be replaced by add_text if it is called */
+ // h->obj will be replaced by add_text if it is called
h->obj = ox_empty_string;
if (0 != pi->circ_array) {
pi->id = get_id_from_attrs(pi, attrs);
@@ -618,8 +525,8 @@
case ComplexCode:
case DateCode:
case TimeCode:
- case RationalCode: /* sub elements read next */
- /* value will be read in the following add_text */
+ case RationalCode: // sub elements read next
+ // value will be read in the following add_text
h->obj = Qundef;
break;
case String64Code:
@@ -641,7 +548,7 @@
}
break;
case RangeCode:
- h->obj = rb_range_new(ox_zero_fixnum, ox_zero_fixnum, Qfalse);
+ h->obj = rb_ary_new_from_args(3, Qnil, Qnil, Qfalse);
break;
case RawCode:
if (hasChildren) {
@@ -670,15 +577,10 @@
}
break;
case StructCode:
-#if HAS_RSTRUCT
h->obj = get_struct_from_attrs(attrs);
if (0 != pi->circ_array) {
circ_array_set(pi->circ_array, h->obj, get_id_from_attrs(pi, attrs));
}
-#else
- set_error(&pi->err, "Ruby structs not supported with this verion of Ruby", pi->str, pi->s);
- return;
-#endif
break;
case ClassCode:
if (Qundef == (h->obj = get_class_from_attrs(attrs, pi, ox_bag_clas))) {
@@ -726,12 +628,15 @@
Helper ph = helper_stack_peek(&pi->helpers);
if (ox_empty_string == h->obj) {
- /* special catch for empty strings */
+ // special catch for empty strings
h->obj = rb_str_new2("");
- }
- if (Qundef == h->obj) {
+ } else if (Qundef == h->obj) {
set_error(&pi->err, "Invalid element for object mode", pi->str, pi->s);
return;
+ } else if (RangeCode == h->type) { // Expect an array of 3 elements.
+ const VALUE *ap = RARRAY_PTR(h->obj);
+
+ h->obj = rb_range_new(*ap, *(ap + 1), Qtrue == *(ap + 2));
}
pi->obj = h->obj;
if (0 != ph) {
@@ -742,45 +647,39 @@
case ExceptionCode:
case ObjectCode:
if (Qnil != ph->obj) {
- if (0 == h->var) {
+ if (0 == h->var || NULL == rb_id2name(h->var )) {
set_error(&pi->err, "Invalid element for object mode", pi->str, pi->s);
return;
}
+ if (RUBY_T_OBJECT != rb_type(ph->obj)) {
+ set_error(&pi->err, "Corrupt object encoding", pi->str, pi->s);
+ return;
+ }
rb_ivar_set(ph->obj, h->var, h->obj);
}
break;
case StructCode:
-#if HAS_RSTRUCT
if (0 == h->var) {
set_error(&pi->err, "Invalid element for object mode", pi->str, pi->s);
return;
}
rb_struct_aset(ph->obj, h->var, h->obj);
-#else
- set_error(&pi->err, "Ruby structs not supported with this verion of Ruby", pi->str, pi->s);
- return;
-#endif
break;
case HashCode:
// put back h
helper_stack_push(&pi->helpers, h->var, h->obj, KeyCode);
break;
case RangeCode:
-#if HAS_RSTRUCT
if (ox_beg_id == h->var) {
- RSTRUCT_SET(ph->obj, 0, h->obj);
+ rb_ary_store(ph->obj, 0, h->obj);
} else if (ox_end_id == h->var) {
- RSTRUCT_SET(ph->obj, 1, h->obj);
+ rb_ary_store(ph->obj, 1, h->obj);
} else if (ox_excl_id == h->var) {
- RSTRUCT_SET(ph->obj, 2, h->obj);
+ rb_ary_store(ph->obj, 2, h->obj);
} else {
set_error(&pi->err, "Invalid range attribute", pi->str, pi->s);
return;
}
-#else
- set_error(&pi->err, "Ruby structs not supported with this verion of Ruby", pi->str, pi->s);
- return;
-#endif
break;
case KeyCode:
{
@@ -872,7 +771,7 @@
for (; text - dot <= 9; text++) {
v2 *= 10;
}
-#if HAS_NANO_TIME
+#if HAVE_RB_TIME_NANO_NEW
return rb_time_nano_new(v, v2);
#else
return rb_time_new(v, v2 / 1000);
@@ -928,14 +827,14 @@
tm.tm_hour = (int)cargs[3];
tm.tm_min = (int)cargs[4];
tm.tm_sec = (int)cargs[5];
-#if HAS_NANO_TIME
+#if HAVE_RB_TIME_NANO_NEW
return rb_time_nano_new(mktime(&tm), cargs[6]);
#else
return rb_time_new(mktime(&tm), cargs[6] / 1000);
#endif
}
-/* debug functions */
+// debug functions
static void
fill_indent(PInfo pi, char *buf, size_t size) {
size_t cnt;
diff -Nru ruby-ox-2.11.0/ext/ox/ox.c ruby-ox-2.14.9/ext/ox/ox.c
--- ruby-ox-2.11.0/ext/ox/ox.c 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/ox.c 2022-02-10 23:24:14.000000000 +0000
@@ -3,214 +3,209 @@
* All rights reserved.
*/
-#include
+#include "ox.h"
+
#include
-#include
#include
+#include
#include
+#include
#include
+#include "intern.h"
#include "ruby.h"
-#include "ox.h"
#include "sax.h"
/* maximum to allocate on the stack, arbitrary limit */
-#define SMALL_XML 4096
-#define WITH_CACHE_TESTS 0
+#define SMALL_XML 4096
+#define WITH_CACHE_TESTS 0
typedef struct _yesNoOpt {
- VALUE sym;
- char *attr;
-} *YesNoOpt;
+ VALUE sym;
+ char *attr;
+} * YesNoOpt;
void Init_ox();
-VALUE Ox = Qnil;
+VALUE Ox = Qnil;
-ID ox_abort_id;
-ID ox_at_column_id;
-ID ox_at_content_id;
-ID ox_at_id;
-ID ox_at_line_id;
-ID ox_at_pos_id;
-ID ox_at_value_id;
-ID ox_attr_id;
-ID ox_attr_value_id;
-ID ox_attributes_id;
-ID ox_attrs_done_id;
-ID ox_beg_id;
-ID ox_bigdecimal_id;
-ID ox_call_id;
-ID ox_cdata_id;
-ID ox_comment_id;
-ID ox_den_id;
-ID ox_doctype_id;
-ID ox_end_element_id;
-ID ox_end_id;
-ID ox_end_instruct_id;
-ID ox_error_id;
-ID ox_excl_id;
-ID ox_external_encoding_id;
-ID ox_fileno_id;
-ID ox_force_encoding_id;
-ID ox_inspect_id;
-ID ox_instruct_id;
-ID ox_jd_id;
-ID ox_keys_id;
-ID ox_local_id;
-ID ox_mesg_id;
-ID ox_message_id;
-ID ox_new_id;
-ID ox_nodes_id;
-ID ox_num_id;
-ID ox_parse_id;
-ID ox_pos_id;
-ID ox_read_id;
-ID ox_readpartial_id;
-ID ox_start_element_id;
-ID ox_string_id;
-ID ox_text_id;
-ID ox_to_c_id;
-ID ox_to_s_id;
-ID ox_to_sym_id;
-ID ox_tv_nsec_id;
-ID ox_tv_sec_id;
-ID ox_tv_usec_id;
-ID ox_value_id;
-
-VALUE ox_encoding_sym;
-VALUE ox_version_sym;
-VALUE ox_standalone_sym;
-VALUE ox_indent_sym;
-VALUE ox_size_sym;
-
-VALUE ox_empty_string;
-VALUE ox_zero_fixnum;
-VALUE ox_sym_bank; // Array
-
-VALUE ox_arg_error_class;
-VALUE ox_bag_clas;
-VALUE ox_bigdecimal_class;
-VALUE ox_cdata_clas;
-VALUE ox_comment_clas;
-VALUE ox_raw_clas;
-VALUE ox_date_class;
-VALUE ox_doctype_clas;
-VALUE ox_document_clas;
-VALUE ox_element_clas;
-VALUE ox_instruct_clas;
-VALUE ox_parse_error_class;
-VALUE ox_stringio_class;
-VALUE ox_struct_class;
-VALUE ox_syntax_error_class;
-VALUE ox_time_class;
-
-Cache ox_symbol_cache = 0;
-Cache ox_class_cache = 0;
-Cache ox_attr_cache = 0;
-
-static VALUE abort_sym;
-static VALUE active_sym;
-static VALUE attr_key_mod_sym;
-static VALUE auto_define_sym;
-static VALUE auto_sym;
-static VALUE block_sym;
-static VALUE circular_sym;
-static VALUE convert_special_sym;
-static VALUE effort_sym;
-static VALUE generic_sym;
-static VALUE hash_no_attrs_sym;
-static VALUE hash_sym;
-static VALUE inactive_sym;
-static VALUE invalid_replace_sym;
-static VALUE limited_sym;
-static VALUE margin_sym;
-static VALUE mode_sym;
-static VALUE nest_ok_sym;
-static VALUE object_sym;
-static VALUE off_sym;
-static VALUE opt_format_sym;
-static VALUE optimized_sym;
-static VALUE overlay_sym;
-static VALUE skip_none_sym;
-static VALUE skip_off_sym;
-static VALUE skip_return_sym;
-static VALUE skip_sym;
-static VALUE skip_white_sym;
-static VALUE smart_sym;
-static VALUE strict_sym;
-static VALUE strip_namespace_sym;
-static VALUE symbolize_keys_sym;
-static VALUE symbolize_sym;
-static VALUE tolerant_sym;
-static VALUE trace_sym;
-static VALUE with_dtd_sym;
-static VALUE with_instruct_sym;
-static VALUE with_xml_sym;
-static VALUE xsd_date_sym;
-static VALUE element_key_mod_sym;
-
-static ID encoding_id;
-static ID has_key_id;
-
-#if HAS_ENCODING_SUPPORT
-rb_encoding *ox_utf8_encoding = 0;
-#elif HAS_PRIVATE_ENCODING
-VALUE ox_utf8_encoding = Qnil;
-#else
-void *ox_utf8_encoding = 0;
-#endif
-
-struct _options ox_default_options = {
- { '\0' }, // encoding
- { '\0' }, // margin
- 2, // indent
- 0, // trace
- 0, // margin_len
- No, // with_dtd
- No, // with_xml
- No, // with_instruct
- No, // circular
- No, // xsd_date
- NoMode, // mode
- StrictEffort, // effort
- Yes, // sym_keys
- SpcSkip, // skip
- No, // smart
- 1, // convert_special
- No, // allow_invalid
- { '\0' }, // inv_repl
- { '\0' }, // strip_ns
- NULL, // html_hints
- Qnil, // attr_key_mod;
- Qnil, // element_key_mod;
-#if HAS_PRIVATE_ENCODING
- Qnil // rb_enc
-#else
- 0 // rb_enc
-#endif
+ID ox_abort_id;
+ID ox_at_column_id;
+ID ox_at_content_id;
+ID ox_at_id;
+ID ox_at_line_id;
+ID ox_at_pos_id;
+ID ox_at_value_id;
+ID ox_attr_id;
+ID ox_attr_value_id;
+ID ox_attributes_id;
+ID ox_attrs_done_id;
+ID ox_beg_id;
+ID ox_bigdecimal_id;
+ID ox_call_id;
+ID ox_cdata_id;
+ID ox_comment_id;
+ID ox_den_id;
+ID ox_doctype_id;
+ID ox_end_element_id;
+ID ox_end_id;
+ID ox_end_instruct_id;
+ID ox_error_id;
+ID ox_excl_id;
+ID ox_external_encoding_id;
+ID ox_fileno_id;
+ID ox_force_encoding_id;
+ID ox_inspect_id;
+ID ox_instruct_id;
+ID ox_jd_id;
+ID ox_keys_id;
+ID ox_local_id;
+ID ox_mesg_id;
+ID ox_message_id;
+ID ox_new_id;
+ID ox_nodes_id;
+ID ox_num_id;
+ID ox_parse_id;
+ID ox_pos_id;
+ID ox_read_id;
+ID ox_readpartial_id;
+ID ox_start_element_id;
+ID ox_string_id;
+ID ox_text_id;
+ID ox_to_c_id;
+ID ox_to_s_id;
+ID ox_to_sym_id;
+ID ox_tv_nsec_id;
+ID ox_tv_sec_id;
+ID ox_tv_usec_id;
+ID ox_value_id;
+
+VALUE ox_encoding_sym;
+VALUE ox_version_sym;
+VALUE ox_standalone_sym;
+VALUE ox_indent_sym;
+VALUE ox_size_sym;
+
+VALUE ox_empty_string;
+VALUE ox_zero_fixnum;
+VALUE ox_sym_bank; // Array
+
+VALUE ox_arg_error_class;
+VALUE ox_bag_clas;
+VALUE ox_bigdecimal_class;
+VALUE ox_cdata_clas;
+VALUE ox_comment_clas;
+VALUE ox_raw_clas;
+VALUE ox_date_class;
+VALUE ox_doctype_clas;
+VALUE ox_document_clas;
+VALUE ox_element_clas;
+VALUE ox_instruct_clas;
+VALUE ox_parse_error_class;
+VALUE ox_stringio_class;
+VALUE ox_struct_class;
+VALUE ox_syntax_error_class;
+VALUE ox_time_class;
+
+SlotCache ox_class_cache = 0;
+
+static VALUE abort_sym;
+static VALUE active_sym;
+static VALUE attr_key_mod_sym;
+static VALUE auto_define_sym;
+static VALUE auto_sym;
+static VALUE block_sym;
+static VALUE circular_sym;
+static VALUE convert_special_sym;
+static VALUE effort_sym;
+static VALUE generic_sym;
+static VALUE hash_no_attrs_sym;
+static VALUE hash_sym;
+static VALUE inactive_sym;
+static VALUE invalid_replace_sym;
+static VALUE limited_sym;
+static VALUE margin_sym;
+static VALUE mode_sym;
+static VALUE nest_ok_sym;
+static VALUE no_empty_sym;
+static VALUE object_sym;
+static VALUE off_sym;
+static VALUE opt_format_sym;
+static VALUE optimized_sym;
+static VALUE overlay_sym;
+static VALUE skip_none_sym;
+static VALUE skip_off_sym;
+static VALUE skip_return_sym;
+static VALUE skip_sym;
+static VALUE skip_white_sym;
+static VALUE smart_sym;
+static VALUE strict_sym;
+static VALUE strip_namespace_sym;
+static VALUE symbolize_keys_sym;
+static VALUE symbolize_sym;
+static VALUE tolerant_sym;
+static VALUE trace_sym;
+static VALUE with_cdata_sym;
+static VALUE with_dtd_sym;
+static VALUE with_instruct_sym;
+static VALUE with_xml_sym;
+static VALUE xsd_date_sym;
+static VALUE element_key_mod_sym;
+
+static ID encoding_id;
+static ID has_key_id;
+
+rb_encoding *ox_utf8_encoding = 0;
+
+struct _options ox_default_options = {
+ {'\0'}, // encoding
+ {'\0'}, // margin
+ 2, // indent
+ 0, // trace
+ 0, // margin_len
+ No, // with_dtd
+ No, // with_xml
+ No, // with_instruct
+ No, // circular
+ No, // xsd_date
+ NoMode, // mode
+ StrictEffort, // effort
+ Yes, // sym_keys
+ SpcSkip, // skip
+ No, // smart
+ true, // convert_special
+ No, // allow_invalid
+ false, // no_empty
+ false, // with_cdata
+ {'\0'}, // inv_repl
+ {'\0'}, // strip_ns
+ NULL, // html_hints
+ Qnil, // attr_key_mod;
+ Qnil, // element_key_mod;
+ 0 // rb_enc
};
-extern ParseCallbacks ox_obj_callbacks;
-extern ParseCallbacks ox_gen_callbacks;
-extern ParseCallbacks ox_limited_callbacks;
-extern ParseCallbacks ox_nomode_callbacks;
-extern ParseCallbacks ox_hash_callbacks;
-extern ParseCallbacks ox_hash_no_attrs_callbacks;
+extern ParseCallbacks ox_obj_callbacks;
+extern ParseCallbacks ox_gen_callbacks;
+extern ParseCallbacks ox_limited_callbacks;
+extern ParseCallbacks ox_nomode_callbacks;
+extern ParseCallbacks ox_hash_callbacks;
+extern ParseCallbacks ox_hash_cdata_callbacks;
+extern ParseCallbacks ox_hash_no_attrs_callbacks;
+extern ParseCallbacks ox_hash_no_attrs_cdata_callbacks;
-static void parse_dump_options(VALUE ropts, Options copts);
+static void parse_dump_options(VALUE ropts, Options copts);
-static char*
-defuse_bom(char *xml, Options options) {
+static char *defuse_bom(char *xml, Options options) {
switch ((uint8_t)*xml) {
- case 0xEF: // UTF-8
- if (0xBB == (uint8_t)xml[1] && 0xBF == (uint8_t)xml[2]) {
- options->rb_enc = ox_utf8_encoding;
- xml += 3;
- } else {
- rb_raise(ox_parse_error_class, "Invalid BOM in XML string.\n");
- }
- break;
+ case 0xEF: // UTF-8
+ if (0xBB == (uint8_t)xml[1] && 0xBF == (uint8_t)xml[2]) {
+ options->rb_enc = ox_utf8_encoding;
+ xml += 3;
+ } else {
+ rb_raise(ox_parse_error_class, "Invalid BOM in XML string.\n");
+ }
+ break;
#if 0
case 0xFE: // UTF-16BE
if (0xFF == (uint8_t)xml[1]) {
@@ -243,31 +238,30 @@
break;
#endif
default:
- // Let it fail if there is a BOM that is not UTF-8. Other BOM options
- // are not ASCII compatible.
- break;
+ // Let it fail if there is a BOM that is not UTF-8. Other BOM options
+ // are not ASCII compatible.
+ break;
}
return xml;
}
-static VALUE
-hints_to_overlay(Hints hints) {
- volatile VALUE overlay = rb_hash_new();
- Hint h;
- int i;
- VALUE ov;
+static VALUE hints_to_overlay(Hints hints) {
+ volatile VALUE overlay = rb_hash_new();
+ Hint h;
+ int i;
+ VALUE ov;
for (i = hints->size, h = hints->hints; 0 < i; i--, h++) {
- switch (h->overlay) {
- case InactiveOverlay: ov = inactive_sym; break;
- case BlockOverlay: ov = block_sym; break;
- case OffOverlay: ov = off_sym; break;
- case AbortOverlay: ov = abort_sym; break;
- case NestOverlay: ov = nest_ok_sym; break;
- case ActiveOverlay:
- default: ov = active_sym; break;
- }
- rb_hash_aset(overlay, rb_str_new2(h->name), ov);
+ switch (h->overlay) {
+ case InactiveOverlay: ov = inactive_sym; break;
+ case BlockOverlay: ov = block_sym; break;
+ case OffOverlay: ov = off_sym; break;
+ case AbortOverlay: ov = abort_sym; break;
+ case NestOverlay: ov = nest_ok_sym; break;
+ case ActiveOverlay:
+ default: ov = active_sym; break;
+ }
+ rb_hash_aset(overlay, rb_str_new2(h->name), ov);
}
return overlay;
}
@@ -292,8 +286,12 @@
* - _:skip_ [:skip_none|:skip_return|:skip_white|:skip_off] determines how to handle white space in text
* - _:smart_ [true|false|nil] flag indicating the SAX parser uses hints if available (use with html)
* - _:convert_special_ [true|false|nil] flag indicating special characters like < are converted with the SAX parser
- * - _:invalid_replace_ [nil|String] replacement string for invalid XML characters on dump. nil indicates include anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
- * - _:strip_namespace_ [String|true|false] false or "" results in no namespace stripping. A string of "*" or true will strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
+ * - _:invalid_replace_ [nil|String] replacement string for invalid XML characters on dump. nil indicates include anyway
+ * as hex. A string, limited to 10 characters will replace the invalid character with the replace.
+ * - _:no_empty_ [true|false|nil] flag indicating there should be no empty elements in a dump
+ * - _:with_cdata_ [true|false] includes cdata in hash_load results
+ * - _:strip_namespace_ [String|true|false] false or "" results in no namespace stripping. A string of "*" or true will
+ * strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
* - _:overlay_ [Hash] a Hash of keys that match html element names and values that are one of
* - _:active_ - make the normal callback for the element
* - _:nest_ok_ - active but the nesting check is ignored
@@ -307,88 +305,107 @@
* Note that an indent of less than zero will result in a tight one line output
* unless the text in the XML fields contain new line characters.
*/
-static VALUE
-get_def_opts(VALUE self) {
- VALUE opts = rb_hash_new();
- int elen = (int)strlen(ox_default_options.encoding);
+static VALUE get_def_opts(VALUE self) {
+ VALUE opts = rb_hash_new();
+ int elen = (int)strlen(ox_default_options.encoding);
rb_hash_aset(opts, ox_encoding_sym, (0 == elen) ? Qnil : rb_str_new(ox_default_options.encoding, elen));
rb_hash_aset(opts, margin_sym, rb_str_new(ox_default_options.margin, ox_default_options.margin_len));
rb_hash_aset(opts, ox_indent_sym, INT2FIX(ox_default_options.indent));
rb_hash_aset(opts, trace_sym, INT2FIX(ox_default_options.trace));
- rb_hash_aset(opts, with_dtd_sym, (Yes == ox_default_options.with_dtd) ? Qtrue : ((No == ox_default_options.with_dtd) ? Qfalse : Qnil));
- rb_hash_aset(opts, with_xml_sym, (Yes == ox_default_options.with_xml) ? Qtrue : ((No == ox_default_options.with_xml) ? Qfalse : Qnil));
- rb_hash_aset(opts, with_instruct_sym, (Yes == ox_default_options.with_instruct) ? Qtrue : ((No == ox_default_options.with_instruct) ? Qfalse : Qnil));
- rb_hash_aset(opts, circular_sym, (Yes == ox_default_options.circular) ? Qtrue : ((No == ox_default_options.circular) ? Qfalse : Qnil));
- rb_hash_aset(opts, xsd_date_sym, (Yes == ox_default_options.xsd_date) ? Qtrue : ((No == ox_default_options.xsd_date) ? Qfalse : Qnil));
- rb_hash_aset(opts, symbolize_keys_sym, (Yes == ox_default_options.sym_keys) ? Qtrue : ((No == ox_default_options.sym_keys) ? Qfalse : Qnil));
+ rb_hash_aset(opts,
+ with_dtd_sym,
+ (Yes == ox_default_options.with_dtd) ? Qtrue : ((No == ox_default_options.with_dtd) ? Qfalse : Qnil));
+ rb_hash_aset(opts,
+ with_xml_sym,
+ (Yes == ox_default_options.with_xml) ? Qtrue : ((No == ox_default_options.with_xml) ? Qfalse : Qnil));
+ rb_hash_aset(
+ opts,
+ with_instruct_sym,
+ (Yes == ox_default_options.with_instruct) ? Qtrue : ((No == ox_default_options.with_instruct) ? Qfalse : Qnil));
+ rb_hash_aset(opts,
+ circular_sym,
+ (Yes == ox_default_options.circular) ? Qtrue : ((No == ox_default_options.circular) ? Qfalse : Qnil));
+ rb_hash_aset(opts,
+ xsd_date_sym,
+ (Yes == ox_default_options.xsd_date) ? Qtrue : ((No == ox_default_options.xsd_date) ? Qfalse : Qnil));
+ rb_hash_aset(opts,
+ symbolize_keys_sym,
+ (Yes == ox_default_options.sym_keys) ? Qtrue : ((No == ox_default_options.sym_keys) ? Qfalse : Qnil));
rb_hash_aset(opts, attr_key_mod_sym, ox_default_options.attr_key_mod);
rb_hash_aset(opts, element_key_mod_sym, ox_default_options.element_key_mod);
- rb_hash_aset(opts, smart_sym, (Yes == ox_default_options.smart) ? Qtrue : ((No == ox_default_options.smart) ? Qfalse : Qnil));
+ rb_hash_aset(opts,
+ smart_sym,
+ (Yes == ox_default_options.smart) ? Qtrue : ((No == ox_default_options.smart) ? Qfalse : Qnil));
rb_hash_aset(opts, convert_special_sym, (ox_default_options.convert_special) ? Qtrue : Qfalse);
+ rb_hash_aset(opts, no_empty_sym, (ox_default_options.no_empty) ? Qtrue : Qfalse);
+ rb_hash_aset(opts, with_cdata_sym, (ox_default_options.with_cdata) ? Qtrue : Qfalse);
switch (ox_default_options.mode) {
- case ObjMode: rb_hash_aset(opts, mode_sym, object_sym); break;
- case GenMode: rb_hash_aset(opts, mode_sym, generic_sym); break;
- case LimMode: rb_hash_aset(opts, mode_sym, limited_sym); break;
- case HashMode: rb_hash_aset(opts, mode_sym, hash_sym); break;
- case HashNoAttrMode: rb_hash_aset(opts, mode_sym, hash_no_attrs_sym); break;
+ case ObjMode: rb_hash_aset(opts, mode_sym, object_sym); break;
+ case GenMode: rb_hash_aset(opts, mode_sym, generic_sym); break;
+ case LimMode: rb_hash_aset(opts, mode_sym, limited_sym); break;
+ case HashMode: rb_hash_aset(opts, mode_sym, hash_sym); break;
+ case HashNoAttrMode: rb_hash_aset(opts, mode_sym, hash_no_attrs_sym); break;
case NoMode:
- default: rb_hash_aset(opts, mode_sym, Qnil); break;
+ default: rb_hash_aset(opts, mode_sym, Qnil); break;
}
switch (ox_default_options.effort) {
- case StrictEffort: rb_hash_aset(opts, effort_sym, strict_sym); break;
- case TolerantEffort: rb_hash_aset(opts, effort_sym, tolerant_sym); break;
- case AutoEffort: rb_hash_aset(opts, effort_sym, auto_define_sym); break;
+ case StrictEffort: rb_hash_aset(opts, effort_sym, strict_sym); break;
+ case TolerantEffort: rb_hash_aset(opts, effort_sym, tolerant_sym); break;
+ case AutoEffort: rb_hash_aset(opts, effort_sym, auto_define_sym); break;
case NoEffort:
- default: rb_hash_aset(opts, effort_sym, Qnil); break;
+ default: rb_hash_aset(opts, effort_sym, Qnil); break;
}
switch (ox_default_options.skip) {
- case OffSkip: rb_hash_aset(opts, skip_sym, skip_off_sym); break;
- case NoSkip: rb_hash_aset(opts, skip_sym, skip_none_sym); break;
- case CrSkip: rb_hash_aset(opts, skip_sym, skip_return_sym); break;
- case SpcSkip: rb_hash_aset(opts, skip_sym, skip_white_sym); break;
- default: rb_hash_aset(opts, skip_sym, Qnil); break;
+ case OffSkip: rb_hash_aset(opts, skip_sym, skip_off_sym); break;
+ case NoSkip: rb_hash_aset(opts, skip_sym, skip_none_sym); break;
+ case CrSkip: rb_hash_aset(opts, skip_sym, skip_return_sym); break;
+ case SpcSkip: rb_hash_aset(opts, skip_sym, skip_white_sym); break;
+ default: rb_hash_aset(opts, skip_sym, Qnil); break;
}
if (Yes == ox_default_options.allow_invalid) {
- rb_hash_aset(opts, invalid_replace_sym, Qnil);
+ rb_hash_aset(opts, invalid_replace_sym, Qnil);
} else {
- rb_hash_aset(opts, invalid_replace_sym, rb_str_new(ox_default_options.inv_repl + 1, (int)*ox_default_options.inv_repl));
+ rb_hash_aset(opts,
+ invalid_replace_sym,
+ rb_str_new(ox_default_options.inv_repl + 1, (int)*ox_default_options.inv_repl));
}
if ('\0' == *ox_default_options.strip_ns) {
- rb_hash_aset(opts, strip_namespace_sym, Qfalse);
+ rb_hash_aset(opts, strip_namespace_sym, Qfalse);
} else if ('*' == *ox_default_options.strip_ns && '\0' == ox_default_options.strip_ns[1]) {
- rb_hash_aset(opts, strip_namespace_sym, Qtrue);
+ rb_hash_aset(opts, strip_namespace_sym, Qtrue);
} else {
- rb_hash_aset(opts, strip_namespace_sym, rb_str_new(ox_default_options.strip_ns, strlen(ox_default_options.strip_ns)));
+ rb_hash_aset(opts,
+ strip_namespace_sym,
+ rb_str_new(ox_default_options.strip_ns, strlen(ox_default_options.strip_ns)));
}
if (NULL == ox_default_options.html_hints) {
- //rb_hash_aset(opts, overlay_sym, hints_to_overlay(ox_hints_html()));
- rb_hash_aset(opts, overlay_sym, Qnil);
+ // rb_hash_aset(opts, overlay_sym, hints_to_overlay(ox_hints_html()));
+ rb_hash_aset(opts, overlay_sym, Qnil);
} else {
- rb_hash_aset(opts, overlay_sym, hints_to_overlay(ox_default_options.html_hints));
+ rb_hash_aset(opts, overlay_sym, hints_to_overlay(ox_default_options.html_hints));
}
return opts;
}
-static int
-set_overlay(VALUE key, VALUE value, VALUE ctx) {
- Hints hints = (Hints)ctx;
- Hint hint;
+static int set_overlay(VALUE key, VALUE value, VALUE ctx) {
+ Hints hints = (Hints)ctx;
+ Hint hint;
if (NULL != (hint = ox_hint_find(hints, StringValuePtr(key)))) {
- if (active_sym == value) {
- hint->overlay = ActiveOverlay;
- } else if (inactive_sym == value) {
- hint->overlay = InactiveOverlay;
- } else if (block_sym == value) {
- hint->overlay = BlockOverlay;
- } else if (nest_ok_sym == value) {
- hint->overlay = NestOverlay;
- } else if (off_sym == value) {
- hint->overlay = OffOverlay;
- } else if (abort_sym == value) {
- hint->overlay = AbortOverlay;
- }
+ if (active_sym == value) {
+ hint->overlay = ActiveOverlay;
+ } else if (inactive_sym == value) {
+ hint->overlay = InactiveOverlay;
+ } else if (block_sym == value) {
+ hint->overlay = BlockOverlay;
+ } else if (nest_ok_sym == value) {
+ hint->overlay = NestOverlay;
+ } else if (off_sym == value) {
+ hint->overlay = OffOverlay;
+ } else if (abort_sym == value) {
+ hint->overlay = AbortOverlay;
+ }
}
return ST_CONTINUE;
}
@@ -406,8 +423,7 @@
*
* *return* [Hash] default SAX HTML settings
*/
-static VALUE
-sax_html_overlay(VALUE self) {
+static VALUE sax_html_overlay(VALUE self) {
return hints_to_overlay(ox_hints_html());
}
@@ -431,8 +447,11 @@
* - _:attr_key_mod_ [Proc|nil] converts attribute keys on parse if not nil
* - _:skip_ [:skip_none|:skip_return|:skip_white|:skip_off] determines how to handle white space in text
* - _:smart_ [true|false|nil] flag indicating the SAX parser uses hints if available (use with html)
- * - _:invalid_replace_ [nil|String] replacement string for invalid XML characters on dump. nil indicates include anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
- * - _:strip_namespace_ [nil|String|true|false] "" or false result in no namespace stripping. A string of "*" or true will strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
+ * - _:invalid_replace_ [nil|String] replacement string for invalid XML characters on dump. nil indicates include
+ * anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
+ * - _:strip_namespace_ [nil|String|true|false] "" or false result in no namespace stripping. A string of "*" or true
+ * will strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
+ * - _:with_cdata_ [true|false] includes cdata in hash_load results
* - _:overlay_ [Hash] a Hash of keys that match html element names and values that are one of
* - _:active_ - make the normal callback for the element
* - _:nest_ok_ - active but ignore nest check
@@ -443,189 +462,199 @@
*
* *return* [nil]
*/
-static VALUE
-set_def_opts(VALUE self, VALUE opts) {
- struct _yesNoOpt ynos[] = {
- { with_xml_sym, &ox_default_options.with_xml },
- { with_dtd_sym, &ox_default_options.with_dtd },
- { with_instruct_sym, &ox_default_options.with_instruct },
- { xsd_date_sym, &ox_default_options.xsd_date },
- { circular_sym, &ox_default_options.circular },
- { symbolize_keys_sym, &ox_default_options.sym_keys },
- { smart_sym, &ox_default_options.smart },
- { Qnil, 0 }
- };
- YesNoOpt o;
- VALUE v;
+static VALUE set_def_opts(VALUE self, VALUE opts) {
+ struct _yesNoOpt ynos[] = {{with_xml_sym, &ox_default_options.with_xml},
+ {with_dtd_sym, &ox_default_options.with_dtd},
+ {with_instruct_sym, &ox_default_options.with_instruct},
+ {xsd_date_sym, &ox_default_options.xsd_date},
+ {circular_sym, &ox_default_options.circular},
+ {symbolize_keys_sym, &ox_default_options.sym_keys},
+ {smart_sym, &ox_default_options.smart},
+ {Qnil, 0}};
+ YesNoOpt o;
+ VALUE v;
Check_Type(opts, T_HASH);
v = rb_hash_aref(opts, ox_encoding_sym);
if (Qnil == v) {
- *ox_default_options.encoding = '\0';
+ *ox_default_options.encoding = '\0';
} else {
- Check_Type(v, T_STRING);
- strncpy(ox_default_options.encoding, StringValuePtr(v), sizeof(ox_default_options.encoding) - 1);
-#if HAS_ENCODING_SUPPORT
- ox_default_options.rb_enc = rb_enc_find(ox_default_options.encoding);
-#elif HAS_PRIVATE_ENCODING
- ox_default_options.rb_enc = rb_str_new2(ox_default_options.encoding);
- rb_gc_register_address(&ox_default_options.rb_enc);
-#endif
+ Check_Type(v, T_STRING);
+ strncpy(ox_default_options.encoding, StringValuePtr(v), sizeof(ox_default_options.encoding) - 1);
+ ox_default_options.rb_enc = rb_enc_find(ox_default_options.encoding);
}
v = rb_hash_aref(opts, ox_indent_sym);
if (Qnil != v) {
- Check_Type(v, T_FIXNUM);
- ox_default_options.indent = FIX2INT(v);
+ Check_Type(v, T_FIXNUM);
+ ox_default_options.indent = FIX2INT(v);
}
v = rb_hash_aref(opts, trace_sym);
if (Qnil != v) {
- Check_Type(v, T_FIXNUM);
- ox_default_options.trace = FIX2INT(v);
+ Check_Type(v, T_FIXNUM);
+ ox_default_options.trace = FIX2INT(v);
}
v = rb_hash_aref(opts, mode_sym);
if (Qnil == v) {
- ox_default_options.mode = NoMode;
+ ox_default_options.mode = NoMode;
} else if (object_sym == v) {
- ox_default_options.mode = ObjMode;
+ ox_default_options.mode = ObjMode;
} else if (generic_sym == v) {
- ox_default_options.mode = GenMode;
+ ox_default_options.mode = GenMode;
} else if (limited_sym == v) {
- ox_default_options.mode = LimMode;
+ ox_default_options.mode = LimMode;
} else if (hash_sym == v) {
- ox_default_options.mode = HashMode;
+ ox_default_options.mode = HashMode;
} else if (hash_no_attrs_sym == v) {
- ox_default_options.mode = HashNoAttrMode;
+ ox_default_options.mode = HashNoAttrMode;
} else {
- rb_raise(ox_parse_error_class, ":mode must be :object, :generic, :limited, :hash, :hash_no_attrs, or nil.\n");
+ rb_raise(ox_parse_error_class, ":mode must be :object, :generic, :limited, :hash, :hash_no_attrs, or nil.\n");
}
v = rb_hash_aref(opts, effort_sym);
if (Qnil == v) {
- ox_default_options.effort = NoEffort;
+ ox_default_options.effort = NoEffort;
} else if (strict_sym == v) {
- ox_default_options.effort = StrictEffort;
+ ox_default_options.effort = StrictEffort;
} else if (tolerant_sym == v) {
- ox_default_options.effort = TolerantEffort;
+ ox_default_options.effort = TolerantEffort;
} else if (auto_define_sym == v) {
- ox_default_options.effort = AutoEffort;
+ ox_default_options.effort = AutoEffort;
} else {
- rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, :auto_define, or nil.\n");
+ rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, :auto_define, or nil.\n");
}
v = rb_hash_aref(opts, skip_sym);
if (Qnil == v) {
- ox_default_options.skip = NoSkip;
+ ox_default_options.skip = NoSkip;
} else if (skip_off_sym == v) {
- ox_default_options.skip = OffSkip;
+ ox_default_options.skip = OffSkip;
} else if (skip_none_sym == v) {
- ox_default_options.skip = NoSkip;
+ ox_default_options.skip = NoSkip;
} else if (skip_return_sym == v) {
- ox_default_options.skip = CrSkip;
+ ox_default_options.skip = CrSkip;
} else if (skip_white_sym == v) {
- ox_default_options.skip = SpcSkip;
+ ox_default_options.skip = SpcSkip;
} else {
- rb_raise(ox_parse_error_class, ":skip must be :skip_none, :skip_return, :skip_white, :skip_off, or nil.\n");
+ rb_raise(ox_parse_error_class, ":skip must be :skip_none, :skip_return, :skip_white, :skip_off, or nil.\n");
}
v = rb_hash_lookup(opts, convert_special_sym);
if (Qnil == v) {
- // no change
+ // no change
+ } else if (Qtrue == v) {
+ ox_default_options.convert_special = 1;
+ } else if (Qfalse == v) {
+ ox_default_options.convert_special = 0;
+ } else {
+ rb_raise(ox_parse_error_class, ":convert_special must be true or false.\n");
+ }
+
+ v = rb_hash_lookup(opts, no_empty_sym);
+ if (Qnil == v) {
+ // no change
} else if (Qtrue == v) {
- ox_default_options.convert_special = 1;
+ ox_default_options.no_empty = 1;
} else if (Qfalse == v) {
- ox_default_options.convert_special = 0;
+ ox_default_options.no_empty = 0;
} else {
- rb_raise(ox_parse_error_class, ":convert_special must be true or false.\n");
+ rb_raise(ox_parse_error_class, ":no_empty must be true or false.\n");
}
v = rb_hash_aref(opts, invalid_replace_sym);
if (Qnil == v) {
- ox_default_options.allow_invalid = Yes;
+ ox_default_options.allow_invalid = Yes;
} else {
- long slen;
+ long slen;
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(ox_default_options.inv_repl) - 2 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":invalid_replace can be no longer than %d characters.",
- (int)sizeof(ox_default_options.inv_repl) - 2);
- }
- strncpy(ox_default_options.inv_repl + 1, StringValuePtr(v), sizeof(ox_default_options.inv_repl) - 1);
- ox_default_options.inv_repl[sizeof(ox_default_options.inv_repl) - 1] = '\0';
- *ox_default_options.inv_repl = (char)slen;
- ox_default_options.allow_invalid = No;
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(ox_default_options.inv_repl) - 2 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":invalid_replace can be no longer than %d characters.",
+ (int)sizeof(ox_default_options.inv_repl) - 2);
+ }
+ strncpy(ox_default_options.inv_repl + 1, StringValuePtr(v), sizeof(ox_default_options.inv_repl) - 1);
+ ox_default_options.inv_repl[sizeof(ox_default_options.inv_repl) - 1] = '\0';
+ *ox_default_options.inv_repl = (char)slen;
+ ox_default_options.allow_invalid = No;
}
v = rb_hash_aref(opts, strip_namespace_sym);
if (Qfalse == v) {
- *ox_default_options.strip_ns = '\0';
+ *ox_default_options.strip_ns = '\0';
} else if (Qtrue == v) {
- *ox_default_options.strip_ns = '*';
- ox_default_options.strip_ns[1] = '\0';
+ *ox_default_options.strip_ns = '*';
+ ox_default_options.strip_ns[1] = '\0';
} else if (Qnil != v) {
- long slen;
+ long slen;
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(ox_default_options.strip_ns) - 1 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":strip_namespace can be no longer than %d characters.",
- (int)sizeof(ox_default_options.strip_ns) - 1);
- }
- strncpy(ox_default_options.strip_ns, StringValuePtr(v), sizeof(ox_default_options.strip_ns) - 1);
- ox_default_options.strip_ns[sizeof(ox_default_options.strip_ns) - 1] = '\0';
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(ox_default_options.strip_ns) - 1 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":strip_namespace can be no longer than %d characters.",
+ (int)sizeof(ox_default_options.strip_ns) - 1);
+ }
+ strncpy(ox_default_options.strip_ns, StringValuePtr(v), sizeof(ox_default_options.strip_ns) - 1);
+ ox_default_options.strip_ns[sizeof(ox_default_options.strip_ns) - 1] = '\0';
}
v = rb_hash_aref(opts, margin_sym);
if (Qnil != v) {
- long slen;
+ long slen;
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(ox_default_options.margin) - 1 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":margin can be no longer than %d characters.",
- (int)sizeof(ox_default_options.margin) - 1);
- }
- strncpy(ox_default_options.margin, StringValuePtr(v), sizeof(ox_default_options.margin) - 1);
- ox_default_options.margin[sizeof(ox_default_options.margin) - 1] = '\0';
- ox_default_options.margin_len = strlen(ox_default_options.margin);
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(ox_default_options.margin) - 1 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":margin can be no longer than %d characters.",
+ (int)sizeof(ox_default_options.margin) - 1);
+ }
+ strncpy(ox_default_options.margin, StringValuePtr(v), sizeof(ox_default_options.margin) - 1);
+ ox_default_options.margin[sizeof(ox_default_options.margin) - 1] = '\0';
+ ox_default_options.margin_len = strlen(ox_default_options.margin);
}
for (o = ynos; 0 != o->attr; o++) {
- v = rb_hash_lookup(opts, o->sym);
- if (Qnil == v) {
- *o->attr = NotSet;
- } else if (Qtrue == v) {
- *o->attr = Yes;
- } else if (Qfalse == v) {
- *o->attr = No;
- } else {
- rb_raise(ox_parse_error_class, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
- }
+ v = rb_hash_lookup(opts, o->sym);
+ if (Qnil == v) {
+ *o->attr = NotSet;
+ } else if (Qtrue == v) {
+ *o->attr = Yes;
+ } else if (Qfalse == v) {
+ *o->attr = No;
+ } else {
+ rb_raise(ox_parse_error_class, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
+ }
}
v = rb_hash_aref(opts, overlay_sym);
if (Qnil == v) {
- ox_hints_destroy(ox_default_options.html_hints);
- ox_default_options.html_hints = NULL;
+ ox_hints_destroy(ox_default_options.html_hints);
+ ox_default_options.html_hints = NULL;
} else {
- int cnt;
+ int cnt;
- Check_Type(v, T_HASH);
- cnt = (int)RHASH_SIZE(v);
- if (0 == cnt) {
- ox_hints_destroy(ox_default_options.html_hints);
- ox_default_options.html_hints = NULL;
- } else {
- ox_hints_destroy(ox_default_options.html_hints);
- ox_default_options.html_hints = ox_hints_dup(ox_hints_html());
- rb_hash_foreach(v, set_overlay, (VALUE)ox_default_options.html_hints);
- }
+ Check_Type(v, T_HASH);
+ cnt = (int)RHASH_SIZE(v);
+ if (0 == cnt) {
+ ox_hints_destroy(ox_default_options.html_hints);
+ ox_default_options.html_hints = NULL;
+ } else {
+ ox_hints_destroy(ox_default_options.html_hints);
+ ox_default_options.html_hints = ox_hints_dup(ox_hints_html());
+ rb_hash_foreach(v, set_overlay, (VALUE)ox_default_options.html_hints);
+ }
+ }
+ if (Qnil != (v = rb_hash_lookup(opts, with_cdata_sym))) {
+ ox_default_options.with_cdata = (Qtrue == v);
}
+
ox_default_options.element_key_mod = rb_hash_lookup2(opts, element_key_mod_sym, ox_default_options.element_key_mod);
- ox_default_options.attr_key_mod = rb_hash_lookup2(opts, attr_key_mod_sym, ox_default_options.attr_key_mod);
+ ox_default_options.attr_key_mod = rb_hash_lookup2(opts, attr_key_mod_sym, ox_default_options.attr_key_mod);
return Qnil;
}
@@ -640,38 +669,37 @@
* - +xml+ [String] XML String in optimized Object format.
* *return* [Object] deserialized Object.
*/
-static VALUE
-to_obj(VALUE self, VALUE ruby_xml) {
- char *xml, *x;
- size_t len;
- VALUE obj;
- struct _options options = ox_default_options;
- struct _err err;
+static VALUE to_obj(VALUE self, VALUE ruby_xml) {
+ char *xml, *x;
+ size_t len;
+ VALUE obj;
+ struct _options options = ox_default_options;
+ struct _err err;
err_init(&err);
Check_Type(ruby_xml, T_STRING);
/* the xml string gets modified so make a copy of it */
len = RSTRING_LEN(ruby_xml) + 1;
- x = defuse_bom(StringValuePtr(ruby_xml), &options);
+ x = defuse_bom(StringValuePtr(ruby_xml), &options);
if (SMALL_XML < len) {
- xml = ALLOC_N(char, len);
+ xml = ALLOC_N(char, len);
} else {
- xml = ALLOCA_N(char, len);
+ xml = ALLOCA_N(char, len);
}
memcpy(xml, x, len);
-#if HAS_GC_GUARD
+#ifdef RB_GC_GUARD
rb_gc_disable();
#endif
obj = ox_parse(xml, len - 1, ox_obj_callbacks, 0, &options, &err);
if (SMALL_XML < len) {
- xfree(xml);
+ xfree(xml);
}
-#if HAS_GC_GUARD
+#ifdef RB_GC_GUARD
RB_GC_GUARD(obj);
rb_gc_enable();
#endif
if (err_has(&err)) {
- ox_err_raise(&err);
+ ox_err_raise(&err);
}
return obj;
}
@@ -684,204 +712,198 @@
*
* _raise_ [Exception] if the XML is malformed.
*/
-static VALUE
-to_gen(VALUE self, VALUE ruby_xml) {
- char *xml, *x;
- size_t len;
- VALUE obj;
- struct _options options = ox_default_options;
- struct _err err;
+static VALUE to_gen(VALUE self, VALUE ruby_xml) {
+ char *xml, *x;
+ size_t len;
+ VALUE obj;
+ struct _options options = ox_default_options;
+ struct _err err;
err_init(&err);
Check_Type(ruby_xml, T_STRING);
/* the xml string gets modified so make a copy of it */
len = RSTRING_LEN(ruby_xml) + 1;
- x = defuse_bom(StringValuePtr(ruby_xml), &options);
+ x = defuse_bom(StringValuePtr(ruby_xml), &options);
if (SMALL_XML < len) {
- xml = ALLOC_N(char, len);
+ xml = ALLOC_N(char, len);
} else {
- xml = ALLOCA_N(char, len);
+ xml = ALLOCA_N(char, len);
}
memcpy(xml, x, len);
obj = ox_parse(xml, len - 1, ox_gen_callbacks, 0, &options, &err);
if (SMALL_XML < len) {
- xfree(xml);
+ xfree(xml);
}
if (err_has(&err)) {
- ox_err_raise(&err);
+ ox_err_raise(&err);
}
return obj;
}
-static VALUE
-load(char *xml, size_t len, int argc, VALUE *argv, VALUE self, VALUE encoding, Err err) {
- VALUE obj;
- struct _options options = ox_default_options;
+static VALUE load(char *xml, size_t len, int argc, VALUE *argv, VALUE self, VALUE encoding, Err err) {
+ VALUE obj;
+ struct _options options = ox_default_options;
if (1 == argc && rb_cHash == rb_obj_class(*argv)) {
- VALUE h = *argv;
- VALUE v;
-
- if (Qnil != (v = rb_hash_lookup(h, mode_sym))) {
- if (object_sym == v) {
- options.mode = ObjMode;
- } else if (optimized_sym == v) {
- options.mode = ObjMode;
- } else if (generic_sym == v) {
- options.mode = GenMode;
- } else if (limited_sym == v) {
- options.mode = LimMode;
- } else if (hash_sym == v) {
- options.mode = HashMode;
- } else if (hash_no_attrs_sym == v) {
- options.mode = HashNoAttrMode;
- } else {
- rb_raise(ox_parse_error_class, ":mode must be :generic, :object, :limited, :hash, :hash_no_attrs.\n");
- }
- }
- if (Qnil != (v = rb_hash_lookup(h, effort_sym))) {
- if (auto_define_sym == v) {
- options.effort = AutoEffort;
- } else if (tolerant_sym == v) {
- options.effort = TolerantEffort;
- } else if (strict_sym == v) {
- options.effort = StrictEffort;
- } else {
- rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, or :auto_define.\n");
- }
- }
- if (Qnil != (v = rb_hash_lookup(h, skip_sym))) {
- if (skip_none_sym == v) {
- options.skip = NoSkip;
- } else if (skip_off_sym == v) {
- options.skip = OffSkip;
- } else if (skip_return_sym == v) {
- options.skip = CrSkip;
- } else if (skip_white_sym == v) {
- options.skip = SpcSkip;
- } else {
- rb_raise(ox_parse_error_class, ":skip must be :skip_none, :skip_return, :skip_white, or :skip_off.\n");
- }
- }
-
- if (Qnil != (v = rb_hash_lookup(h, trace_sym))) {
- Check_Type(v, T_FIXNUM);
- options.trace = FIX2INT(v);
- }
- if (Qnil != (v = rb_hash_lookup(h, symbolize_keys_sym))) {
- options.sym_keys = (Qfalse == v) ? No : Yes;
- }
- options.element_key_mod = rb_hash_lookup2(h, element_key_mod_sym, options.element_key_mod);
- options.attr_key_mod = rb_hash_lookup2(h, attr_key_mod_sym, options.attr_key_mod);
-
- if (Qnil != (v = rb_hash_lookup(h, convert_special_sym))) {
- options.convert_special = (Qfalse != v);
- }
-
- v = rb_hash_lookup(h, invalid_replace_sym);
- if (Qnil == v) {
- if (Qtrue == rb_funcall(h, has_key_id, 1, invalid_replace_sym)) {
- options.allow_invalid = Yes;
- }
- } else {
- long slen;
+ VALUE h = *argv;
+ VALUE v;
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(options.inv_repl) - 2 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":invalid_replace can be no longer than %d characters.",
- (int)sizeof(options.inv_repl) - 2);
- }
- strncpy(options.inv_repl + 1, StringValuePtr(v), sizeof(options.inv_repl) - 1);
- options.inv_repl[sizeof(options.inv_repl) - 1] = '\0';
- *options.inv_repl = (char)slen;
- options.allow_invalid = No;
- }
- v = rb_hash_lookup(h, strip_namespace_sym);
- if (Qfalse == v) {
- *options.strip_ns = '\0';
- } else if (Qtrue == v) {
- *options.strip_ns = '*';
- options.strip_ns[1] = '\0';
- } else if (Qnil != v) {
- long slen;
-
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(options.strip_ns) - 1 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":strip_namespace can be no longer than %d characters.",
- (int)sizeof(options.strip_ns) - 1);
- }
- strncpy(options.strip_ns, StringValuePtr(v), sizeof(options.strip_ns) - 1);
- options.strip_ns[sizeof(options.strip_ns) - 1] = '\0';
- }
- v = rb_hash_lookup(h, margin_sym);
- if (Qnil != v) {
- long slen;
-
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(options.margin) - 1 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":margin can be no longer than %d characters.",
- (int)sizeof(options.margin) - 1);
- }
- strncpy(options.margin, StringValuePtr(v), sizeof(options.margin) - 1);
- options.margin[sizeof(options.margin) - 1] = '\0';
- options.margin_len = strlen(options.margin);
- }
+ if (Qnil != (v = rb_hash_lookup(h, mode_sym))) {
+ if (object_sym == v) {
+ options.mode = ObjMode;
+ } else if (optimized_sym == v) {
+ options.mode = ObjMode;
+ } else if (generic_sym == v) {
+ options.mode = GenMode;
+ } else if (limited_sym == v) {
+ options.mode = LimMode;
+ } else if (hash_sym == v) {
+ options.mode = HashMode;
+ } else if (hash_no_attrs_sym == v) {
+ options.mode = HashNoAttrMode;
+ } else {
+ rb_raise(ox_parse_error_class, ":mode must be :generic, :object, :limited, :hash, :hash_no_attrs.\n");
+ }
+ }
+ if (Qnil != (v = rb_hash_lookup(h, effort_sym))) {
+ if (auto_define_sym == v) {
+ options.effort = AutoEffort;
+ } else if (tolerant_sym == v) {
+ options.effort = TolerantEffort;
+ } else if (strict_sym == v) {
+ options.effort = StrictEffort;
+ } else {
+ rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, or :auto_define.\n");
+ }
+ }
+ if (Qnil != (v = rb_hash_lookup(h, skip_sym))) {
+ if (skip_none_sym == v) {
+ options.skip = NoSkip;
+ } else if (skip_off_sym == v) {
+ options.skip = OffSkip;
+ } else if (skip_return_sym == v) {
+ options.skip = CrSkip;
+ } else if (skip_white_sym == v) {
+ options.skip = SpcSkip;
+ } else {
+ rb_raise(ox_parse_error_class, ":skip must be :skip_none, :skip_return, :skip_white, or :skip_off.\n");
+ }
+ }
+
+ if (Qnil != (v = rb_hash_lookup(h, trace_sym))) {
+ Check_Type(v, T_FIXNUM);
+ options.trace = FIX2INT(v);
+ }
+ if (Qnil != (v = rb_hash_lookup(h, symbolize_keys_sym))) {
+ options.sym_keys = (Qfalse == v) ? No : Yes;
+ }
+ options.element_key_mod = rb_hash_lookup2(h, element_key_mod_sym, options.element_key_mod);
+ options.attr_key_mod = rb_hash_lookup2(h, attr_key_mod_sym, options.attr_key_mod);
+
+ if (Qnil != (v = rb_hash_lookup(h, convert_special_sym))) {
+ options.convert_special = (Qfalse != v);
+ }
+ if (Qnil != (v = rb_hash_lookup(h, no_empty_sym))) {
+ options.no_empty = (Qfalse != v);
+ }
+
+ v = rb_hash_lookup(h, invalid_replace_sym);
+ if (Qnil == v) {
+ if (Qtrue == rb_funcall(h, has_key_id, 1, invalid_replace_sym)) {
+ options.allow_invalid = Yes;
+ }
+ } else {
+ long slen;
+
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(options.inv_repl) - 2 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":invalid_replace can be no longer than %d characters.",
+ (int)sizeof(options.inv_repl) - 2);
+ }
+ strncpy(options.inv_repl + 1, StringValuePtr(v), sizeof(options.inv_repl) - 1);
+ options.inv_repl[sizeof(options.inv_repl) - 1] = '\0';
+ *options.inv_repl = (char)slen;
+ options.allow_invalid = No;
+ }
+ v = rb_hash_lookup(h, strip_namespace_sym);
+ if (Qfalse == v) {
+ *options.strip_ns = '\0';
+ } else if (Qtrue == v) {
+ *options.strip_ns = '*';
+ options.strip_ns[1] = '\0';
+ } else if (Qnil != v) {
+ long slen;
+
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(options.strip_ns) - 1 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":strip_namespace can be no longer than %d characters.",
+ (int)sizeof(options.strip_ns) - 1);
+ }
+ strncpy(options.strip_ns, StringValuePtr(v), sizeof(options.strip_ns) - 1);
+ options.strip_ns[sizeof(options.strip_ns) - 1] = '\0';
+ }
+ v = rb_hash_lookup(h, margin_sym);
+ if (Qnil != v) {
+ long slen;
+
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(options.margin) - 1 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":margin can be no longer than %d characters.",
+ (int)sizeof(options.margin) - 1);
+ }
+ strncpy(options.margin, StringValuePtr(v), sizeof(options.margin) - 1);
+ options.margin[sizeof(options.margin) - 1] = '\0';
+ options.margin_len = strlen(options.margin);
+ }
+ if (Qnil != (v = rb_hash_lookup(h, with_cdata_sym))) {
+ options.with_cdata = (Qtrue == v);
+ }
}
-#if HAS_ENCODING_SUPPORT
if ('\0' == *options.encoding) {
- if (Qnil != encoding) {
- options.rb_enc = rb_enc_from_index(rb_enc_get_index(encoding));
- } else {
- options.rb_enc = 0;
- }
- } else if (0 == options.rb_enc) {
- options.rb_enc = rb_enc_find(options.encoding);
- }
-#elif HAS_PRIVATE_ENCODING
- if ('\0' == *options.encoding) {
- if (Qnil != encoding) {
- options.rb_enc = encoding;
- } else {
- options.rb_enc = Qnil;
- }
+ if (Qnil != encoding) {
+ options.rb_enc = rb_enc_from_index(rb_enc_get_index(encoding));
+ } else {
+ options.rb_enc = 0;
+ }
} else if (0 == options.rb_enc) {
- options.rb_enc = rb_str_new2(options.encoding);
- rb_gc_register_address(&options.rb_enc);
+ options.rb_enc = rb_enc_find(options.encoding);
}
-#endif
xml = defuse_bom(xml, &options);
switch (options.mode) {
case ObjMode:
-#if HAS_GC_GUARD
- rb_gc_disable();
+#ifdef RB_GC_GUARD
+ rb_gc_disable();
#endif
- obj = ox_parse(xml, len, ox_obj_callbacks, 0, &options, err);
-#if HAS_GC_GUARD
- RB_GC_GUARD(obj);
- rb_gc_enable();
+ obj = ox_parse(xml, len, ox_obj_callbacks, 0, &options, err);
+#ifdef RB_GC_GUARD
+ RB_GC_GUARD(obj);
+ rb_gc_enable();
#endif
- break;
- case GenMode:
- obj = ox_parse(xml, len, ox_gen_callbacks, 0, &options, err);
- break;
- case LimMode:
- obj = ox_parse(xml, len, ox_limited_callbacks, 0, &options, err);
- break;
+ break;
+ case GenMode: obj = ox_parse(xml, len, ox_gen_callbacks, 0, &options, err); break;
+ case LimMode: obj = ox_parse(xml, len, ox_limited_callbacks, 0, &options, err); break;
case HashMode:
- obj = ox_parse(xml, len, ox_hash_callbacks, 0, &options, err);
- break;
+ if (options.with_cdata) {
+ obj = ox_parse(xml, len, ox_hash_cdata_callbacks, 0, &options, err);
+ } else {
+ obj = ox_parse(xml, len, ox_hash_callbacks, 0, &options, err);
+ }
+ break;
case HashNoAttrMode:
- obj = ox_parse(xml, len, ox_hash_no_attrs_callbacks, 0, &options, err);
- break;
- case NoMode:
- obj = ox_parse(xml, len, ox_nomode_callbacks, 0, &options, err);
- break;
- default:
- obj = ox_parse(xml, len, ox_gen_callbacks, 0, &options, err);
- break;
+ if (options.with_cdata) {
+ obj = ox_parse(xml, len, ox_hash_no_attrs_cdata_callbacks, 0, &options, err);
+ } else {
+ obj = ox_parse(xml, len, ox_hash_no_attrs_callbacks, 0, &options, err);
+ }
+ break;
+ case NoMode: obj = ox_parse(xml, len, ox_nomode_callbacks, 0, &options, err); break;
+ default: obj = ox_parse(xml, len, ox_gen_callbacks, 0, &options, err); break;
}
return obj;
}
@@ -908,45 +930,41 @@
* - _:auto_define_ - auto define missing classes and modules
* - *:trace* [Fixnum] trace level as a Fixnum, default: 0 (silent)
* - *:symbolize_keys* [true|false|nil] symbolize element attribute keys or leave as Strings
- * - *:invalid_replace* [nil|String] replacement string for invalid XML characters on dump. nil indicates include anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
- * - *:strip_namespace* [String|true|false] "" or false result in no namespace stripping. A string of "*" or true will strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
+ * - *:invalid_replace* [nil|String] replacement string for invalid XML characters on dump. nil indicates include
+ * anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
+ * - *:strip_namespace* [String|true|false] "" or false result in no namespace stripping. A string of "*" or true will
+ * strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
+ * - *:with_cdata* [true|false] if true cdata is included in hash_load output otherwise it is not.
*/
-static VALUE
-load_str(int argc, VALUE *argv, VALUE self) {
- char *xml;
- size_t len;
- VALUE obj;
- VALUE encoding;
- struct _err err;
+static VALUE load_str(int argc, VALUE *argv, VALUE self) {
+ char *xml;
+ size_t len;
+ VALUE obj;
+ VALUE encoding;
+ struct _err err;
err_init(&err);
Check_Type(*argv, T_STRING);
/* the xml string gets modified so make a copy of it */
len = RSTRING_LEN(*argv) + 1;
if (SMALL_XML < len) {
- xml = ALLOC_N(char, len);
+ xml = ALLOC_N(char, len);
} else {
- xml = ALLOCA_N(char, len);
+ xml = ALLOCA_N(char, len);
}
-#if HAS_ENCODING_SUPPORT
-#ifdef MACRUBY_RUBY
- encoding = rb_funcall(*argv, encoding_id, 0);
-#else
+#if HAVE_RB_OBJ_ENCODING
encoding = rb_obj_encoding(*argv);
-#endif
-#elif HAS_PRIVATE_ENCODING
- encoding = rb_funcall(*argv, encoding_id, 0);
#else
encoding = Qnil;
#endif
memcpy(xml, StringValuePtr(*argv), len);
xml[len - 1] = '\0';
- obj = load(xml, len - 1, argc - 1, argv + 1, self, encoding, &err);
+ obj = load(xml, len - 1, argc - 1, argv + 1, self, encoding, &err);
if (SMALL_XML < len) {
- xfree(xml);
+ xfree(xml);
}
if (err_has(&err)) {
- ox_err_raise(&err);
+ ox_err_raise(&err);
}
return obj;
}
@@ -970,45 +988,46 @@
* - _:auto_define_ - auto define missing classes and modules
* - *:trace* [Fixnum] trace level as a Fixnum, default: 0 (silent)
* - *:symbolize_keys* [true|false|nil] symbolize element attribute keys or leave as Strings
- * - *:invalid_replace* [nil|String] replacement string for invalid XML characters on dump. nil indicates include anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
- * - *:strip_namespace* [String|true|false] "" or false result in no namespace stripping. A string of "*" or true will strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
+ * - *:invalid_replace* [nil|String] replacement string for invalid XML characters on dump. nil indicates include
+ * anyway as hex. A string, limited to 10 characters will replace the invalid character with the replace.
+ * - *:strip_namespace* [String|true|false] "" or false result in no namespace stripping. A string of "*" or true will
+ * strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
*/
-static VALUE
-load_file(int argc, VALUE *argv, VALUE self) {
- char *path;
- char *xml;
- FILE *f;
- off_t len;
- VALUE obj;
- struct _err err;
+static VALUE load_file(int argc, VALUE *argv, VALUE self) {
+ char *path;
+ char *xml;
+ FILE *f;
+ off_t len;
+ VALUE obj;
+ struct _err err;
err_init(&err);
Check_Type(*argv, T_STRING);
path = StringValuePtr(*argv);
if (0 == (f = fopen(path, "r"))) {
- rb_raise(rb_eIOError, "%s\n", strerror(errno));
+ rb_raise(rb_eIOError, "%s\n", strerror(errno));
}
fseek(f, 0, SEEK_END);
len = ftello(f);
if (SMALL_XML < len) {
- xml = ALLOC_N(char, len + 1);
+ xml = ALLOC_N(char, len + 1);
} else {
- xml = ALLOCA_N(char, len + 1);
+ xml = ALLOCA_N(char, len + 1);
}
fseek(f, 0, SEEK_SET);
- if (len != fread(xml, 1, len, f)) {
- ox_err_set(&err, rb_eLoadError, "Failed to read %ld bytes from %s.\n", (long)len, path);
- obj = Qnil;
+ if ((size_t)len != fread(xml, 1, len, f)) {
+ ox_err_set(&err, rb_eLoadError, "Failed to read %ld bytes from %s.\n", (long)len, path);
+ obj = Qnil;
} else {
- xml[len] = '\0';
- obj = load(xml, len, argc - 1, argv + 1, self, Qnil, &err);
+ xml[len] = '\0';
+ obj = load(xml, len, argc - 1, argv + 1, self, Qnil, &err);
}
fclose(f);
if (SMALL_XML < len) {
- xfree(xml);
+ xfree(xml);
}
if (err_has(&err)) {
- ox_err_raise(&err);
+ ox_err_raise(&err);
}
return obj;
}
@@ -1023,66 +1042,68 @@
* - *:convert_special* [true|false] flag indicating special characters like < are converted
* - *:symbolize* [true|false] flag indicating the parser symbolize element and attribute names
* - *:smart* [true|false] flag indicating the parser uses hints if available (use with html)
- * - *:skip* [:skip_none|:skip_return|:skip_white|:skip_off] flag indicating the parser skips \\r or collpase white space into a single space. Default (skip space)
- * - *:strip_namespace* [nil|String|true|false] "" or false result in no namespace stripping. A string of "*" or true will strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
+ * - *:skip* [:skip_none|:skip_return|:skip_white|:skip_off] flag indicating the parser skips \\r or collpase white
+ * space into a single space. Default (skip space)
+ * - *:strip_namespace* [nil|String|true|false] "" or false result in no namespace stripping. A string of "*" or true
+ * will strip all namespaces. Any other non-empty string indicates that matching namespaces will be stripped.
*/
-static VALUE
-sax_parse(int argc, VALUE *argv, VALUE self) {
- struct _saxOptions options;
+static VALUE sax_parse(int argc, VALUE *argv, VALUE self) {
+ struct _saxOptions options;
- options.symbolize = (No != ox_default_options.sym_keys);
+ options.symbolize = (No != ox_default_options.sym_keys);
options.convert_special = ox_default_options.convert_special;
- options.smart = (Yes == ox_default_options.smart);
- options.skip = ox_default_options.skip;
- options.hints = NULL;
+ options.smart = (Yes == ox_default_options.smart);
+ options.skip = ox_default_options.skip;
+ options.hints = NULL;
strcpy(options.strip_ns, ox_default_options.strip_ns);
if (argc < 2) {
- rb_raise(ox_parse_error_class, "Wrong number of arguments to sax_parse.\n");
+ rb_raise(ox_parse_error_class, "Wrong number of arguments to sax_parse.\n");
}
if (3 <= argc && rb_cHash == rb_obj_class(argv[2])) {
- VALUE h = argv[2];
- VALUE v;
-
- if (Qnil != (v = rb_hash_lookup(h, convert_special_sym))) {
- options.convert_special = (Qtrue == v);
- }
- if (Qnil != (v = rb_hash_lookup(h, smart_sym))) {
- options.smart = (Qtrue == v);
- }
- if (Qnil != (v = rb_hash_lookup(h, symbolize_sym))) {
- options.symbolize = (Qtrue == v);
- }
- if (Qnil != (v = rb_hash_lookup(h, skip_sym))) {
- if (skip_return_sym == v) {
- options.skip = CrSkip;
- } else if (skip_white_sym == v) {
- options.skip = SpcSkip;
- } else if (skip_none_sym == v) {
- options.skip = NoSkip;
- } else if (skip_off_sym == v) {
- options.skip = OffSkip;
- }
- }
- if (Qnil != (v = rb_hash_lookup(h, strip_namespace_sym))) {
- if (Qfalse == v) {
- *options.strip_ns = '\0';
- } else if (Qtrue == v) {
- *options.strip_ns = '*';
- options.strip_ns[1] = '\0';
- } else {
- long slen;
+ VALUE h = argv[2];
+ VALUE v;
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(options.strip_ns) - 1 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":strip_namespace can be no longer than %d characters.",
- (int)sizeof(options.strip_ns) - 1);
- }
- strncpy(options.strip_ns, StringValuePtr(v), sizeof(options.strip_ns) - 1);
- options.strip_ns[sizeof(options.strip_ns) - 1] = '\0';
- }
- }
+ if (Qnil != (v = rb_hash_lookup(h, convert_special_sym))) {
+ options.convert_special = (Qtrue == v);
+ }
+ if (Qnil != (v = rb_hash_lookup(h, smart_sym))) {
+ options.smart = (Qtrue == v);
+ }
+ if (Qnil != (v = rb_hash_lookup(h, symbolize_sym))) {
+ options.symbolize = (Qtrue == v);
+ }
+ if (Qnil != (v = rb_hash_lookup(h, skip_sym))) {
+ if (skip_return_sym == v) {
+ options.skip = CrSkip;
+ } else if (skip_white_sym == v) {
+ options.skip = SpcSkip;
+ } else if (skip_none_sym == v) {
+ options.skip = NoSkip;
+ } else if (skip_off_sym == v) {
+ options.skip = OffSkip;
+ }
+ }
+ if (Qnil != (v = rb_hash_lookup(h, strip_namespace_sym))) {
+ if (Qfalse == v) {
+ *options.strip_ns = '\0';
+ } else if (Qtrue == v) {
+ *options.strip_ns = '*';
+ options.strip_ns[1] = '\0';
+ } else {
+ long slen;
+
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(options.strip_ns) - 1 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":strip_namespace can be no longer than %d characters.",
+ (int)sizeof(options.strip_ns) - 1);
+ }
+ strncpy(options.strip_ns, StringValuePtr(v), sizeof(options.strip_ns) - 1);
+ options.strip_ns[sizeof(options.strip_ns) - 1] = '\0';
+ }
+ }
}
ox_sax_parse(argv[0], argv[1], &options);
@@ -1098,7 +1119,8 @@
* - +options+ [Hash] options parse options
* - *:convert_special* [true|false] flag indicating special characters like < are converted
* - *:symbolize* [true|false] flag indicating the parser symbolize element and attribute names
- * - *:skip* [:skip_none|:skip_return|:skip_white|:skip_off] flag indicating the parser skips \\r or collapse white space into a single space. Default (skip space)
+ * - *:skip* [:skip_none|:skip_return|:skip_white|:skip_off] flag indicating the parser skips \\r or collapse white
+ * space into a single space. Default (skip space)
* - *:overlay* [Hash] a Hash of keys that match html element names and values that are one of
* - _:active_ - make the normal callback for the element
* - _:nest_ok_ - active but ignore nest check
@@ -1107,165 +1129,166 @@
* - _:off_ - block this element and it's children unless the child element is active
* - _:abort_ - abort the html processing and return
*/
-static VALUE
-sax_html(int argc, VALUE *argv, VALUE self) {
- struct _saxOptions options;
- bool free_hints = false;
+static VALUE sax_html(int argc, VALUE *argv, VALUE self) {
+ struct _saxOptions options;
+ bool free_hints = false;
- options.symbolize = (No != ox_default_options.sym_keys);
+ options.symbolize = (No != ox_default_options.sym_keys);
options.convert_special = ox_default_options.convert_special;
- options.smart = true;
- options.skip = ox_default_options.skip;
- options.hints = ox_default_options.html_hints;
+ options.smart = true;
+ options.skip = ox_default_options.skip;
+ options.hints = ox_default_options.html_hints;
if (NULL == options.hints) {
- options.hints = ox_hints_html();
+ options.hints = ox_hints_html();
}
*options.strip_ns = '\0';
if (argc < 2) {
- rb_raise(ox_parse_error_class, "Wrong number of arguments to sax_html.\n");
+ rb_raise(ox_parse_error_class, "Wrong number of arguments to sax_html.\n");
}
if (3 <= argc && rb_cHash == rb_obj_class(argv[2])) {
- volatile VALUE h = argv[2];
- volatile VALUE v;
+ volatile VALUE h = argv[2];
+ volatile VALUE v;
- if (Qnil != (v = rb_hash_lookup(h, convert_special_sym))) {
- options.convert_special = (Qtrue == v);
- }
- if (Qnil != (v = rb_hash_lookup(h, symbolize_sym))) {
- options.symbolize = (Qtrue == v);
- }
- if (Qnil != (v = rb_hash_lookup(h, skip_sym))) {
- if (skip_return_sym == v) {
- options.skip = CrSkip;
- } else if (skip_white_sym == v) {
- options.skip = SpcSkip;
- } else if (skip_none_sym == v) {
- options.skip = NoSkip;
- } else if (skip_off_sym == v) {
- options.skip = OffSkip;
- }
- }
- if (Qnil != (v = rb_hash_lookup(h, overlay_sym))) {
- int cnt;
-
- Check_Type(v, T_HASH);
- cnt = (int)RHASH_SIZE(v);
- if (0 == cnt) {
- options.hints = ox_hints_html();
- } else {
- options.hints = ox_hints_dup(options.hints);
- free_hints = true;
- rb_hash_foreach(v, set_overlay, (VALUE)options.hints);
- }
- }
+ if (Qnil != (v = rb_hash_lookup(h, convert_special_sym))) {
+ options.convert_special = (Qtrue == v);
+ }
+ if (Qnil != (v = rb_hash_lookup(h, symbolize_sym))) {
+ options.symbolize = (Qtrue == v);
+ }
+ if (Qnil != (v = rb_hash_lookup(h, skip_sym))) {
+ if (skip_return_sym == v) {
+ options.skip = CrSkip;
+ } else if (skip_white_sym == v) {
+ options.skip = SpcSkip;
+ } else if (skip_none_sym == v) {
+ options.skip = NoSkip;
+ } else if (skip_off_sym == v) {
+ options.skip = OffSkip;
+ }
+ }
+ if (Qnil != (v = rb_hash_lookup(h, overlay_sym))) {
+ int cnt;
+
+ Check_Type(v, T_HASH);
+ cnt = (int)RHASH_SIZE(v);
+ if (0 == cnt) {
+ options.hints = ox_hints_html();
+ } else {
+ options.hints = ox_hints_dup(options.hints);
+ free_hints = true;
+ rb_hash_foreach(v, set_overlay, (VALUE)options.hints);
+ }
+ }
}
ox_sax_parse(argv[0], argv[1], &options);
if (free_hints) {
- ox_hints_destroy(options.hints);
+ ox_hints_destroy(options.hints);
}
return Qnil;
}
-static void
-parse_dump_options(VALUE ropts, Options copts) {
- struct _yesNoOpt ynos[] = {
- { with_xml_sym, &copts->with_xml },
- { with_dtd_sym, &copts->with_dtd },
- { with_instruct_sym, &copts->with_instruct },
- { xsd_date_sym, &copts->xsd_date },
- { circular_sym, &copts->circular },
- { Qnil, 0 }
- };
- YesNoOpt o;
+static void parse_dump_options(VALUE ropts, Options copts) {
+ struct _yesNoOpt ynos[] = {{with_xml_sym, &copts->with_xml},
+ {with_dtd_sym, &copts->with_dtd},
+ {with_instruct_sym, &copts->with_instruct},
+ {xsd_date_sym, &copts->xsd_date},
+ {circular_sym, &copts->circular},
+ {Qnil, 0}};
+ YesNoOpt o;
if (rb_cHash == rb_obj_class(ropts)) {
- VALUE v;
+ VALUE v;
- if (Qnil != (v = rb_hash_lookup(ropts, ox_indent_sym))) {
+ if (Qnil != (v = rb_hash_lookup(ropts, ox_indent_sym))) {
#ifdef RUBY_INTEGER_UNIFICATION
- if (rb_cInteger != rb_obj_class(v) && T_FIXNUM != rb_type(v)) {
+ if (rb_cInteger != rb_obj_class(v) && T_FIXNUM != rb_type(v)) {
#else
- if (rb_cFixnum != rb_obj_class(v)) {
+ if (rb_cFixnum != rb_obj_class(v)) {
#endif
- rb_raise(ox_parse_error_class, ":indent must be a Fixnum.\n");
- }
- copts->indent = NUM2INT(v);
- }
- if (Qnil != (v = rb_hash_lookup(ropts, trace_sym))) {
+ rb_raise(ox_parse_error_class, ":indent must be a Fixnum.\n");
+ }
+ copts->indent = NUM2INT(v);
+ }
+ if (Qnil != (v = rb_hash_lookup(ropts, trace_sym))) {
#ifdef RUBY_INTEGER_UNIFICATION
- if (rb_cInteger != rb_obj_class(v) && T_FIXNUM != rb_type(v)) {
+ if (rb_cInteger != rb_obj_class(v) && T_FIXNUM != rb_type(v)) {
#else
- if (rb_cFixnum != rb_obj_class(v)) {
+ if (rb_cFixnum != rb_obj_class(v)) {
#endif
- rb_raise(ox_parse_error_class, ":trace must be a Fixnum.\n");
- }
- copts->trace = NUM2INT(v);
- }
- if (Qnil != (v = rb_hash_lookup(ropts, ox_encoding_sym))) {
- if (rb_cString != rb_obj_class(v)) {
- rb_raise(ox_parse_error_class, ":encoding must be a String.\n");
- }
- strncpy(copts->encoding, StringValuePtr(v), sizeof(copts->encoding) - 1);
- }
- if (Qnil != (v = rb_hash_lookup(ropts, effort_sym))) {
- if (auto_define_sym == v) {
- copts->effort = AutoEffort;
- } else if (tolerant_sym == v) {
- copts->effort = TolerantEffort;
- } else if (strict_sym == v) {
- copts->effort = StrictEffort;
- } else {
- rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, or :auto_define.\n");
- }
- }
- v = rb_hash_lookup(ropts, invalid_replace_sym);
- if (Qnil == v) {
- if (Qtrue == rb_funcall(ropts, has_key_id, 1, invalid_replace_sym)) {
- copts->allow_invalid = Yes;
- }
- } else {
- long slen;
-
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(copts->inv_repl) - 2 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":invalid_replace can be no longer than %d characters.",
- (int)sizeof(copts->inv_repl) - 2);
- }
- strncpy(copts->inv_repl + 1, StringValuePtr(v), sizeof(copts->inv_repl) - 1);
- copts->inv_repl[sizeof(copts->inv_repl) - 1] = '\0';
- *copts->inv_repl = (char)slen;
- copts->allow_invalid = No;
- }
- v = rb_hash_lookup(ropts, margin_sym);
- if (Qnil != v) {
- long slen;
-
- Check_Type(v, T_STRING);
- slen = RSTRING_LEN(v);
- if (sizeof(copts->margin) - 2 < (size_t)slen) {
- rb_raise(ox_parse_error_class, ":margin can be no longer than %d characters.",
- (int)sizeof(copts->margin) - 2);
- }
- strncpy(copts->margin, StringValuePtr(v), sizeof(copts->margin) - 1);
- copts->margin[sizeof(copts->margin) - 1] = '\0';
- copts->margin_len = (char)slen;
- }
-
- for (o = ynos; 0 != o->attr; o++) {
- if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
- VALUE c = rb_obj_class(v);
-
- if (rb_cTrueClass == c) {
- *o->attr = Yes;
- } else if (rb_cFalseClass == c) {
- *o->attr = No;
- } else {
- rb_raise(ox_parse_error_class, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
- }
- }
- }
+ rb_raise(ox_parse_error_class, ":trace must be a Fixnum.\n");
+ }
+ copts->trace = NUM2INT(v);
+ }
+ if (Qnil != (v = rb_hash_lookup(ropts, ox_encoding_sym))) {
+ if (rb_cString != rb_obj_class(v)) {
+ rb_raise(ox_parse_error_class, ":encoding must be a String.\n");
+ }
+ strncpy(copts->encoding, StringValuePtr(v), sizeof(copts->encoding) - 1);
+ }
+ if (Qnil != (v = rb_hash_lookup(ropts, no_empty_sym))) {
+ copts->no_empty = (v == Qtrue);
+ }
+ if (Qnil != (v = rb_hash_lookup(ropts, effort_sym))) {
+ if (auto_define_sym == v) {
+ copts->effort = AutoEffort;
+ } else if (tolerant_sym == v) {
+ copts->effort = TolerantEffort;
+ } else if (strict_sym == v) {
+ copts->effort = StrictEffort;
+ } else {
+ rb_raise(ox_parse_error_class, ":effort must be :strict, :tolerant, or :auto_define.\n");
+ }
+ }
+ v = rb_hash_lookup(ropts, invalid_replace_sym);
+ if (Qnil == v) {
+ if (Qtrue == rb_funcall(ropts, has_key_id, 1, invalid_replace_sym)) {
+ copts->allow_invalid = Yes;
+ }
+ } else {
+ long slen;
+
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(copts->inv_repl) - 2 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":invalid_replace can be no longer than %d characters.",
+ (int)sizeof(copts->inv_repl) - 2);
+ }
+ strncpy(copts->inv_repl + 1, StringValuePtr(v), sizeof(copts->inv_repl) - 1);
+ copts->inv_repl[sizeof(copts->inv_repl) - 1] = '\0';
+ *copts->inv_repl = (char)slen;
+ copts->allow_invalid = No;
+ }
+ v = rb_hash_lookup(ropts, margin_sym);
+ if (Qnil != v) {
+ long slen;
+
+ Check_Type(v, T_STRING);
+ slen = RSTRING_LEN(v);
+ if (sizeof(copts->margin) - 2 < (size_t)slen) {
+ rb_raise(ox_parse_error_class,
+ ":margin can be no longer than %d characters.",
+ (int)sizeof(copts->margin) - 2);
+ }
+ strncpy(copts->margin, StringValuePtr(v), sizeof(copts->margin) - 1);
+ copts->margin[sizeof(copts->margin) - 1] = '\0';
+ copts->margin_len = (char)slen;
+ }
+
+ for (o = ynos; 0 != o->attr; o++) {
+ if (Qnil != (v = rb_hash_lookup(ropts, o->sym))) {
+ VALUE c = rb_obj_class(v);
+
+ if (rb_cTrueClass == c) {
+ *o->attr = Yes;
+ } else if (rb_cFalseClass == c) {
+ *o->attr = No;
+ } else {
+ rb_raise(ox_parse_error_class, "%s must be true or false.\n", rb_id2name(SYM2ID(o->sym)));
+ }
+ }
+ }
}
}
@@ -1275,43 +1298,59 @@
* - +obj+ [Object] Object to serialize as an XML document String
* - +options+ [Hash] formating options
* - *:indent* [Fixnum] format expected
+ * - *:no_empty* [true|false] if true don't output empty elements
* - *:xsd_date* [true|false] use XSD date format if true, default: false
* - *:circular* [true|false] allow circular references, default: false
- * - *:strict|:tolerant]* [ :effort effort to use when an undumpable object (e.g., IO) is encountered, default: :strict
+ * - *:strict|:tolerant]* [ :effort effort to use when an undumpable object (e.g., IO) is encountered, default:
+ * :strict
* - _:strict_ - raise an NotImplementedError if an undumpable object is encountered
* - _:tolerant_ - replaces undumplable objects with nil
*
* Note that an indent of less than zero will result in a tight one line output
* unless the text in the XML fields contain new line characters.
*/
-static VALUE
-dump(int argc, VALUE *argv, VALUE self) {
- char *xml;
- struct _options copts = ox_default_options;
- VALUE rstr;
+static VALUE dump(int argc, VALUE *argv, VALUE self) {
+ char *xml;
+ struct _options copts = ox_default_options;
+ VALUE rstr;
if (2 == argc) {
- parse_dump_options(argv[1], &copts);
+ parse_dump_options(argv[1], &copts);
}
if (0 == (xml = ox_write_obj_to_str(*argv, &copts))) {
- rb_raise(rb_eNoMemError, "Not enough memory.\n");
+ rb_raise(rb_eNoMemError, "Not enough memory.\n");
}
rstr = rb_str_new2(xml);
-#if HAS_ENCODING_SUPPORT
- if ('\0' != *copts.encoding) {
- rb_enc_associate(rstr, rb_enc_find(copts.encoding));
- }
-#elif HAS_PRIVATE_ENCODING
if ('\0' != *copts.encoding) {
- rb_funcall(rstr, ox_force_encoding_id, 1, rb_str_new2(copts.encoding));
+ rb_enc_associate(rstr, rb_enc_find(copts.encoding));
}
-#endif
xfree(xml);
return rstr;
}
-/* call-seq: to_file(file_path, obj, options)
+/* call-seq: to_xml(obj, options) => xml-string
+ *
+ * Dumps an Object (obj) to a string.
+ * - +obj+ [Object] Object to serialize as an XML document String
+ * - +options+ [Hash] formating options
+ * - *:indent* [Fixnum] format expected
+ * - *:no_empty* [true|false] if true don't output empty elements
+ * - *:xsd_date* [true|false] use XSD date format if true, default: false
+ * - *:circular* [true|false] allow circular references, default: false
+ * - *:strict|:tolerant]* [ :effort effort to use when an undumpable object (e.g., IO) is encountered, default:
+ * :strict
+ * - _:strict_ - raise an NotImplementedError if an undumpable object is encountered
+ * - _:tolerant_ - replaces undumplable objects with nil
+ *
+ * Note that an indent of less than zero will result in a tight one line output
+ * unless the text in the XML fields contain new line characters.
+ */
+static VALUE to_xml(int argc, VALUE *argv, VALUE self) {
+ return dump(argc, argv, self);
+}
+
+/* call-seq: to_file(file_path, obj, options) => Object
*
* Dumps an Object to the specified file.
* - +file_path+ [String] file path to write the XML document to
@@ -1320,19 +1359,19 @@
* - *:indent* [Fixnum] format expected
* - *:xsd_date* [true|false] use XSD date format if true, default: false
* - *:circular* [true|false] allow circular references, default: false
- * - *:strict|:tolerant]* [ :effort effort to use when an undumpable object (e.g., IO) is encountered, default: :strict
+ * - *:strict|:tolerant]* [ :effort effort to use when an undumpable object (e.g., IO) is encountered, default:
+ * :strict
* - _:strict_ - raise an NotImplementedError if an undumpable object is encountered
* - _:tolerant_ - replaces undumplable objects with nil
*
* Note that an indent of less than zero will result in a tight one line output
* unless the text in the XML fields contain new line characters.
*/
-static VALUE
-to_file(int argc, VALUE *argv, VALUE self) {
- struct _options copts = ox_default_options;
+static VALUE to_file(int argc, VALUE *argv, VALUE self) {
+ struct _options copts = ox_default_options;
if (3 == argc) {
- parse_dump_options(argv[2], &copts);
+ parse_dump_options(argv[2], &copts);
}
Check_Type(*argv, T_STRING);
ox_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
@@ -1341,24 +1380,25 @@
}
#if WITH_CACHE_TESTS
-extern void ox_cache_test(void);
+extern void ox_cache_test(void);
-static VALUE
-cache_test(VALUE self) {
+static VALUE cache_test(VALUE self) {
ox_cache_test();
return Qnil;
}
-extern void ox_cache8_test(void);
+extern void ox_cache8_test(void);
-static VALUE
-cache8_test(VALUE self) {
+static VALUE cache8_test(VALUE self) {
ox_cache8_test();
return Qnil;
}
#endif
void Init_ox() {
+#if HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(true);
+#endif
Ox = rb_define_module("Ox");
rb_define_module_function(Ox, "default_options", get_def_opts, 0);
@@ -1370,7 +1410,7 @@
rb_define_module_function(Ox, "sax_parse", sax_parse, -1);
rb_define_module_function(Ox, "sax_html", sax_html, -1);
- rb_define_module_function(Ox, "to_xml", dump, -1);
+ rb_define_module_function(Ox, "to_xml", to_xml, -1);
rb_define_module_function(Ox, "dump", dump, -1);
rb_define_module_function(Ox, "load_file", load_file, -1);
@@ -1385,59 +1425,59 @@
rb_require("bigdecimal");
rb_require("stringio");
- ox_abort_id = rb_intern("abort");
- ox_at_column_id = rb_intern("@column");
- ox_at_content_id = rb_intern("@content");
- ox_at_id = rb_intern("at");
- ox_at_line_id = rb_intern("@line");
- ox_at_pos_id = rb_intern("@pos");
- ox_at_value_id = rb_intern("@value");
- ox_attr_id = rb_intern("attr");
- ox_attr_value_id = rb_intern("attr_value");
- ox_attributes_id = rb_intern("@attributes");
- ox_attrs_done_id = rb_intern("attrs_done");
- ox_beg_id = rb_intern("@beg");
- ox_bigdecimal_id = rb_intern("BigDecimal");
- ox_call_id = rb_intern("call");
- ox_cdata_id = rb_intern("cdata");
- ox_comment_id = rb_intern("comment");
- ox_den_id = rb_intern("@den");
- ox_doctype_id = rb_intern("doctype");
- ox_end_element_id = rb_intern("end_element");
- ox_end_id = rb_intern("@end");
- ox_end_instruct_id = rb_intern("end_instruct");
- ox_error_id = rb_intern("error");
- ox_excl_id = rb_intern("@excl");
+ ox_abort_id = rb_intern("abort");
+ ox_at_column_id = rb_intern("@column");
+ ox_at_content_id = rb_intern("@content");
+ ox_at_id = rb_intern("at");
+ ox_at_line_id = rb_intern("@line");
+ ox_at_pos_id = rb_intern("@pos");
+ ox_at_value_id = rb_intern("@value");
+ ox_attr_id = rb_intern("attr");
+ ox_attr_value_id = rb_intern("attr_value");
+ ox_attributes_id = rb_intern("@attributes");
+ ox_attrs_done_id = rb_intern("attrs_done");
+ ox_beg_id = rb_intern("@beg");
+ ox_bigdecimal_id = rb_intern("BigDecimal");
+ ox_call_id = rb_intern("call");
+ ox_cdata_id = rb_intern("cdata");
+ ox_comment_id = rb_intern("comment");
+ ox_den_id = rb_intern("@den");
+ ox_doctype_id = rb_intern("doctype");
+ ox_end_element_id = rb_intern("end_element");
+ ox_end_id = rb_intern("@end");
+ ox_end_instruct_id = rb_intern("end_instruct");
+ ox_error_id = rb_intern("error");
+ ox_excl_id = rb_intern("@excl");
ox_external_encoding_id = rb_intern("external_encoding");
- ox_fileno_id = rb_intern("fileno");
- ox_force_encoding_id = rb_intern("force_encoding");
- ox_inspect_id = rb_intern("inspect");
- ox_instruct_id = rb_intern("instruct");
- ox_jd_id = rb_intern("jd");
- ox_keys_id = rb_intern("keys");
- ox_local_id = rb_intern("local");
- ox_mesg_id = rb_intern("mesg");
- ox_message_id = rb_intern("message");
- ox_nodes_id = rb_intern("@nodes");
- ox_new_id = rb_intern("new");
- ox_num_id = rb_intern("@num");
- ox_parse_id = rb_intern("parse");
- ox_pos_id = rb_intern("pos");
- ox_read_id = rb_intern("read");
- ox_readpartial_id = rb_intern("readpartial");
- ox_start_element_id = rb_intern("start_element");
- ox_string_id = rb_intern("string");
- ox_text_id = rb_intern("text");
- ox_to_c_id = rb_intern("to_c");
- ox_to_s_id = rb_intern("to_s");
- ox_to_sym_id = rb_intern("to_sym");
- ox_tv_nsec_id = rb_intern("tv_nsec");
- ox_tv_sec_id = rb_intern("tv_sec");
- ox_tv_usec_id = rb_intern("tv_usec");
- ox_value_id = rb_intern("value");
+ ox_fileno_id = rb_intern("fileno");
+ ox_force_encoding_id = rb_intern("force_encoding");
+ ox_inspect_id = rb_intern("inspect");
+ ox_instruct_id = rb_intern("instruct");
+ ox_jd_id = rb_intern("jd");
+ ox_keys_id = rb_intern("keys");
+ ox_local_id = rb_intern("local");
+ ox_mesg_id = rb_intern("mesg");
+ ox_message_id = rb_intern("message");
+ ox_nodes_id = rb_intern("@nodes");
+ ox_new_id = rb_intern("new");
+ ox_num_id = rb_intern("@num");
+ ox_parse_id = rb_intern("parse");
+ ox_pos_id = rb_intern("pos");
+ ox_read_id = rb_intern("read");
+ ox_readpartial_id = rb_intern("readpartial");
+ ox_start_element_id = rb_intern("start_element");
+ ox_string_id = rb_intern("string");
+ ox_text_id = rb_intern("text");
+ ox_to_c_id = rb_intern("to_c");
+ ox_to_s_id = rb_intern("to_s");
+ ox_to_sym_id = rb_intern("to_sym");
+ ox_tv_nsec_id = rb_intern("tv_nsec");
+ ox_tv_sec_id = rb_intern("tv_sec");
+ ox_tv_usec_id = rb_intern("tv_usec");
+ ox_value_id = rb_intern("value");
encoding_id = rb_intern("encoding");
- has_key_id = rb_intern("has_key?");
+ has_key_id = rb_intern("has_key?");
rb_require("ox/version");
rb_require("ox/error");
@@ -1452,79 +1492,162 @@
rb_require("ox/bag");
rb_require("ox/sax");
- ox_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
- ox_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
- ox_parse_error_class = rb_const_get_at(Ox, rb_intern("ParseError"));
+ ox_time_class = rb_const_get(rb_cObject, rb_intern("Time"));
+ ox_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
+ ox_parse_error_class = rb_const_get_at(Ox, rb_intern("ParseError"));
ox_syntax_error_class = rb_const_get_at(Ox, rb_intern("SyntaxError"));
- ox_arg_error_class = rb_const_get_at(Ox, rb_intern("ArgError"));
- ox_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
- ox_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
- ox_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
-
- abort_sym = ID2SYM(rb_intern("abort")); rb_gc_register_address(&abort_sym);
- active_sym = ID2SYM(rb_intern("active")); rb_gc_register_address(&active_sym);
- attr_key_mod_sym = ID2SYM(rb_intern("attr_key_mod")); rb_gc_register_address(&attr_key_mod_sym);
- auto_define_sym = ID2SYM(rb_intern("auto_define")); rb_gc_register_address(&auto_define_sym);
- auto_sym = ID2SYM(rb_intern("auto")); rb_gc_register_address(&auto_sym);
- block_sym = ID2SYM(rb_intern("block")); rb_gc_register_address(&block_sym);
- circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
- convert_special_sym = ID2SYM(rb_intern("convert_special")); rb_gc_register_address(&convert_special_sym);
- effort_sym = ID2SYM(rb_intern("effort")); rb_gc_register_address(&effort_sym);
- element_key_mod_sym = ID2SYM(rb_intern("element_key_mod")); rb_gc_register_address(&element_key_mod_sym);
- generic_sym = ID2SYM(rb_intern("generic")); rb_gc_register_address(&generic_sym);
- hash_no_attrs_sym = ID2SYM(rb_intern("hash_no_attrs")); rb_gc_register_address(&hash_no_attrs_sym);
- hash_sym = ID2SYM(rb_intern("hash")); rb_gc_register_address(&hash_sym);
- inactive_sym = ID2SYM(rb_intern("inactive")); rb_gc_register_address(&inactive_sym);
- invalid_replace_sym = ID2SYM(rb_intern("invalid_replace")); rb_gc_register_address(&invalid_replace_sym);
- limited_sym = ID2SYM(rb_intern("limited")); rb_gc_register_address(&limited_sym);
- margin_sym = ID2SYM(rb_intern("margin")); rb_gc_register_address(&margin_sym);
- mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
- nest_ok_sym = ID2SYM(rb_intern("nest_ok")); rb_gc_register_address(&nest_ok_sym);
- object_sym = ID2SYM(rb_intern("object")); rb_gc_register_address(&object_sym);
- off_sym = ID2SYM(rb_intern("off")); rb_gc_register_address(&off_sym);
- opt_format_sym = ID2SYM(rb_intern("opt_format")); rb_gc_register_address(&opt_format_sym);
- optimized_sym = ID2SYM(rb_intern("optimized")); rb_gc_register_address(&optimized_sym);
- overlay_sym = ID2SYM(rb_intern("overlay")); rb_gc_register_address(&overlay_sym);
- ox_encoding_sym = ID2SYM(rb_intern("encoding")); rb_gc_register_address(&ox_encoding_sym);
- ox_indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&ox_indent_sym);
- ox_size_sym = ID2SYM(rb_intern("size")); rb_gc_register_address(&ox_size_sym);
- ox_standalone_sym = ID2SYM(rb_intern("standalone")); rb_gc_register_address(&ox_standalone_sym);
- ox_version_sym = ID2SYM(rb_intern("version")); rb_gc_register_address(&ox_version_sym);
- skip_none_sym = ID2SYM(rb_intern("skip_none")); rb_gc_register_address(&skip_none_sym);
- skip_off_sym = ID2SYM(rb_intern("skip_off")); rb_gc_register_address(&skip_off_sym);
- skip_return_sym = ID2SYM(rb_intern("skip_return")); rb_gc_register_address(&skip_return_sym);
- skip_sym = ID2SYM(rb_intern("skip")); rb_gc_register_address(&skip_sym);
- skip_white_sym = ID2SYM(rb_intern("skip_white")); rb_gc_register_address(&skip_white_sym);
- smart_sym = ID2SYM(rb_intern("smart")); rb_gc_register_address(&smart_sym);
- strict_sym = ID2SYM(rb_intern("strict")); rb_gc_register_address(&strict_sym);
- strip_namespace_sym = ID2SYM(rb_intern("strip_namespace")); rb_gc_register_address(&strip_namespace_sym);
- symbolize_keys_sym = ID2SYM(rb_intern("symbolize_keys")); rb_gc_register_address(&symbolize_keys_sym);
- symbolize_sym = ID2SYM(rb_intern("symbolize")); rb_gc_register_address(&symbolize_sym);
- tolerant_sym = ID2SYM(rb_intern("tolerant")); rb_gc_register_address(&tolerant_sym);
- trace_sym = ID2SYM(rb_intern("trace")); rb_gc_register_address(&trace_sym);
- with_dtd_sym = ID2SYM(rb_intern("with_dtd")); rb_gc_register_address(&with_dtd_sym);
- with_instruct_sym = ID2SYM(rb_intern("with_instructions")); rb_gc_register_address(&with_instruct_sym);
- with_xml_sym = ID2SYM(rb_intern("with_xml")); rb_gc_register_address(&with_xml_sym);
- xsd_date_sym = ID2SYM(rb_intern("xsd_date")); rb_gc_register_address(&xsd_date_sym);
-
- ox_empty_string = rb_str_new2(""); rb_gc_register_address(&ox_empty_string);
- ox_zero_fixnum = INT2NUM(0); rb_gc_register_address(&ox_zero_fixnum);
- ox_sym_bank = rb_ary_new(); rb_gc_register_address(&ox_sym_bank);
+ ox_arg_error_class = rb_const_get_at(Ox, rb_intern("ArgError"));
+ ox_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
+ ox_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
+ ox_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
+
+ abort_sym = ID2SYM(rb_intern("abort"));
+ rb_gc_register_address(&abort_sym);
+ active_sym = ID2SYM(rb_intern("active"));
+ rb_gc_register_address(&active_sym);
+ attr_key_mod_sym = ID2SYM(rb_intern("attr_key_mod"));
+ rb_gc_register_address(&attr_key_mod_sym);
+ auto_define_sym = ID2SYM(rb_intern("auto_define"));
+ rb_gc_register_address(&auto_define_sym);
+ auto_sym = ID2SYM(rb_intern("auto"));
+ rb_gc_register_address(&auto_sym);
+ block_sym = ID2SYM(rb_intern("block"));
+ rb_gc_register_address(&block_sym);
+ circular_sym = ID2SYM(rb_intern("circular"));
+ rb_gc_register_address(&circular_sym);
+ convert_special_sym = ID2SYM(rb_intern("convert_special"));
+ rb_gc_register_address(&convert_special_sym);
+ effort_sym = ID2SYM(rb_intern("effort"));
+ rb_gc_register_address(&effort_sym);
+ element_key_mod_sym = ID2SYM(rb_intern("element_key_mod"));
+ rb_gc_register_address(&element_key_mod_sym);
+ generic_sym = ID2SYM(rb_intern("generic"));
+ rb_gc_register_address(&generic_sym);
+ hash_no_attrs_sym = ID2SYM(rb_intern("hash_no_attrs"));
+ rb_gc_register_address(&hash_no_attrs_sym);
+ hash_sym = ID2SYM(rb_intern("hash"));
+ rb_gc_register_address(&hash_sym);
+ inactive_sym = ID2SYM(rb_intern("inactive"));
+ rb_gc_register_address(&inactive_sym);
+ invalid_replace_sym = ID2SYM(rb_intern("invalid_replace"));
+ rb_gc_register_address(&invalid_replace_sym);
+ limited_sym = ID2SYM(rb_intern("limited"));
+ rb_gc_register_address(&limited_sym);
+ margin_sym = ID2SYM(rb_intern("margin"));
+ rb_gc_register_address(&margin_sym);
+ mode_sym = ID2SYM(rb_intern("mode"));
+ rb_gc_register_address(&mode_sym);
+ nest_ok_sym = ID2SYM(rb_intern("nest_ok"));
+ rb_gc_register_address(&nest_ok_sym);
+ no_empty_sym = ID2SYM(rb_intern("no_empty"));
+ rb_gc_register_address(&no_empty_sym);
+ object_sym = ID2SYM(rb_intern("object"));
+ rb_gc_register_address(&object_sym);
+ off_sym = ID2SYM(rb_intern("off"));
+ rb_gc_register_address(&off_sym);
+ opt_format_sym = ID2SYM(rb_intern("opt_format"));
+ rb_gc_register_address(&opt_format_sym);
+ optimized_sym = ID2SYM(rb_intern("optimized"));
+ rb_gc_register_address(&optimized_sym);
+ overlay_sym = ID2SYM(rb_intern("overlay"));
+ rb_gc_register_address(&overlay_sym);
+ ox_encoding_sym = ID2SYM(rb_intern("encoding"));
+ rb_gc_register_address(&ox_encoding_sym);
+ ox_indent_sym = ID2SYM(rb_intern("indent"));
+ rb_gc_register_address(&ox_indent_sym);
+ ox_size_sym = ID2SYM(rb_intern("size"));
+ rb_gc_register_address(&ox_size_sym);
+ ox_standalone_sym = ID2SYM(rb_intern("standalone"));
+ rb_gc_register_address(&ox_standalone_sym);
+ ox_version_sym = ID2SYM(rb_intern("version"));
+ rb_gc_register_address(&ox_version_sym);
+ skip_none_sym = ID2SYM(rb_intern("skip_none"));
+ rb_gc_register_address(&skip_none_sym);
+ skip_off_sym = ID2SYM(rb_intern("skip_off"));
+ rb_gc_register_address(&skip_off_sym);
+ skip_return_sym = ID2SYM(rb_intern("skip_return"));
+ rb_gc_register_address(&skip_return_sym);
+ skip_sym = ID2SYM(rb_intern("skip"));
+ rb_gc_register_address(&skip_sym);
+ skip_white_sym = ID2SYM(rb_intern("skip_white"));
+ rb_gc_register_address(&skip_white_sym);
+ smart_sym = ID2SYM(rb_intern("smart"));
+ rb_gc_register_address(&smart_sym);
+ strict_sym = ID2SYM(rb_intern("strict"));
+ rb_gc_register_address(&strict_sym);
+ strip_namespace_sym = ID2SYM(rb_intern("strip_namespace"));
+ rb_gc_register_address(&strip_namespace_sym);
+ symbolize_keys_sym = ID2SYM(rb_intern("symbolize_keys"));
+ rb_gc_register_address(&symbolize_keys_sym);
+ symbolize_sym = ID2SYM(rb_intern("symbolize"));
+ rb_gc_register_address(&symbolize_sym);
+ tolerant_sym = ID2SYM(rb_intern("tolerant"));
+ rb_gc_register_address(&tolerant_sym);
+ trace_sym = ID2SYM(rb_intern("trace"));
+ rb_gc_register_address(&trace_sym);
+ with_cdata_sym = ID2SYM(rb_intern("with_cdata"));
+ rb_gc_register_address(&with_cdata_sym);
+ with_dtd_sym = ID2SYM(rb_intern("with_dtd"));
+ rb_gc_register_address(&with_dtd_sym);
+ with_instruct_sym = ID2SYM(rb_intern("with_instructions"));
+ rb_gc_register_address(&with_instruct_sym);
+ with_xml_sym = ID2SYM(rb_intern("with_xml"));
+ rb_gc_register_address(&with_xml_sym);
+ xsd_date_sym = ID2SYM(rb_intern("xsd_date"));
+ rb_gc_register_address(&xsd_date_sym);
+
+ ox_empty_string = rb_str_new2("");
+ rb_gc_register_address(&ox_empty_string);
+ ox_zero_fixnum = INT2NUM(0);
+ rb_gc_register_address(&ox_zero_fixnum);
+ ox_sym_bank = rb_ary_new();
+ rb_gc_register_address(&ox_sym_bank);
ox_document_clas = rb_const_get_at(Ox, rb_intern("Document"));
- ox_element_clas = rb_const_get_at(Ox, rb_intern("Element"));
+ ox_element_clas = rb_const_get_at(Ox, rb_intern("Element"));
ox_instruct_clas = rb_const_get_at(Ox, rb_intern("Instruct"));
- ox_comment_clas = rb_const_get_at(Ox, rb_intern("Comment"));
- ox_raw_clas = rb_const_get_at(Ox, rb_intern("Raw"));
- ox_doctype_clas = rb_const_get_at(Ox, rb_intern("DocType"));
- ox_cdata_clas = rb_const_get_at(Ox, rb_intern("CData"));
- ox_bag_clas = rb_const_get_at(Ox, rb_intern("Bag"));
-
- ox_cache_new(&ox_symbol_cache);
- ox_cache_new(&ox_class_cache);
- ox_cache_new(&ox_attr_cache);
+ ox_comment_clas = rb_const_get_at(Ox, rb_intern("Comment"));
+ ox_raw_clas = rb_const_get_at(Ox, rb_intern("Raw"));
+ ox_doctype_clas = rb_const_get_at(Ox, rb_intern("DocType"));
+ ox_cdata_clas = rb_const_get_at(Ox, rb_intern("CData"));
+ ox_bag_clas = rb_const_get_at(Ox, rb_intern("Bag"));
+
+ // Classes can move in more recent versions so register them all.
+ rb_gc_register_address(&Ox);
+ rb_gc_register_address(&ox_arg_error_class);
+ rb_gc_register_address(&ox_bag_clas);
+ rb_gc_register_address(&ox_bag_clas);
+ rb_gc_register_address(&ox_bigdecimal_class);
+ rb_gc_register_address(&ox_cdata_clas);
+ rb_gc_register_address(&ox_cdata_clas);
+ rb_gc_register_address(&ox_comment_clas);
+ rb_gc_register_address(&ox_comment_clas);
+ rb_gc_register_address(&ox_date_class);
+ rb_gc_register_address(&ox_doctype_clas);
+ rb_gc_register_address(&ox_doctype_clas);
+ rb_gc_register_address(&ox_document_clas);
+ rb_gc_register_address(&ox_document_clas);
+ rb_gc_register_address(&ox_element_clas);
+ rb_gc_register_address(&ox_element_clas);
+ rb_gc_register_address(&ox_encoding_sym);
+ rb_gc_register_address(&ox_indent_sym);
+ rb_gc_register_address(&ox_instruct_clas);
+ rb_gc_register_address(&ox_instruct_clas);
+ rb_gc_register_address(&ox_parse_error_class);
+ rb_gc_register_address(&ox_raw_clas);
+ rb_gc_register_address(&ox_raw_clas);
+ rb_gc_register_address(&ox_size_sym);
+ rb_gc_register_address(&ox_standalone_sym);
+ rb_gc_register_address(&ox_stringio_class);
+ rb_gc_register_address(&ox_struct_class);
+ rb_gc_register_address(&ox_syntax_error_class);
+ rb_gc_register_address(&ox_time_class);
+ rb_gc_register_address(&ox_version_sym);
+
+ slot_cache_new(&ox_class_cache);
ox_sax_define();
+ ox_hash_init();
#if WITH_CACHE_TESTS
// space added to stop yardoc from trying to document
@@ -1532,12 +1655,7 @@
rb_define _module_function(Ox, "cache8_test", cache8_test, 0);
#endif
-#if HAS_ENCODING_SUPPORT
ox_utf8_encoding = rb_enc_find("UTF-8");
-#elif HAS_PRIVATE_ENCODING
- ox_utf8_encoding = rb_str_new2("UTF-8");
- rb_gc_register_address(&ox_utf8_encoding);
-#endif
}
#if __GNUC__ > 4
@@ -1545,19 +1663,19 @@
#else
void
#endif
-_ox_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line) {
- int xline = 1;
- int col = 1;
+_ox_raise_error(const char *msg, const char *xml, const char *current, const char *file, int line) {
+ int xline = 1;
+ int col = 1;
for (; xml < current && '\n' != *current; current--) {
- col++;
+ col++;
}
for (; xml < current; current--) {
- if ('\n' == *current) {
- xline++;
- }
+ if ('\n' == *current) {
+ xline++;
+ }
}
-#if HAS_GC_GUARD
+#ifdef RB_GC_GUARD
rb_gc_enable();
#endif
rb_raise(ox_parse_error_class, "%s at line %d, column %d [%s:%d]\n", msg, xline, col, file, line);
diff -Nru ruby-ox-2.11.0/ext/ox/ox.h ruby-ox-2.14.9/ext/ox/ox.h
--- ruby-ox-2.11.0/ext/ox/ox.h 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/ox.h 2022-02-10 23:24:14.000000000 +0000
@@ -16,264 +16,236 @@
#define RSTRING_NOT_MODIFIED
#include "ruby.h"
-#if HAS_ENCODING_SUPPORT
#include "ruby/encoding.h"
-#endif
-#ifdef RUBINIUS_RUBY
-#undef T_COMPLEX
-enum st_retval {ST_CONTINUE = 0, ST_STOP = 1, ST_DELETE = 2, ST_CHECK};
+#if HAVE_RUBY_ST_H
+#include "ruby/st.h"
#else
-#if HAS_TOP_LEVEL_ST_H
-/* Only on travis, local is where it is for all others. Seems to vary depending on the travis machine picked up. */
+// Only on travis, local is where it is for all others. Seems to vary depending on the travis machine picked up.
#include "st.h"
-#else
-#include "ruby/st.h"
#endif
-#endif
-
-#include "cache.h"
-#include "err.h"
-#include "type.h"
#include "attr.h"
+#include "err.h"
#include "helper.h"
+#include "slotcache.h"
+#include "type.h"
#define raise_error(msg, xml, current) _ox_raise_error(msg, xml, current, __FILE__, __LINE__)
-#define MAX_TEXT_LEN 4096
+#define MAX_TEXT_LEN 4096
-#define SILENT 0
-#define TRACE 1
-#define DEBUG 2
-
-#define XSD_DATE 0x0001
-#define WITH_XML 0x0002
-#define WITH_INST 0x0004
-#define WITH_DTD 0x0008
-#define CIRCULAR 0x0010
-
-#define XSD_DATE_SET 0x0100
-#define WITH_XML_SET 0x0200
-#define WITH_INST_SET 0x0400
-#define WITH_DTD_SET 0x0800
-#define CIRCULAR_SET 0x1000
+#define SILENT 0
+#define TRACE 1
+#define DEBUG 2
+
+#define XSD_DATE 0x0001
+#define WITH_XML 0x0002
+#define WITH_INST 0x0004
+#define WITH_DTD 0x0008
+#define CIRCULAR 0x0010
+
+#define XSD_DATE_SET 0x0100
+#define WITH_XML_SET 0x0200
+#define WITH_INST_SET 0x0400
+#define WITH_DTD_SET 0x0800
+#define CIRCULAR_SET 0x1000
typedef enum {
- UseObj = 1,
- UseAttr = 2,
- UseAttrSet = 3,
- UseArray = 4,
- UseAMember = 5,
- UseHash = 6,
- UseHashKey = 7,
- UseHashVal = 8,
- UseRange = 9,
- UseRangeAttr= 10,
- UseRaw = 11,
+ UseObj = 1,
+ UseAttr = 2,
+ UseAttrSet = 3,
+ UseArray = 4,
+ UseAMember = 5,
+ UseHash = 6,
+ UseHashKey = 7,
+ UseHashVal = 8,
+ UseRange = 9,
+ UseRangeAttr = 10,
+ UseRaw = 11,
} Use;
typedef enum {
- StrictEffort = 's',
- TolerantEffort = 't',
- AutoEffort = 'a',
- NoEffort = 0,
+ StrictEffort = 's',
+ TolerantEffort = 't',
+ AutoEffort = 'a',
+ NoEffort = 0,
} Effort;
-typedef enum {
- Yes = 'y',
- No = 'n',
- NotSet = 0
-} YesNo;
+typedef enum { Yes = 'y', No = 'n', NotSet = 0 } YesNo;
-typedef enum {
- ObjMode = 'o',
- GenMode = 'g',
- LimMode = 'l',
- HashMode = 'h',
- HashNoAttrMode = 'n',
- NoMode = 0
-} LoadMode;
+typedef enum { ObjMode = 'o', GenMode = 'g', LimMode = 'l', HashMode = 'h', HashNoAttrMode = 'n', NoMode = 0 } LoadMode;
typedef enum {
- OffSkip = 'o',
- NoSkip = 'n',
- CrSkip = 'r',
- SpcSkip = 's',
+ OffSkip = 'o',
+ NoSkip = 'n',
+ CrSkip = 'r',
+ SpcSkip = 's',
} SkipMode;
-typedef struct _pInfo *PInfo;
+typedef struct _pInfo *PInfo;
typedef struct _parseCallbacks {
- void (*instruct)(PInfo pi, const char *target, Attr attrs, const char *content);
- void (*add_doctype)(PInfo pi, const char *docType);
- void (*add_comment)(PInfo pi, const char *comment);
- void (*add_cdata)(PInfo pi, const char *cdata, size_t len);
- void (*add_text)(PInfo pi, char *text, int closed);
- void (*add_element)(PInfo pi, const char *ename, Attr attrs, int hasChildren);
- void (*end_element)(PInfo pi, const char *ename);
- void (*finish)(PInfo pi);
-} *ParseCallbacks;
+ void (*instruct)(PInfo pi, const char *target, Attr attrs, const char *content);
+ void (*add_doctype)(PInfo pi, const char *docType);
+ void (*add_comment)(PInfo pi, const char *comment);
+ void (*add_cdata)(PInfo pi, const char *cdata, size_t len);
+ void (*add_text)(PInfo pi, char *text, int closed);
+ void (*add_element)(PInfo pi, const char *ename, Attr attrs, int hasChildren);
+ void (*end_element)(PInfo pi, const char *ename);
+ void (*finish)(PInfo pi);
+} * ParseCallbacks;
typedef struct _circArray {
- VALUE obj_array[1024];
- VALUE *objs;
- unsigned long size; /* allocated size or initial array size */
- unsigned long cnt;
-} *CircArray;
+ VALUE obj_array[1024];
+ VALUE *objs;
+ unsigned long size; /* allocated size or initial array size */
+ unsigned long cnt;
+} * CircArray;
typedef struct _options {
- char encoding[64]; /* encoding, stored in the option to avoid GC invalidation in default values */
- char margin[128]; /* left margin for dumping */
- int indent; /* indention for dump, default 2 */
- int trace; /* trace level */
- char margin_len; /* margin length */
- char with_dtd; /* YesNo */
- char with_xml; /* YesNo */
- char with_instruct; /* YesNo */
- char circular; /* YesNo */
- char xsd_date; /* YesNo */
- char mode; /* LoadMode */
- char effort; /* Effort */
- char sym_keys; /* symbolize keys */
- char skip; /* skip mode */
- char smart; /* YesNo sax smart mode */
- char convert_special;/* boolean true or false */
- char allow_invalid; /* YesNo */
- char inv_repl[12]; /* max 10 valid characters, first character is the length */
- char strip_ns[64]; /* namespace to strip, \0 is no-strip, \* is all, else only matches */
- struct _hints *html_hints; /* html hints */
- VALUE attr_key_mod;
- VALUE element_key_mod;
-#if HAS_ENCODING_SUPPORT
- rb_encoding *rb_enc;
-#elif HAS_PRIVATE_ENCODING
- VALUE rb_enc;
-#else
- void *rb_enc;
-#endif
-} *Options;
+ char encoding[64]; // encoding, stored in the option to avoid GC invalidation in default values
+ char margin[128]; // left margin for dumping
+ int indent; // indention for dump, default 2
+ int trace; // trace level
+ char margin_len; // margin length
+ char with_dtd; // YesNo
+ char with_xml; // YesNo
+ char with_instruct; // YesNo
+ char circular; // YesNo
+ char xsd_date; // YesNo
+ char mode; // LoadMode
+ char effort; // Effort
+ char sym_keys; // symbolize keys
+ char skip; // skip mode
+ char smart; // YesNo sax smart mode
+ char convert_special; // boolean true or false
+ char allow_invalid; // YesNo
+ char no_empty; // boolean - no empty elements when dumping
+ char with_cdata; // boolean - hash_load should include cdata
+ char inv_repl[12]; // max 10 valid characters, first character is the length
+ char strip_ns[64]; // namespace to strip, \0 is no-strip, \* is all, else only matches
+ struct _hints *html_hints; // html hints
+ VALUE attr_key_mod;
+ VALUE element_key_mod;
+ rb_encoding *rb_enc;
+} * Options;
-/* parse information structure */
+// parse information structure
struct _pInfo {
- struct _helperStack helpers;
- struct _err err;
- char *str; //buffer being read from
- char *end; // end of original string
- char *s; // current position in buffer
- VALUE obj;
- ParseCallbacks pcb;
- CircArray circ_array;
- unsigned long id; //set for text types when cirs_array is set
- Options options;
- char last; // last character read, rarely set
+ struct _helperStack helpers;
+ struct _err err;
+ char *str; // buffer being read from
+ char *end; // end of original string
+ char *s; // current position in buffer
+ VALUE obj;
+ ParseCallbacks pcb;
+ CircArray circ_array;
+ unsigned long id; // set for text types when cirs_array is set
+ Options options;
+ VALUE *marked;
+ int mark_size; // allocated size
+ int mark_cnt;
+ char last; // last character read, rarely set
};
-extern VALUE ox_parse(char *xml, size_t len, ParseCallbacks pcb, char **endp, Options options, Err err);
-extern void _ox_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line);
+extern VALUE ox_parse(char *xml, size_t len, ParseCallbacks pcb, char **endp, Options options, Err err);
+extern void _ox_raise_error(const char *msg, const char *xml, const char *current, const char *file, int line);
-extern void ox_sax_define(void);
+extern void ox_sax_define(void);
-extern char* ox_write_obj_to_str(VALUE obj, Options copts);
-extern void ox_write_obj_to_file(VALUE obj, const char *path, Options copts);
+extern char *ox_write_obj_to_str(VALUE obj, Options copts);
+extern void ox_write_obj_to_file(VALUE obj, const char *path, Options copts);
-extern struct _options ox_default_options;
-
-extern VALUE Ox;
-
-extern ID ox_abort_id;
-extern ID ox_at_column_id;
-extern ID ox_at_content_id;
-extern ID ox_at_id;
-extern ID ox_at_line_id;
-extern ID ox_at_pos_id;
-extern ID ox_at_value_id;
-extern ID ox_attr_id;
-extern ID ox_attr_value_id;
-extern ID ox_attrs_done_id;
-extern ID ox_attributes_id;
-extern ID ox_beg_id;
-extern ID ox_bigdecimal_id;
-extern ID ox_call_id;
-extern ID ox_cdata_id;
-extern ID ox_comment_id;
-extern ID ox_den_id;
-extern ID ox_doctype_id;
-extern ID ox_end_element_id;
-extern ID ox_end_id;
-extern ID ox_end_instruct_id;
-extern ID ox_error_id;
-extern ID ox_excl_id;
-extern ID ox_external_encoding_id;
-extern ID ox_fileno_id;
-extern ID ox_force_encoding_id;
-extern ID ox_inspect_id;
-extern ID ox_instruct_id;
-extern ID ox_jd_id;
-extern ID ox_keys_id;
-extern ID ox_local_id;
-extern ID ox_mesg_id;
-extern ID ox_message_id;
-extern ID ox_new_id;
-extern ID ox_nodes_id;
-extern ID ox_num_id;
-extern ID ox_parse_id;
-extern ID ox_pos_id;
-extern ID ox_read_id;
-extern ID ox_readpartial_id;
-extern ID ox_start_element_id;
-extern ID ox_string_id;
-extern ID ox_text_id;
-extern ID ox_to_c_id;
-extern ID ox_to_s_id;
-extern ID ox_to_sym_id;
-extern ID ox_tv_sec_id;
-extern ID ox_tv_nsec_id;
-extern ID ox_tv_usec_id;
-extern ID ox_value_id;
-
-#if HAS_ENCODING_SUPPORT
-extern rb_encoding *ox_utf8_encoding;
-#elif HAS_PRIVATE_ENCODING
-extern VALUE ox_utf8_encoding;
-#else
-extern void *ox_utf8_encoding;
-#endif
+extern struct _options ox_default_options;
+
+extern VALUE Ox;
+
+extern ID ox_abort_id;
+extern ID ox_at_column_id;
+extern ID ox_at_content_id;
+extern ID ox_at_id;
+extern ID ox_at_line_id;
+extern ID ox_at_pos_id;
+extern ID ox_at_value_id;
+extern ID ox_attr_id;
+extern ID ox_attr_value_id;
+extern ID ox_attrs_done_id;
+extern ID ox_attributes_id;
+extern ID ox_beg_id;
+extern ID ox_bigdecimal_id;
+extern ID ox_call_id;
+extern ID ox_cdata_id;
+extern ID ox_comment_id;
+extern ID ox_den_id;
+extern ID ox_doctype_id;
+extern ID ox_end_element_id;
+extern ID ox_end_id;
+extern ID ox_end_instruct_id;
+extern ID ox_error_id;
+extern ID ox_excl_id;
+extern ID ox_external_encoding_id;
+extern ID ox_fileno_id;
+extern ID ox_force_encoding_id;
+extern ID ox_inspect_id;
+extern ID ox_instruct_id;
+extern ID ox_jd_id;
+extern ID ox_keys_id;
+extern ID ox_local_id;
+extern ID ox_mesg_id;
+extern ID ox_message_id;
+extern ID ox_new_id;
+extern ID ox_nodes_id;
+extern ID ox_num_id;
+extern ID ox_parse_id;
+extern ID ox_pos_id;
+extern ID ox_read_id;
+extern ID ox_readpartial_id;
+extern ID ox_start_element_id;
+extern ID ox_string_id;
+extern ID ox_text_id;
+extern ID ox_to_c_id;
+extern ID ox_to_s_id;
+extern ID ox_to_sym_id;
+extern ID ox_tv_sec_id;
+extern ID ox_tv_nsec_id;
+extern ID ox_tv_usec_id;
+extern ID ox_value_id;
+
+extern rb_encoding *ox_utf8_encoding;
+
+extern VALUE ox_empty_string;
+extern VALUE ox_encoding_sym;
+extern VALUE ox_indent_sym;
+extern VALUE ox_size_sym;
+extern VALUE ox_standalone_sym;
+extern VALUE ox_sym_bank; // Array
+extern VALUE ox_version_sym;
+extern VALUE ox_zero_fixnum;
+
+extern VALUE ox_bigdecimal_class;
+extern VALUE ox_date_class;
+extern VALUE ox_stringio_class;
+extern VALUE ox_struct_class;
+extern VALUE ox_time_class;
+
+extern VALUE ox_document_clas;
+extern VALUE ox_element_clas;
+extern VALUE ox_instruct_clas;
+extern VALUE ox_bag_clas;
+extern VALUE ox_comment_clas;
+extern VALUE ox_raw_clas;
+extern VALUE ox_doctype_clas;
+extern VALUE ox_cdata_clas;
-extern VALUE ox_empty_string;
-extern VALUE ox_encoding_sym;
-extern VALUE ox_indent_sym;
-extern VALUE ox_size_sym;
-extern VALUE ox_standalone_sym;
-extern VALUE ox_sym_bank; // Array
-extern VALUE ox_version_sym;
-extern VALUE ox_zero_fixnum;
-
-extern VALUE ox_bigdecimal_class;
-extern VALUE ox_date_class;
-extern VALUE ox_stringio_class;
-extern VALUE ox_struct_class;
-extern VALUE ox_time_class;
-
-extern VALUE ox_document_clas;
-extern VALUE ox_element_clas;
-extern VALUE ox_instruct_clas;
-extern VALUE ox_bag_clas;
-extern VALUE ox_comment_clas;
-extern VALUE ox_raw_clas;
-extern VALUE ox_doctype_clas;
-extern VALUE ox_cdata_clas;
-
-extern Cache ox_symbol_cache;
-extern Cache ox_class_cache;
-extern Cache ox_attr_cache;
+extern SlotCache ox_class_cache;
-extern void ox_init_builder(VALUE ox);
+extern void ox_init_builder(VALUE ox);
#if defined(__cplusplus)
#if 0
{ /* satisfy cc-mode */
#endif
-} /* extern "C" { */
+} /* extern "C" { */
#endif
#endif /* OX_H */
diff -Nru ruby-ox-2.11.0/ext/ox/parse.c ruby-ox-2.14.9/ext/ox/parse.c
--- ruby-ox-2.11.0/ext/ox/parse.c 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/parse.c 2022-02-10 23:24:14.000000000 +0000
@@ -5,13 +5,16 @@
#include
#include
+#include
#include
#include
+#include
#include "ruby.h"
#include "ox.h"
#include "err.h"
#include "attr.h"
+#include "intern.h"
#include "helper.h"
#include "special.h"
@@ -93,6 +96,30 @@
}
}
+static void fix_newlines(char *buf) {
+#if HAVE_INDEX
+ if (NULL != index(buf, '\r')) {
+#endif
+ char *s = buf;
+ char *d = buf;
+
+ for (; '\0' != *s; s++) {
+ if ('\r' == *s) {
+ if ('\n' == *(s + 1)) {
+ continue;
+ }
+ *d = '\n';
+ } else if (d < s) {
+ *d = *s;
+ }
+ d++;
+ }
+ *d = '\0';
+#if HAVE_INDEX
+ }
+#endif
+}
+
static void
mark_pi_cb(void *ptr) {
if (NULL != ptr) {
@@ -121,7 +148,7 @@
if (DEBUG <= options->trace) {
printf("Parsing xml:\n%s\n", xml);
}
- /* initialize parse info */
+ // initialize parse info
helper_stack_init(&pi.helpers);
// Protect against GC
wrap = Data_Wrap_Struct(rb_cObject, mark_pi_cb, NULL, &pi);
@@ -134,8 +161,11 @@
pi.obj = Qnil;
pi.circ_array = 0;
pi.options = options;
+ pi.marked = NULL;
+ pi.mark_size = 0;
+ pi.mark_cnt = 0;
while (1) {
- next_non_white(&pi); /* skip white space */
+ next_non_white(&pi); // skip white space
if ('\0' == *pi.s) {
break;
}
@@ -143,31 +173,31 @@
*endp = pi.s;
break;
}
- if ('<' != *pi.s) { /* all top level entities start with < */
+ if ('<' != *pi.s) { // all top level entities start with <
set_error(err, "invalid format, expected <", pi.str, pi.s);
helper_stack_cleanup(&pi.helpers);
return Qnil;
}
- pi.s++; /* past < */
+ pi.s++; // past <
switch (*pi.s) {
- case '?': /* processing instruction */
+ case '?': // processing instruction
pi.s++;
read_instruction(&pi);
break;
- case '!': /* comment or doctype */
+ case '!': // comment or doctype
pi.s++;
if ('\0' == *pi.s) {
set_error(err, "invalid format, DOCTYPE or comment not terminated", pi.str, pi.s);
helper_stack_cleanup(&pi.helpers);
return Qnil;
} else if ('-' == *pi.s) {
- pi.s++; /* skip - */
+ pi.s++; // skip -
if ('-' != *pi.s) {
set_error(err, "invalid format, bad comment format", pi.str, pi.s);
helper_stack_cleanup(&pi.helpers);
return Qnil;
} else {
- pi.s++; /* skip second - */
+ pi.s++; // skip second -
read_comment(&pi);
}
} else if ((TolerantEffort == options->effort) ? 0 == strncasecmp("DOCTYPE", pi.s, 7) : 0 == strncmp("DOCTYPE", pi.s, 7)) {
@@ -208,32 +238,11 @@
return pi.obj;
}
-static char*
-gather_content(const char *src, char *content, size_t len) {
- for (; 0 < len; src++, content++, len--) {
- switch (*src) {
- case '?':
- if ('>' == *(src + 1)) {
- *content = '\0';
- return (char*)(src + 1);
- }
- *content = *src;
- break;
- case '\0':
- return 0;
- default:
- *content = *src;
- break;
- }
- }
- return 0;
-}
-
-/* Entered after the "" sequence. Ready to read the rest.
- */
+// Entered after the "" sequence. Ready to read the rest.
static void
read_instruction(PInfo pi) {
- char content[1024];
+ char content[256];
+ char *content_ptr;
struct _attrStack attrs;
char *attr_name;
char *attr_value;
@@ -241,7 +250,8 @@
char *end;
char c;
char *cend;
- int attrs_ok = 1;
+ size_t size;
+ bool attrs_ok = true;
*content = '\0';
attr_stack_init(&attrs);
@@ -249,13 +259,36 @@
return;
}
end = pi->s;
- if (0 == (cend = gather_content(pi->s, content, sizeof(content) - 1))) {
- set_error(&pi->err, "processing instruction content too large or not terminated", pi->str, pi->s);
- return;
+ for (; true; pi->s++) {
+ switch (*pi->s) {
+ case '?':
+ if ('>' == *(pi->s + 1)) {
+ pi->s++;
+ goto DONE;
+ }
+ break;
+ case '\0':
+ set_error(&pi->err, "processing instruction not terminated", pi->str, pi->s);
+ return;
+ default:
+ break;
+ }
+ }
+DONE:
+ cend = pi->s;
+ size = cend - end - 1;
+ pi->s = end;
+ if (size < sizeof(content)) {
+ content_ptr = content;
+ } else {
+ content_ptr = ALLOC_N(char, size + 1);
}
+ memcpy(content_ptr, end, size);
+ content_ptr[size] = '\0';
+
next_non_white(pi);
c = *pi->s;
- *end = '\0'; /* terminate name */
+ *end = '\0'; // terminate name
if ('?' != c) {
while ('?' != c) {
pi->last = 0;
@@ -272,11 +305,11 @@
end = pi->s;
next_non_white(pi);
if ('=' != *pi->s++) {
- attrs_ok = 0;
+ attrs_ok = false;
break;
}
- *end = '\0'; /* terminate name */
- /* read value */
+ *end = '\0'; // terminate name
+ // read value
next_non_white(pi);
if (0 == (attr_value = read_quoted_value(pi))) {
attr_stack_cleanup(&attrs);
@@ -309,10 +342,13 @@
if (attrs_ok) {
pi->pcb->instruct(pi, target, attrs.head, 0);
} else {
- pi->pcb->instruct(pi, target, attrs.head, content);
+ pi->pcb->instruct(pi, target, attrs.head, content_ptr);
}
}
attr_stack_cleanup(&attrs);
+ if (content_ptr != content) {
+ xfree(content_ptr);
+ }
}
static void
@@ -355,15 +391,14 @@
}
}
-/* Entered after the "s;
+ doctype = pi->s;
read_delimited(pi, '>');
if (err_has(&pi->err)) {
return;
@@ -372,12 +407,12 @@
*pi->s = '\0';
pi->s++;
if (0 != pi->pcb->add_doctype) {
- pi->pcb->add_doctype(pi, docType);
+ fix_newlines(doctype);
+ pi->pcb->add_doctype(pi, doctype);
}
}
-/* Entered after "
+ { "ucirc", 251 }, // latin small letter u with circumflex
+ { "ugrave", 249 }, // latin small letter u with grave
+ { "uml", 168 }, // diaeresis = spacing diaeresis
+ { "upsih", 978 }, // greek upsilon with hook symbol
+ { "upsilon", 965 }, // greek small letter upsilon
+ { "uuml", 252 }, // latin small letter u with diaeresis
+ { "weierp", 8472 }, // script capital P = power set
+ { "xi", 958 }, // greek small letter xi, U+03BE ISOgrk3
+ { "yacute", 253 }, // latin small letter y with acute
+ { "yen", 165 }, // yen sign = yuan sign, U+00A5 ISOnum
+ { "yuml", 255 }, // latin small letter y with diaeresis
+ { "zeta", 950 }, // greek small letter zeta, U+03B6 ISOgrk3
+ { "zwj", 8205 }, // zero width joiner, U+200D NEW RFC 2070
+ { "zwnj", 8204 }, // zero width non-joiner
+ { NULL, 0 },
+};
+
+static uint64_t
+calc_hash(const char *key) {
+ uint64_t h = 0;
+
+ if (NULL != key) {
+ const uint8_t *k = (const uint8_t*)key;
+
+ for (; 0 != *k; k++) {
+ // narrow to most used range of 0x4D (77) in size
+ h = 77 * h + ((*k | 0x20) - 0x2D);
+ }
+ }
+ return h;
+}
+
+static Slot*
+get_bucketp(uint64_t h) {
+ return entity_cache.buckets + (BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
+}
+
+static void
+cache_set(Slot s) {
+ int64_t h = calc_hash(s->key);
+ Slot *bucket = get_bucketp(h);
+
+ s->hash = h;
+ s->next = *bucket;
+ *bucket = s;
+}
+
+static Slot
+cache_get(const char *key) {
+ int64_t h = calc_hash(key);
+ Slot *bucket = get_bucketp(h);
+ Slot s;
+
+ for (s = *bucket; NULL != s; s = s->next) {
+ if (h == (int64_t)s->hash && 0 == strcasecmp(s->key, key)) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+static void
+cache_init() {
+ Slot e = entities;
+
+ memset(&entity_cache, 0, sizeof(struct _cache));
+ for (; NULL != e->key; e++) {
+ cache_set(e);
+ }
+ inited = true;
+}
+
+char*
+ox_entity_lookup(char *text, const char *key) {
+ Slot s = entities;
+
+ if (!inited) {
+ cache_init();
+ }
+ if (NULL == (s = cache_get(key))) {
+ return NULL;
+ }
+ return ox_ucs_to_utf8_chars(text, s->code);
+}
diff -Nru ruby-ox-2.11.0/ext/ox/special.h ruby-ox-2.14.9/ext/ox/special.h
--- ruby-ox-2.11.0/ext/ox/special.h 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/ext/ox/special.h 2022-02-10 23:24:14.000000000 +0000
@@ -9,5 +9,6 @@
#include
extern char* ox_ucs_to_utf8_chars(char *text, uint64_t u);
+extern char* ox_entity_lookup(char *text, const char *key);
#endif /* OX_SPECIAL_H */
diff -Nru ruby-ox-2.11.0/Gemfile ruby-ox-2.14.9/Gemfile
--- ruby-ox-2.11.0/Gemfile 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/Gemfile 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,3 @@
+source "https://rubygems.org"
+
+gemspec
diff -Nru ruby-ox-2.11.0/.github/FUNDING.yml ruby-ox-2.14.9/.github/FUNDING.yml
--- ruby-ox-2.11.0/.github/FUNDING.yml 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/.github/FUNDING.yml 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: rubygems/ox
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with a single custom sponsorship URL
diff -Nru ruby-ox-2.11.0/.github/workflows/CI.yml ruby-ox-2.14.9/.github/workflows/CI.yml
--- ruby-ox-2.11.0/.github/workflows/CI.yml 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/.github/workflows/CI.yml 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,43 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - develop
+ - master
+ pull_request:
+
+jobs:
+ Build:
+ strategy:
+ fail-fast: true
+ matrix:
+ # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
+ ruby:
+ - head
+ - '3.0'
+ - '2.7'
+ - '2.6'
+ - '2.5'
+ os:
+ - ubuntu
+ - macos
+ - windows
+
+ runs-on: ${{ matrix.os }}-latest
+ continue-on-error: ${{ matrix.ruby == '3.0' || matrix.ruby == 'head'}}
+ name: Ruby ${{ matrix.ruby }} (${{ matrix.os }})
+ steps:
+ - uses: actions/checkout@v2
+ - uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby }}
+ - name: build
+ run: |
+ cd ./ext/ox
+ ruby extconf.rb
+ make
+ - name: test
+ run: |
+ cd test
+ ./tests.rb
diff -Nru ruby-ox-2.11.0/.gitignore ruby-ox-2.14.9/.gitignore
--- ruby-ox-2.11.0/.gitignore 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/.gitignore 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,15 @@
+ox-*.gem
+.DS_Store
+\#*\#
+.\#*
+*~
+*.o
+*.so
+Makefile
+*.bundle
+.rbenv-version
+.ruby-version
+*.rbc
+.rbx
+.yardoc
+doc
diff -Nru ruby-ox-2.11.0/lib/ox/element.rb ruby-ox-2.14.9/lib/ox/element.rb
--- ruby-ox-2.11.0/lib/ox/element.rb 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/lib/ox/element.rb 2022-02-10 23:24:14.000000000 +0000
@@ -44,6 +44,7 @@
@nodes = []
end
alias name value
+ alias name= value=
# Returns the Element's nodes array. These are the sub-elements of this
# Element.
@@ -114,17 +115,8 @@
# matching name will be yielded to. If the cond is a Hash then the
# keys-value pairs in the cond must match the child attribute values with
# the same keys. Any other cond type will yield to nothing.
- def each(cond=nil)
- if cond.nil?
- nodes.each { |n| yield(n) }
- else
- cond = cond.to_s if cond.is_a?(Symbol)
- if cond.is_a?(String)
- nodes.each { |n| yield(n) if n.is_a?(Element) && cond == n.name }
- elsif cond.is_a?(Hash)
- nodes.each { |n| yield(n) if n.is_a?(Element) && n.attr_match(cond) }
- end
- end
+ def each(cond=nil, &block)
+ build_enumerator(cond).each(&block)
end
# Returns an array of Nodes or Strings that correspond to the locations
@@ -412,6 +404,24 @@
private
+ # Builds an enumerator for use in `#each` call
+ #
+ # - +cond+ [Hash, String, nil] an element filter
+ def build_enumerator(cond)
+ if cond.nil?
+ nodes.each
+ else
+ cond = cond.to_s if cond.is_a?(Symbol)
+ Enumerator.new do |yielder|
+ if cond.is_a?(String)
+ nodes.each { |n| yielder.yield(n) if n.is_a?(Element) && cond == n.name }
+ elsif cond.is_a?(Hash)
+ nodes.each { |n| yielder.yield(n) if n.is_a?(Element) && n.attr_match(cond) }
+ end
+ end
+ end
+ end
+
# Removes recursively children for nodes and sub_nodes
#
# - +found+ [Array] An array of Ox::Element
diff -Nru ruby-ox-2.11.0/lib/ox/hasattrs.rb ruby-ox-2.14.9/lib/ox/hasattrs.rb
--- ruby-ox-2.11.0/lib/ox/hasattrs.rb 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/lib/ox/hasattrs.rb 2022-02-10 23:24:14.000000000 +0000
@@ -13,7 +13,7 @@
@attributes = { } if !instance_variable_defined?(:@attributes) or @attributes.nil?
@attributes
end
-
+
# Returns the value of an attribute.
# - +attr+ [Symbol|String] attribute name or key to return the value for
def [](attr)
@@ -27,9 +27,16 @@
def []=(attr, value)
raise "argument to [] must be a Symbol or a String." unless attr.is_a?(Symbol) or attr.is_a?(String)
@attributes = { } if !instance_variable_defined?(:@attributes) or @attributes.nil?
+ a_str = attr.to_s
+ a_sym = attr.to_sym
+ if @attributes.has_key?(a_str)
+ attr = a_str
+ elsif @attributes.has_key?(a_sym)
+ attr = a_sym
+ end
@attributes[attr] = value.to_s
end
-
+
# Handles the 'easy' API that allows navigating a simple XML by
# referencing attributes by name.
# - +id+ [Symbol] element or attribute name
diff -Nru ruby-ox-2.11.0/lib/ox/version.rb ruby-ox-2.14.9/lib/ox/version.rb
--- ruby-ox-2.11.0/lib/ox/version.rb 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/lib/ox/version.rb 2022-02-10 23:24:14.000000000 +0000
@@ -1,5 +1,5 @@
module Ox
# Current version of the module.
- VERSION = '2.11.0'
+ VERSION = '2.14.9'
end
diff -Nru ruby-ox-2.11.0/misc/notes ruby-ox-2.14.9/misc/notes
--- ruby-ox-2.11.0/misc/notes 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/misc/notes 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,46 @@
+;; -*- mode: outline; outline-regexp: " *[-\+]"; indent-tabs-mode: nil -*-
+
+^c^d hide subtree
+^c^s show subtree
+
+- Element
+ - each
+ - locate with pattern)
+
+- rdoc
+ - --coverage-report for coverage (does not generate docs)
+ - rdoc --main README.md --title Ox --exclude extconf.rb lib ext/ox README.md
+
+- todo
+
+ - taint deprecated
+ - implement a marked array or linked list in pinfo
+ - not super efficient but it should work
+ - search from tail
+ - grow if needed. start with none and grow as needed in blocks of ??
+
+ - HTML (5) builder
+ - should be correct for all elements
+ - pedantic
+
+ - add hints and overaly to parse.c
+
+ - continuous sax parser
+ - parse input stream and continue waiting for next one
+
+
+
+
+
+
+- add UTF-16 support
+U+0000..U+D7FF, U+E000..U+EFFF 00000xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx
+U+10000..U+10FFFF uuuuuxxxxxxyyyyyyyyyy 110110wwwwxxxxxx 110111yyyyyyyyyy (where uuuuu = wwww + 1)
+
+
+- hints
+xmllint --valid --noout --dtdvalid ../misc/ox.dtd --debug sample.xml
+
+#rsync -vru --exclude ".*" --exclude "*~" --exclude "#*" --exclude misc --exclude "*.o" ~/code/ox/ ~/git/ox
+rsync -vrI --exclude ".*" --exclude "*~" --exclude "#*" --exclude misc --exclude "*.o" ~/code/ox/ ~/git/ox
+rsync -vrI --exclude ".*" --exclude "*~" --exclude "#*" --exclude misc --exclude "*.o" ~/git/ox/ ~/code/ox
diff -Nru ruby-ox-2.11.0/misc/ox.dtd ruby-ox-2.14.9/misc/ox.dtd
--- ruby-ox-2.11.0/misc/ox.dtd 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/misc/ox.dtd 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -Nru ruby-ox-2.11.0/misc/sample.xml ruby-ox-2.14.9/misc/sample.xml
--- ruby-ox-2.11.0/misc/sample.xml 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/misc/sample.xml 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,1542 @@
+
+
+
+
+ ..
+ 1309872137.000000
+ 1309872137.000000
+ 510
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ doc
+ 1302099563.000000
+ 1302099563.000000
+ 68
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+
+ ext
+ 1302099552.000000
+ 1302099552.000000
+ 102
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ ox
+ 1309874864.000000
+ 1309874864.000000
+ 1020
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ base64.c
+ 1302604726.000000
+ 1302604726.000000
+ 4536
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ base64.h
+ 1302604726.000000
+ 1302604726.000000
+ 1919
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ base64.o
+ 1309873370.000000
+ 1309873370.000000
+ 6100
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache.c
+ 1302604726.000000
+ 1302604726.000000
+ 5211
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache.h
+ 1302604726.000000
+ 1302604726.000000
+ 1877
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache.o
+ 1309873370.000000
+ 1309873370.000000
+ 8764
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache16.z
+ 1302099552.000000
+ 1302099552.000000
+ 2596
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache8.c
+ 1302604726.000000
+ 1302604726.000000
+ 1844
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache8.h
+ 1302604726.000000
+ 1302604726.000000
+ 1915
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache8.o
+ 1309873370.000000
+ 1309873370.000000
+ 11272
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache8_test.c
+ 1302604726.000000
+ 1302604726.000000
+ 2542
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache8_test.o
+ 1309873370.000000
+ 1309873370.000000
+ 3952
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache_test.c
+ 1302604726.000000
+ 1302604726.000000
+ 2493
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cache_test.o
+ 1309873370.000000
+ 1309873370.000000
+ 4856
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ dump.c
+ 1309866269.000000
+ 1309866269.000000
+ 26622
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ dump.o
+ 1309873371.000000
+ 1309873371.000000
+ 63344
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ extconf.rb
+ 1302604726.000000
+ 1302604726.000000
+ 155
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ gen_load.c
+ 1302604726.000000
+ 1302604726.000000
+ 5948
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ gen_load.o
+ 1309873371.000000
+ 1309873371.000000
+ 14520
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ Makefile
+ 1309515102.000000
+ 1309515102.000000
+ 5423
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ obj_load.c
+ 1309874863.000000
+ 1309874863.000000
+ 23657
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ obj_load.o
+ 1309874864.000000
+ 1309874864.000000
+ 42948
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ ox.bundle
+ 1309874864.000000
+ 1309874864.000000
+ 75208
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ ox.c
+ 1309873745.000000
+ 1309873745.000000
+ 15782
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ ox.h
+ 1309872911.000000
+ 1309872911.000000
+ 6158
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ ox.o
+ 1309874178.000000
+ 1309874178.000000
+ 38320
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ parse.c
+ 1309872884.000000
+ 1309872884.000000
+ 16499
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ parse.o
+ 1309873372.000000
+ 1309873372.000000
+ 28616
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+
+
+
+
+ lib
+ 1302604726.000000
+ 1302604726.000000
+ 136
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ ox
+ 1309866905.000000
+ 1309866905.000000
+ 340
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ bag.rb
+ 1309866905.000000
+ 1309866905.000000
+ 2924
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ bag.rb~
+ 1309866847.000000
+ 1309866847.000000
+ 2924
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ cdata.rb
+ 1302604726.000000
+ 1302604726.000000
+ 233
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ comment.rb
+ 1302604726.000000
+ 1302604726.000000
+ 297
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ doctype.rb
+ 1302604726.000000
+ 1302604726.000000
+ 291
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ document.rb
+ 1302604726.000000
+ 1302604726.000000
+ 792
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ element.rb
+ 1309513804.000000
+ 1309513804.000000
+ 2273
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ node.rb
+ 1302604726.000000
+ 1302604726.000000
+ 645
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+
+
+ ox.rb
+ 1302604726.000000
+ 1302604726.000000
+ 3168
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+
+
+ LICENSE
+ 1302604726.000000
+ 1302604726.000000
+ 1488
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ misc
+ 1302099562.000000
+ 1302099562.000000
+ 102
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ load_cfg.rb
+ 1302099562.000000
+ 1302099562.000000
+ 1273
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+
+
+ notes
+ 1309872137.000000
+ 1309872137.000000
+ 776
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ ox-1.0.2.gem
+ 1309515131.000000
+ 1309515131.000000
+ 40448
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ ox.gemspec
+ 1309866269.000000
+ 1309866269.000000
+ 747
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ README.rdoc
+ 1309513804.000000
+ 1309513804.000000
+ 4633
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ test
+ 1309873425.000000
+ 1309873425.000000
+ 680
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ cache16_test.rb
+ 1302604726.000000
+ 1302604726.000000
+ 201
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ cache8_test.rb
+ 1302604726.000000
+ 1302604726.000000
+ 200
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ cache_test.rb
+ 1302604726.000000
+ 1302604726.000000
+ 199
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ files.rb
+ 1302604726.000000
+ 1302604726.000000
+ 712
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ func.rb
+ 1309873425.000000
+ 1309873425.000000
+ 5408
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ gen_sample.rb
+ 1302604726.000000
+ 1302604726.000000
+ 330
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ obj_sample.rb
+ 1302604726.000000
+ 1302604726.000000
+ 326
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ ox
+ 1302604726.000000
+ 1302604726.000000
+ 476
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ change.rb
+ 1302604726.000000
+ 1302604726.000000
+ 330
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ dir.rb
+ 1302604726.000000
+ 1302604726.000000
+ 255
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ doc.rb
+ 1302604726.000000
+ 1302604726.000000
+ 831
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ file.rb
+ 1302604726.000000
+ 1302604726.000000
+ 1085
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ group.rb
+ 1302604726.000000
+ 1302604726.000000
+ 239
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ hasprops.rb
+ 1302604726.000000
+ 1302604726.000000
+ 344
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ layer.rb
+ 1302604726.000000
+ 1302604726.000000
+ 189
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ line.rb
+ 1302604726.000000
+ 1302604726.000000
+ 369
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ oval.rb
+ 1302604726.000000
+ 1302604726.000000
+ 166
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ rect.rb
+ 1302604726.000000
+ 1302604726.000000
+ 166
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ shape.rb
+ 1302604726.000000
+ 1302604726.000000
+ 658
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ text.rb
+ 1302604726.000000
+ 1302604726.000000
+ 470
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+
+
+ perf_gen.rb
+ 1302604726.000000
+ 1302604726.000000
+ 5216
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ perf_mars.rb
+ 1302604726.000000
+ 1302604726.000000
+ 2423
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ perf_obj.rb
+ 1302604726.000000
+ 1302604726.000000
+ 5398
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ perf_pod.rb
+ 1302604726.000000
+ 1302604726.000000
+ 2373
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ perf_write.rb
+ 1302604726.000000
+ 1302604726.000000
+ 1675
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ Sample.graffle
+ 1302604726.000000
+ 1302604726.000000
+ 46577
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ sample.marshal
+ 1309874878.000000
+ 1309874878.000000
+ 12412
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ sample.rb
+ 1302604726.000000
+ 1302604726.000000
+ 1488
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ sample.xml
+ 1309874878.000000
+ 1309874878.000000
+ 28053
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ test.rb
+ 1302604726.000000
+ 1302604726.000000
+ 1225
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+
+
+ xml
+ 1302604726.000000
+ 1302604726.000000
+ 510
+ ohler
+ staff
+
+ user
+ rwx
+ group
+ r-x
+ other
+ r-x
+
+
+
+ attr.xml
+ 1302604726.000000
+ 1302604726.000000
+ 40
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ comment.xml
+ 1302604726.000000
+ 1302604726.000000
+ 44
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ empty.xml
+ 1302604726.000000
+ 1302604726.000000
+ 23
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ env.xml
+ 1302604726.000000
+ 1302604726.000000
+ 1097
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ loner.xml
+ 1302604726.000000
+ 1302604726.000000
+ 30
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ mixed.xml
+ 1302604726.000000
+ 1302604726.000000
+ 120
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ nest.xml
+ 1302604726.000000
+ 1302604726.000000
+ 183
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ sample.xml
+ 1302604726.000000
+ 1302604726.000000
+ 68
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ wFractal.xml
+ 1302604726.000000
+ 1302604726.000000
+ 29077
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ wFractal_report.xml
+ 1302604726.000000
+ 1302604726.000000
+ 44231
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ wr.xml
+ 1302604726.000000
+ 1302604726.000000
+ 15822
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ zoo.xml
+ 1302604726.000000
+ 1302604726.000000
+ 420
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+ zoo2.xml
+ 1302604726.000000
+ 1302604726.000000
+ 1138
+ ohler
+ staff
+
+ user
+ rw-
+ group
+ r--
+ other
+ r--
+
+
+
+
+
+
diff -Nru ruby-ox-2.11.0/misc/simple.xml ruby-ox-2.14.9/misc/simple.xml
--- ruby-ox-2.11.0/misc/simple.xml 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/misc/simple.xml 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,7 @@
+
+
+
+
+ 510
+ xyz
+
Binary files /tmp/tmpq8_bweir/gytkTYhgct/ruby-ox-2.11.0/ox.gemspec and /tmp/tmpq8_bweir/31KX50TWgi/ruby-ox-2.14.9/ox.gemspec differ
diff -Nru ruby-ox-2.11.0/README.md ruby-ox-2.14.9/README.md
--- ruby-ox-2.11.0/README.md 2019-06-20 20:06:23.000000000 +0000
+++ ruby-ox-2.14.9/README.md 2022-02-10 23:24:14.000000000 +0000
@@ -1,7 +1,7 @@
# Ox gem
A fast XML parser and Object marshaller as a Ruby gem.
-[![Build Status](https://secure.travis-ci.org/ohler55/ox.svg?branch=master)](http://travis-ci.org/ohler55/ox) [![TideLift](https://tidelift.com/badges/github/ohler55/ox)](https://tidelift.com/subscription/pkg/rubygems-ox?utm_source=rubygems-ox&utm_medium=referral&utm_campaign=readme)
+[![Build Status](https://img.shields.io/github/workflow/status/ohler55/ox/CI?logo=github)](https://github.com/ohler55/ox/actions/workflows/CI.yml)
## Installation
gem install ox
@@ -16,13 +16,9 @@
*RubyGems* *repo*: https://rubygems.org/gems/ox
-## Follow @oxgem on Twitter
-
-[Follow @peterohler on Twitter](http://twitter.com/#!/peterohler) for announcements and news about the Ox gem.
-
## Support
-[Get supported Ox with a Tidelift Subscription.](https://tidelift.com/subscription/pkg/rubygems-ox?utm_source=rubygems-ox&utm_medium=referral&utm_campaign=readme)
+[Get supported Ox with a Tidelift Subscription.](https://tidelift.com/subscription/pkg/rubygems-ox?utm_source=rubygems-ox&utm_medium=referral&utm_campaign=readme) Security updates are [supported](https://tidelift.com/security).
## Links of Interest
@@ -78,7 +74,7 @@
callbacks that are of interest to the caller. (See the perf_sax.rb file for an
example.)
-Ox is compatible with Ruby 1.8.7, 1.9.3, 2.1.2, 2.2.0 and RBX.
+Ox is compatible with Ruby 2.3, 2.4, 2.5, 2.6, 2.7, 3.0.
### Object Dump Sample:
@@ -108,7 +104,13 @@
```ruby
require 'ox'
-doc = Ox::Document.new(:version => '1.0')
+doc = Ox::Document.new
+
+instruct = Ox::Instruct.new(:xml)
+instruct[:version] = '1.0'
+instruct[:encoding] = 'UTF-8'
+instruct[:standalone] = 'yes'
+doc << instruct
top = Ox::Element.new('top')
top[:name] = 'sample'
@@ -120,20 +122,31 @@
bot = Ox::Element.new('bottom')
bot[:name] = 'third'
+bot << 'text at bottom'
mid << bot
+other_elements = Ox::Element.new('otherElements')
+other_elements << Ox::CData.new('John Smith')
+other_elements << Ox::Comment.new('Director\'s commentary')
+# other_elements << Ox::DocType.new('content')
+other_elements << Ox::Raw.new('Be carefull with this! Direct inject into XML!')
+top << other_elements
+
+
xml = Ox.dump(doc)
# xml =
+#
#
#
-#
+# text at bottom
#
+#
+# John Smith]]>
+#
+# Be carefull with this! Direct inject into XML!
+#
#
-
-doc2 = Ox.parse(xml)
-puts "Same? #{doc == doc2}"
-# true
```
### HTML Parsing:
@@ -306,3 +319,33 @@
an Object that refers back to the first Object. When this option is used an
Object ID is added to each XML Object element as the value of the 'a'
attribute.
+
+## Contributors
+
+### Code Contributors
+
+This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
+
+
+### Financial Contributors
+
+Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/ohler/contribute)]
+
+#### Individuals
+
+
+
+#### Organizations
+
+Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/ohler/contribute)]
+
+
+
+
+
+
+
+
+
+
+
diff -Nru ruby-ox-2.11.0/SECURITY.md ruby-ox-2.14.9/SECURITY.md
--- ruby-ox-2.11.0/SECURITY.md 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/SECURITY.md 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,14 @@
+# Security Policy
+
+## Supported Versions
+
+Use this section to tell people about which versions of your project are
+currently being supported with security updates.
+
+| Version | Supported |
+| ------- | ------------------ |
+| 2.11.0 | :white_check_mark: |
+
+## Reporting a Vulnerability
+
+Report a vulnerability by creating an issue.
diff -Nru ruby-ox-2.11.0/test/c/cache16.c ruby-ox-2.14.9/test/c/cache16.c
--- ruby-ox-2.11.0/test/c/cache16.c 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/c/cache16.c 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,100 @@
+
+#include
+#include
+#include
+#include
+#include
+
+#include "cache16.h"
+
+struct _Cache16 {
+ VALUE value;
+ struct _Cache16 *slots[16];
+};
+
+static void slot_print(Cache16 cache, unsigned int depth);
+static void v2s(VALUE v, char *buf, unsigned long len);
+
+void
+ox_cache16_new(Cache16 *cache) {
+ Cache16 *cp;
+ int i;
+
+ if (0 == (*cache = (Cache16)malloc(sizeof(struct _Cache16)))) {
+ rb_raise(rb_eStandardError, "not enough memory\n");
+ }
+ (*cache)->value = Qundef;
+ for (i = 16, cp = (*cache)->slots; 0 < i; i--, cp++) {
+ *cp = 0;
+ }
+}
+
+VALUE
+ox_cache16_get(Cache16 cache, const char *key, VALUE **slot) {
+ unsigned char *k = (unsigned char*)key;
+ Cache16 *cp;
+
+ for (; '\0' != *k; k++) {
+ cp = cache->slots + (unsigned int)(*k >> 4); // upper 4 bits
+ if (0 == *cp) {
+ ox_cache16_new(cp);
+ }
+ cache = *cp;
+ cp = cache->slots + (unsigned int)(*k & 0x0F); // lower 4 bits
+ if (0 == *cp) {
+ ox_cache16_new(cp);
+ }
+ cache = *cp;
+ }
+ *slot = &cache->value;
+
+ return cache->value;
+}
+
+void
+ox_cache16_print(Cache16 cache) {
+ //printf("-------------------------------------------\n");
+ slot_print(cache, 0);
+}
+
+static void
+slot_print(Cache16 c, unsigned int depth) {
+ char indent[256];
+ Cache16 *cp;
+ unsigned int i;
+
+ if (sizeof(indent) - 1 < depth) {
+ depth = ((int)sizeof(indent) - 1);
+ }
+ memset(indent, ' ', depth);
+ indent[depth] = '\0';
+ for (i = 0, cp = c->slots; i < 16; i++, cp++) {
+ if (0 == *cp) {
+ //printf("%s%02u:\n", indent, i);
+ } else {
+ if (Qundef == (*cp)->value) {
+ printf("%s%02u:\n", indent, i);
+ } else {
+ char value[1024];
+ const char *clas;
+
+ if (Qundef == (*cp)->value) {
+ strcpy(value, "undefined");
+ clas = "";
+ } else {
+ v2s((*cp)->value, value, sizeof(value));
+ clas = rb_class2name(rb_obj_class((*cp)->value));
+ }
+ printf("%s%02u: %s (%s)\n", indent, i, value, clas);
+ }
+ slot_print(*cp, depth + 2);
+ }
+ }
+}
+
+static void
+v2s(VALUE v, char *buf, unsigned long len) {
+ VALUE rs = rb_funcall2(v, rb_intern("to_s"), 0, 0);
+
+ snprintf(buf, len, "%s", StringValuePtr(rs));
+}
diff -Nru ruby-ox-2.11.0/test/c/cache8_test.c ruby-ox-2.14.9/test/c/cache8_test.c
--- ruby-ox-2.11.0/test/c/cache8_test.c 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/c/cache8_test.c 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,45 @@
+/* cache8_test.c
+ * Copyright (c) 2011, Peter Ohler
+ * All rights reserved.
+ */
+
+#include
+#include "cache8.h"
+
+static slot_t data[] = {
+ 0x000000A0A0A0A0A0ULL,
+ 0x0000000000ABCDEFULL,
+ 0x0123456789ABCDEFULL,
+ 0x0000000000000001ULL,
+ 0x0000000000000002ULL,
+ 0x0000000000000003ULL,
+ 0x0000000000000004ULL,
+ 0
+};
+
+void
+ox_cache8_test() {
+ Cache8 c;
+ slot_t v;
+ slot_t *d;
+ slot_t cnt = 1;
+ slot_t *slot = 0;
+
+ ox_cache8_new(&c);
+ for (d = data; 0 != *d; d++) {
+ v = ox_cache8_get(c, *d, &slot);
+ if (0 == v) {
+ if (0 == slot) {
+ printf("*** failed to get a slot for 0x%016llx\n", (unsigned long long)*d);
+ } else {
+ printf("*** adding 0x%016llx to cache with value %llu\n", (unsigned long long)*d, (unsigned long long)cnt);
+ *slot = cnt++;
+ }
+ } else {
+ printf("*** get on 0x%016llx returned %llu\n", (unsigned long long)*d, (unsigned long long)v);
+ }
+ /*ox_cache8_print(c); */
+ }
+ ox_cache8_print(c);
+}
+
diff -Nru ruby-ox-2.11.0/test/c/cache_test.c ruby-ox-2.14.9/test/c/cache_test.c
--- ruby-ox-2.11.0/test/c/cache_test.c 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/c/cache_test.c 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,55 @@
+/* cache_test.c
+ * Copyright (c) 2011, Peter Ohler
+ * All rights reserved.
+ */
+
+#include "cache.h"
+
+static const char *data[] = {
+#if 1
+ "one",
+ "two",
+ "one",
+ "onex",
+ "oney",
+ "one",
+ "tw",
+ "onexyzabcdefgh",
+#else
+ "abc",
+ "abcd",
+ "ab",
+ "a",
+ "abcdefghijklmnop",
+#endif
+ 0
+};
+
+void
+ox_cache_test() {
+ Cache c;
+ const char **d;
+ VALUE v;
+ VALUE *slot = 0;;
+
+ ox_cache_new(&c);
+ for (d = data; 0 != *d; d++) {
+ /*printf("*** cache_get on %s\n", *d);*/
+ v = ox_cache_get(c, *d, &slot, 0);
+ if (Qundef == v) {
+ if (0 == slot) {
+ /*printf("*** failed to get a slot for %s\n", *d); */
+ } else {
+ /*printf("*** added '%s' to cache\n", *d); */
+ v = ID2SYM(rb_intern(*d));
+ *slot = v;
+ }
+ } else {
+ VALUE rs = rb_funcall2(v, rb_intern("to_s"), 0, 0);
+
+ printf("*** get on '%s' returned '%s' (%s)\n", *d, StringValuePtr(rs), rb_class2name(rb_obj_class(v)));
+ }
+ /*ox_cache_print(c);*/
+ }
+ ox_cache_print(c);
+}
diff -Nru ruby-ox-2.11.0/test/cache8_test.rb ruby-ox-2.14.9/test/cache8_test.rb
--- ruby-ox-2.11.0/test/cache8_test.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/cache8_test.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+
+$: << '.'
+$: << '../lib'
+$: << '../ext'
+
+if __FILE__ == $0
+ if (i = ARGV.index('-I'))
+ x,path = ARGV.slice!(i, 2)
+ $: << path
+ end
+end
+
+require 'ox'
+
+Ox.cache8_test
+
diff -Nru ruby-ox-2.11.0/test/cache_test.rb ruby-ox-2.14.9/test/cache_test.rb
--- ruby-ox-2.11.0/test/cache_test.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/cache_test.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+
+$: << '.'
+$: << '../lib'
+$: << '../ext'
+
+if __FILE__ == $0
+ if (i = ARGV.index('-I'))
+ x,path = ARGV.slice!(i, 2)
+ $: << path
+ end
+end
+
+require 'ox'
+
+Ox.cache_test
+
diff -Nru ruby-ox-2.11.0/test/files.rb ruby-ox-2.14.9/test/files.rb
--- ruby-ox-2.11.0/test/files.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/files.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby -wW2
+
+if $0 == __FILE__
+ $: << '.'
+ $: << '..'
+ $: << '../lib'
+ $: << '../ext'
+end
+
+require 'pp'
+require 'sample/file'
+require 'sample/dir'
+
+def files(dir)
+ d = ::Sample::Dir.new(dir)
+ Dir.new(dir).each do |fn|
+ next if fn.start_with?('.')
+ filename = File.join(dir, fn)
+ #filename = '.' == dir ? fn : File.join(dir, fn)
+ if File.directory?(filename)
+ d << files(filename)
+ else
+ d << ::Sample::File.new(filename)
+ end
+ end
+ #pp d
+ d
+end
+
diff -Nru ruby-ox-2.11.0/test/gen_sample.rb ruby-ox-2.14.9/test/gen_sample.rb
--- ruby-ox-2.11.0/test/gen_sample.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/gen_sample.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,22 @@
+
+require 'ox'
+
+doc = Ox::Document.new(:version => '1.0')
+
+top = Ox::Element.new('top')
+top[:name] = 'sample'
+doc << top
+
+mid = Ox::Element.new('middle')
+mid[:name] = 'second'
+top << mid
+
+bot = Ox::Element.new('bottom')
+bot[:name] = 'third'
+mid << bot
+
+xml = Ox.dump(doc)
+puts xml
+
+doc2 = Ox.parse(xml)
+puts "Same? #{doc == doc2}"
diff -Nru ruby-ox-2.11.0/test/obj_sample.rb ruby-ox-2.14.9/test/obj_sample.rb
--- ruby-ox-2.11.0/test/obj_sample.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/obj_sample.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,19 @@
+
+require 'ox'
+
+class Sample
+ attr_accessor :a, :b, :c
+
+ def initialize(a, b, c)
+ @a = a
+ @b = b
+ @c = c
+ end
+end # File
+
+# Create Object
+obj = Sample.new(1, "bee", ['x', :y, 7.0])
+# Now dump the Object to an XML String.
+xml = Ox.dump(obj)
+# Convert the object back into a Sample Object.
+obj2 = Ox.parse_obj(xml)
diff -Nru ruby-ox-2.11.0/test/ox/change.rb ruby-ox-2.14.9/test/ox/change.rb
--- ruby-ox-2.11.0/test/ox/change.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/change.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,16 @@
+
+module Test
+ module Ox
+ class Change
+ attr_accessor :time
+ attr_accessor :user
+ attr_accessor :comment
+
+ def initialize(comment=nil, time=nil, user=nil)
+ @user = user || ENV['USER']
+ @time = time || Time.now
+ @comment = comment
+ end
+ end # Change
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/ox/dir.rb ruby-ox-2.14.9/test/ox/dir.rb
--- ruby-ox-2.11.0/test/ox/dir.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/dir.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,21 @@
+
+require 'etc'
+
+module Test
+ module Ox
+
+ class Dir < File
+ attr_accessor :files
+
+ def initialize(filename)
+ super
+ @files = []
+ end
+
+ def <<(f)
+ @files << f
+ end
+
+ end # Dir
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/ox/doc.rb ruby-ox-2.14.9/test/ox/doc.rb
--- ruby-ox-2.11.0/test/ox/doc.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/doc.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,39 @@
+
+require 'test/ox/hasprops'
+require 'test/ox/group'
+require 'test/ox/layer'
+require 'test/ox/line'
+require 'test/ox/shape'
+require 'test/ox/oval'
+require 'test/ox/rect'
+require 'test/ox/text'
+require 'test/ox/change'
+
+module Test
+ module Ox
+ class Doc
+ include HasProps
+
+ attr_accessor :title
+ attr_accessor :create_time
+ attr_accessor :user
+ # Hash of layers in the document indexed by layer name.
+ attr_reader :layers
+ attr_reader :change_history
+
+ def initialize(title)
+ @title = title
+ @user = ENV['USER']
+ @create_time = Time.now
+ @layers = { }
+ @change_history = []
+ end
+
+ def add_change(comment, time=nil, user=nil)
+ @change_history << Change.new(comment, time, user)
+ end
+
+ end # Doc
+ end # Ox
+end # Test
+
diff -Nru ruby-ox-2.11.0/test/ox/file.rb ruby-ox-2.14.9/test/ox/file.rb
--- ruby-ox-2.11.0/test/ox/file.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/file.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,33 @@
+
+require 'etc'
+
+module Test
+ module Ox
+
+ class File
+ attr_accessor :name, :ctime, :mtime, :size, :owner, :group, :permissions
+
+ def initialize(filename)
+ @name = ::File.basename(filename)
+ stat = ::File.stat(filename)
+ @ctime = stat.ctime
+ @mtime = stat.mtime
+ @size = stat.size
+ @owner = Etc.getpwuid(stat.uid).name
+ @group = Etc.getgrgid(stat.gid).name
+ @permissions = {
+ :user => [(0 != (stat.mode & 0x0100)) ? 'r' : '-',
+ (0 != (stat.mode & 0x0080)) ? 'w' : '-',
+ (0 != (stat.mode & 0x0040)) ? 'x' : '-'].join(''),
+ :group => [(0 != (stat.mode & 0x0020)) ? 'r' : '-',
+ (0 != (stat.mode & 0x0010)) ? 'w' : '-',
+ (0 != (stat.mode & 0x0008)) ? 'x' : '-'].join(''),
+ :other => [(0 != (stat.mode & 0x0004)) ? 'r' : '-',
+ (0 != (stat.mode & 0x0002)) ? 'w' : '-',
+ (0 != (stat.mode & 0x0001)) ? 'x' : '-'].join('')
+ }
+ end
+
+ end # File
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/ox/group.rb ruby-ox-2.14.9/test/ox/group.rb
--- ruby-ox-2.11.0/test/ox/group.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/group.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,18 @@
+
+module Test
+ module Ox
+ class Group
+ attr_reader :members
+
+ def initialize()
+ @members = []
+ end
+
+ def <<(member)
+ @members << member
+ end
+
+ end # Group
+ end # Ox
+end # Test
+
diff -Nru ruby-ox-2.11.0/test/ox/hasprops.rb ruby-ox-2.14.9/test/ox/hasprops.rb
--- ruby-ox-2.11.0/test/ox/hasprops.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/hasprops.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,18 @@
+
+module Test
+ module Ox
+ module HasProps
+
+ def add_prop(key, value)
+ @props = { } unless self.instance_variable_defined?(:@props)
+ @props[key] = value
+ end
+
+ def props
+ @props = { } unless self.instance_variable_defined?(:@props)
+ @props
+ end
+
+ end # HashProps
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/ox/layer.rb ruby-ox-2.14.9/test/ox/layer.rb
--- ruby-ox-2.11.0/test/ox/layer.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/layer.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,14 @@
+
+module Test
+ module Ox
+ class Layer < Group
+ attr_accessor :name
+
+ def initialize(name)
+ super()
+ @name = name
+ end
+
+ end # Layer
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/ox/line.rb ruby-ox-2.14.9/test/ox/line.rb
--- ruby-ox-2.11.0/test/ox/line.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/line.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,22 @@
+module Test
+ module Ox
+
+ class Line
+ include HasProps
+
+ attr_accessor :x, :y, :dx, :dy
+ attr_accessor :color
+ attr_accessor :thick
+
+ def initialize(x, y, dx, dy, thick, color)
+ @x = x
+ @y = y
+ @dx = dx
+ @dy = dy
+ @thick = thick
+ @color = color
+ end
+
+ end # Line
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/ox/oval.rb ruby-ox-2.14.9/test/ox/oval.rb
--- ruby-ox-2.11.0/test/ox/oval.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/oval.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,12 @@
+module Test
+ module Ox
+
+ class Oval < Shape
+
+ def initialize(left, top, wide, high, color=nil)
+ super
+ end
+
+ end # Oval
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/ox/rect.rb ruby-ox-2.14.9/test/ox/rect.rb
--- ruby-ox-2.11.0/test/ox/rect.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/rect.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,12 @@
+
+module Test
+ module Ox
+ class Rect < Shape
+
+ def initialize(left, top, wide, high, color=nil)
+ super
+ end
+
+ end # Rect
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/ox/shape.rb ruby-ox-2.14.9/test/ox/shape.rb
--- ruby-ox-2.11.0/test/ox/shape.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/shape.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,37 @@
+
+module Test
+ module Ox
+ class Shape
+ include HasProps
+
+ attr_accessor :bounds
+ attr_accessor :color
+ attr_accessor :border, :border_color
+
+ def initialize(left, top, wide, high, color=nil)
+ @bounds = [[left, top], [left + wide, top + high]]
+ @color = color
+ @border = 1
+ @border_color = :black
+ end
+
+ def left
+ @bounds[0][0]
+ end
+
+ def top
+ @bounds[0][1]
+ end
+
+ def width
+ @bounds[1][0] - @bounds[0][0]
+ end
+
+ def height
+ @bounds[1][1] - @bounds[0][1]
+ end
+
+ end # Shape
+ end # Ox
+end # Test
+
diff -Nru ruby-ox-2.11.0/test/ox/text.rb ruby-ox-2.14.9/test/ox/text.rb
--- ruby-ox-2.11.0/test/ox/text.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/ox/text.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,23 @@
+
+module Test
+ module Ox
+
+ class Text < Shape
+ attr_accessor :text
+ attr_accessor :font
+ attr_accessor :font_size
+ attr_accessor :just
+ attr_accessor :text_color
+
+ def initialize(text, left, top, wide, high, color=nil)
+ super(left, top, wide, high, color)
+ @text = text
+ @font = 'helvetica'
+ @font_size = 14
+ @just = 'left'
+ @text_color = 'black'
+ end
+
+ end # Text
+ end # Ox
+end # Test
diff -Nru ruby-ox-2.11.0/test/parse_cmp.rb ruby-ox-2.14.9/test/parse_cmp.rb
--- ruby-ox-2.11.0/test/parse_cmp.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/parse_cmp.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,261 @@
+#!/usr/bin/env ruby -wW1
+
+$: << '../lib'
+$: << '../ext'
+
+require 'optparse'
+require 'stringio'
+require 'ox'
+
+$verbose = 0
+$iter = 100
+
+opts = OptionParser.new
+opts.on("-v", "increase verbosity") { $verbose += 1 }
+opts.on("-i", "--iterations [Int]", Integer, "iterations") { |i| $iter = i }
+opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
+files = opts.parse(ARGV)
+
+### XML conversion to Hash using in memory Ox parsing ###
+
+def node_to_dict(element)
+ dict = Hash.new
+ key = nil
+ element.nodes.each do |n|
+ raise "A dict can only contain elements." unless n.is_a?(::Ox::Element)
+ if key.nil?
+ raise "Expected a key, not a #{n.name}." unless 'key' == n.name
+ key = first_text(n)
+ else
+ dict[key] = node_to_value(n)
+ key = nil
+ end
+ end
+ dict
+end
+
+def node_to_array(element)
+ a = Array.new
+ element.nodes.each do |n|
+ a.push(node_to_value(n))
+ end
+ a
+end
+
+def node_to_value(node)
+ raise "A dict can only contain elements." unless node.is_a?(::Ox::Element)
+ case node.name
+ when 'key'
+ raise "Expected a value, not a key."
+ when 'string'
+ value = first_text(node)
+ when 'dict'
+ value = node_to_dict(node)
+ when 'array'
+ value = node_to_array(node)
+ when 'integer'
+ value = first_text(node).to_i
+ when 'real'
+ value = first_text(node).to_f
+ when 'true'
+ value = true
+ when 'false'
+ value = false
+ else
+ raise "#{node.name} is not a know element type."
+ end
+ value
+end
+
+def first_text(node)
+ node.nodes.each do |n|
+ return n if n.is_a?(String)
+ end
+ nil
+end
+
+def parse_gen(xml)
+ doc = Ox.parse(xml)
+ plist = doc.root
+ dict = nil
+ plist.nodes.each do |n|
+ if n.is_a?(::Ox::Element)
+ dict = node_to_dict(n)
+ break
+ end
+ end
+ dict
+end
+
+### XML conversion to Hash using Ox SAX parser ###
+
+class Handler
+ def initialize()
+ @key = nil
+ @type = nil
+ @plist = nil
+ @stack = []
+ end
+
+ def text(value)
+ last = @stack.last
+ if last.is_a?(Hash) and @key.nil?
+ raise "Expected a key, not #{@type} with a value of #{value}." unless :key == @type
+ @key = value
+ else
+ append(value)
+ end
+ end
+
+ def start_element(name)
+ if :dict == name
+ dict = Hash.new
+ append(dict)
+ @stack.push(dict)
+ elsif :array == name
+ a = Array.new
+ append(a)
+ @stack.push(a)
+ elsif :true == name
+ append(true)
+ elsif :false == name
+ append(false)
+ else
+ @type = name
+ end
+ end
+
+ def end_element(name)
+ @stack.pop if :dict == name or :array == name
+ end
+
+ def plist
+ @plist
+ end
+
+ def append(value)
+ unless value.is_a?(Array) or value.is_a?(Hash)
+ case @type
+ when :string
+ # ignore
+ when :key
+ # ignore
+ when :integer
+ value = value.to_i
+ when :real
+ value = value.to_f
+ end
+ end
+ last = @stack.last
+ if last.is_a?(Hash)
+ raise "Expected a key, not with a value of #{value}." if @key.nil?
+ last[@key] = value
+ @key = nil
+ elsif last.is_a?(Array)
+ last.push(value)
+ elsif last.nil?
+ @plist = value
+ end
+ end
+
+end
+
+def parse_sax(xml)
+ io = StringIO.new(xml)
+ start = Time.now
+ handler = Handler.new()
+ Ox.sax_parse(handler, io)
+ handler.plist
+end
+
+### XML conversion to Hash using Ox Object parsing with gsub! replacements ###
+
+def convert_parse_obj(xml)
+ xml = plist_to_obj_xml(xml)
+ ::Ox.load(xml, :mode => :object)
+end
+
+### XML conversion to Hash using Ox Object parsing after gsub! replacements ###
+
+def parse_obj(xml)
+ ::Ox.load(xml, :mode => :object)
+end
+
+def plist_to_obj_xml(xml)
+ xml = xml.gsub(%{
+}, '')
+ xml.gsub!(%{
+}, '')
+ { '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ }.each do |pat,rep|
+ xml.gsub!(pat, rep)
+ end
+ xml
+end
+
+files.each do |filename|
+ xml = File.read(filename)
+
+ if 0 < $verbose
+ d1 = parse_gen(xml)
+ d2 = parse_sax(xml)
+ d3 = convert_parse_obj(xml)
+ puts "--- It is #{d1 == d2 and d2 == d3} that all parsers yield the same Hash. ---"
+ end
+
+ start = Time.now
+ $iter.times do
+ parse_gen(xml)
+ end
+ gen_time = Time.now - start
+
+ start = Time.now
+ $iter.times do
+ parse_sax(xml)
+ end
+ sax_time = Time.now - start
+
+ start = Time.now
+ $iter.times do
+ convert_parse_obj(xml)
+ end
+ conv_obj_time = Time.now - start
+
+ xml = plist_to_obj_xml(xml)
+ start = Time.now
+ $iter.times do
+ parse_obj(xml)
+ end
+ obj_time = Time.now - start
+
+ puts "In memory parsing and conversion took #{gen_time} for #{$iter} iterations."
+ puts "SAX parsing and conversion took #{sax_time} for #{$iter} iterations."
+ puts "XML gsub Object parsing and conversion took #{conv_obj_time} for #{$iter} iterations."
+ puts "Object parsing and conversion took #{obj_time} for #{$iter} iterations."
+end
+
+# Results for a run:
+#
+# > parse_cmp.rb Sample.graffle -i 1000
+# In memory parsing and conversion took 4.135701 for 1000 iterations.
+# SAX parsing and conversion took 3.731695 for 1000 iterations.
+# XML gsub Object parsing and conversion took 3.292397 for 1000 iterations.
+# Object parsing and conversion took 0.808877 for 1000 iterations.
diff -Nru ruby-ox-2.11.0/test/perf_gen.rb ruby-ox-2.14.9/test/perf_gen.rb
--- ruby-ox-2.11.0/test/perf_gen.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/perf_gen.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,142 @@
+#!/usr/bin/env ruby
+
+$: << '.'
+$: << '..'
+$: << '../lib'
+$: << '../ext'
+
+if __FILE__ == $0
+ if (i = ARGV.index('-I'))
+ x = ARGV.slice!(i, 2)
+ $: << x[1]
+ end
+end
+
+require 'optparse'
+require 'ox'
+require 'sample'
+require 'test/ox/doc'
+require 'files'
+require 'perf'
+begin
+ require 'nokogiri'
+rescue Exception => e
+end
+begin
+ require 'libxml'
+rescue Exception => e
+end
+
+$verbose = 0
+$ox_only = false
+
+do_sample = false
+do_files = false
+do_load = false
+do_dump = false
+do_read = false
+do_write = false
+$iter = 1000
+
+opts = OptionParser.new
+opts.on("-v", "increase verbosity") { $verbose += 1 }
+
+opts.on("-x", "ox only") { $ox_only = true }
+
+opts.on("-s", "load and dump as sample Ruby object") { do_sample = true }
+opts.on("-f", "load and dump as files Ruby object") { do_files = true }
+
+opts.on("-l", "load") { do_load = true }
+opts.on("-d", "dump") { do_dump = true }
+opts.on("-r", "read") { do_read = true }
+opts.on("-w", "write") { do_write = true }
+opts.on("-a", "load, dump, read and write") { do_load = true; do_dump = true; do_read = true; do_write = true }
+
+opts.on("-i", "--iterations [Int]", Integer, "iterations") { |it| $iter = it }
+
+opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
+files = opts.parse(ARGV)
+
+Ox.default_options = {mode: :generic}
+
+data = []
+
+if files.empty?
+ data = []
+ obj = do_sample ? sample_doc(2) : files('..')
+ xml = Ox.dump(obj, :indent => 2, :opt_format => true)
+ File.open('sample.xml', 'w') { |f| f.write(xml) }
+ gen = Ox.parse(xml)
+ h = { :file => 'sample.xml', :xml => xml, :ox => gen }
+ h[:nokogiri] = Nokogiri::XML::Document.parse(xml) unless defined?(::Nokogiri).nil?
+ h[:libxml] = LibXML::XML::Document.string(xml) unless defined?(::LibXML).nil?
+ data << h
+else
+ puts "loading and parsing #{files}\n\n"
+ data = files.map do |f|
+ xml = File.read(f)
+ obj = Ox.parse(xml)
+ gen = Ox.parse(xml)
+ h = { :file => f, :xml => xml, :ox => gen }
+ h[:nokogiri] = Nokogiri::XML::Document.parse(xml) unless defined?(::Nokogiri).nil?
+ h[:libxml] = LibXML::XML::Document.string(xml) unless defined?(::LibXML).nil?
+ h
+ end
+end
+
+data.each do |d|
+ if do_load
+ perf = Perf.new()
+ perf.add('Ox', 'parse') { Ox.parse(xml) }
+ perf.add('Nokogiri', 'parse') { Nokogiri::XML::Document.parse(xml) } unless defined?(::Nokogiri).nil?
+ perf.add('LibXML', 'parse') { LibXML::XML::Document.string(xml) } unless defined?(::LibXML).nil?
+ perf.run($iter)
+ end
+
+ if do_dump
+ perf = Perf.new()
+ perf.add('Ox', 'dump') { Ox.dump($obj, :indent => 2) }
+ perf.before('Ox') { $obj = d[:ox] }
+ unless defined?(::Nokogiri).nil?
+ perf.add('Nokogiri', 'dump') { $obj.to_xml(:indent => 2) }
+ perf.before('Nokogiri') { $obj = d[:nokogiri] }
+ end
+ unless defined?(::LibXML).nil?
+ perf.add('LibXML', 'dump') { $obj.to_s() }
+ perf.before('LibXML') { $obj = d[:libxml] }
+ end
+ perf.run($iter)
+ end
+
+ if do_read
+ $filename = d[:file]
+ perf = Perf.new()
+ perf.add('Ox', 'load_file') { Ox.load_file($filename) }
+ perf.add('Nokogiri', 'parse') { Nokogiri::XML::Document.parse(File.open($filename)) } unless defined?(::Nokogiri).nil?
+ perf.add('LibXML', 'parse') { LibXML::XML::Document.file($filename) } unless defined?(::LibXML).nil?
+ perf.run($iter)
+ end
+
+ if do_write
+ $filename = 'out.xml'
+ perf = Perf.new()
+ perf.add('Ox', 'to_file') { Ox.to_file($filename, $obj, :indent => 0) }
+ perf.before('Ox') { $obj = d[:ox] }
+ unless defined?(::Nokogiri).nil?
+ perf.add('Nokogiri', 'dump') {
+ xml = $obj.to_xml(:indent => 0)
+ File.open($filename, "w") { |f| f.write(xml) }
+ }
+ end
+ perf.before('Nokogiri') { $obj = d[:nokogiri] }
+ unless defined?(::LibXML).nil?
+ perf.add('LibXML', 'dump') {
+ xml = $obj.to_s()
+ File.open($filename, "w") { |f| f.write(xml) }
+ }
+ perf.before('LibXML') { $obj = d[:libxml] }
+ end
+ perf.run($iter)
+ end
+
+end
diff -Nru ruby-ox-2.11.0/test/perf_mars.rb ruby-ox-2.14.9/test/perf_mars.rb
--- ruby-ox-2.11.0/test/perf_mars.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/perf_mars.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,114 @@
+#!/usr/bin/env ruby
+
+$: << '.'
+$: << '..'
+$: << '../lib'
+$: << '../ext'
+
+if __FILE__ == $0
+ if (i = ARGV.index('-I'))
+ x,path = ARGV.slice!(i, 2)
+ $: << path
+ end
+end
+
+require 'optparse'
+require 'pp'
+require 'ox'
+
+it = 5000
+
+opts = OptionParser.new
+opts.on("-i", "--iterations [Int]", Integer, "iterations") { |i| it = i }
+opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
+files = opts.parse(ARGV)
+
+module Test
+ module Ox
+ class Wrap
+ attr_accessor :values
+ def initialize(v=[])
+ @values = v
+ end
+ end
+ end
+end
+
+data = {
+ :Boolean => ::Test::Ox::Wrap.new(),
+ :Fixnum => ::Test::Ox::Wrap.new(),
+ :Float => ::Test::Ox::Wrap.new(),
+ :String => ::Test::Ox::Wrap.new(),
+ :Symbol => ::Test::Ox::Wrap.new(),
+ :Time => ::Test::Ox::Wrap.new(),
+ :Array => ::Test::Ox::Wrap.new(),
+ :Hash => ::Test::Ox::Wrap.new(),
+ :Range => ::Test::Ox::Wrap.new(),
+ :Regexp => ::Test::Ox::Wrap.new(),
+ :Bignum => ::Test::Ox::Wrap.new(),
+ :Complex => ::Test::Ox::Wrap.new(),
+ :Rational => ::Test::Ox::Wrap.new(),
+ :Struct => ::Test::Ox::Wrap.new(),
+ :Class => ::Test::Ox::Wrap.new(),
+ :Object => ::Test::Ox::Wrap.new(),
+}
+
+s = Struct.new('Zoo', :x, :y, :z)
+
+(1..200).each do |i|
+ data[:Boolean].values << (0 == (i % 2))
+ data[:Fixnum].values << ((i - 10) * 101)
+ data[:Float].values << ((i.to_f - 10.7) * 30.5)
+ data[:String].values << "String #{i}"
+ data[:Symbol].values << "Symbol#{i}".to_sym
+ data[:Time].values << Time.now + i
+ data[:Array].values << []
+ data[:Hash].values << { }
+ data[:Range].values << (0..7)
+ data[:Regexp].values << /^[0-9]/
+ data[:Bignum].values << (7 ** 55)
+ data[:Complex].values << Complex(1, 2)
+ data[:Rational].values << Rational(1, 3)
+ data[:Struct].values << s.new(1, 3, 5)
+ data[:Class].values << ::Test::Ox::Wrap
+ data[:Object].values << ::Test::Ox::Wrap.new(i)
+end
+
+puts " load dump"
+puts "type Ox Marshal ratio Ox Marshal ratio"
+data.each do |type,a|
+ #xml = Ox.dump(a, :indent => -1, :xsd_date => true)
+ xml = Ox.dump(a, :indent => -1)
+ #pp a
+ #puts xml
+ start = Time.now
+ (1..it).each do
+ obj = Ox.load(xml, :mode => :object)
+ #pp obj
+ end
+ ox_load_time = Time.now - start
+
+ m = Marshal.dump(a)
+ start = Time.now
+ (1..it).each do
+ obj = Marshal.load(m)
+ end
+ mars_load_time = Time.now - start
+
+ obj = Ox.load(xml, :mode => :object)
+ start = Time.now
+ (1..it).each do
+ xml = Ox.dump(a, :indent => -1)
+ end
+ ox_dump_time = Time.now - start
+
+ start = Time.now
+ (1..it).each do
+ m = Marshal.dump(a)
+ end
+ mars_dump_time = Time.now - start
+
+ puts "%8s %6.3f %6.3f %0.1f %6.3f %6.3f %0.1f" % [type.to_s,
+ ox_load_time, mars_load_time, mars_load_time / ox_load_time,
+ ox_dump_time, mars_dump_time, mars_dump_time / ox_dump_time]
+end
diff -Nru ruby-ox-2.11.0/test/perf_obj.rb ruby-ox-2.14.9/test/perf_obj.rb
--- ruby-ox-2.11.0/test/perf_obj.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/perf_obj.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,128 @@
+#!/usr/bin/env ruby
+
+$: << '.'
+$: << '../lib'
+$: << '../ext'
+
+if __FILE__ == $0
+ if (i = ARGV.index('-I'))
+ x = ARGV.slice!(i, 2)
+ $: << x[1]
+ end
+end
+
+require 'optparse'
+require 'ox'
+require 'perf'
+require 'sample'
+require 'files'
+begin
+ require 'oj'
+rescue Exception
+end
+
+$circular = false
+$indent = 0
+ox_only = false
+
+do_sample = false
+do_files = false
+
+do_load = false
+do_dump = false
+do_read = false
+do_write = false
+$iter = 1000
+
+opts = OptionParser.new
+opts.on("-c", "circular options") { $circular = true }
+
+opts.on("-s", "load and dump as sample Ruby object") { do_sample = true }
+opts.on("-f", "load and dump as files Ruby object") { do_files = true }
+
+opts.on("-l", "load") { do_load = true }
+opts.on("-d", "dump") { do_dump = true }
+opts.on("-r", "read") { do_read = true }
+opts.on("-w", "write") { do_write = true }
+opts.on("-a", "load, dump, read and write") { do_load = true; do_dump = true; do_read = true; do_write = true }
+
+opts.on("-i", "--iterations [Int]", Integer, "iterations") { |it| $iter = it }
+opts.on("-o", "ox_only") { ox_only = true }
+
+opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
+files = opts.parse(ARGV)
+
+$obj = nil
+$xml = nil
+$mars = nil
+$json = nil
+
+unless do_load || do_dump || do_read || do_write
+ do_load = true
+ do_dump = true
+ do_read = true
+ do_write = true
+end
+
+# prepare all the formats for input
+if files.empty?
+ $obj = do_sample ? sample_doc(2) : files('..')
+ $mars = Marshal.dump($obj)
+ $xml = Ox.dump($obj, :indent => $indent, :circular => $circular)
+ File.open('sample.xml', 'w') { |f| f.write($xml) }
+ File.open('sample.marshal', 'w') { |f| f.write($mars) }
+ unless defined?(::Oj).nil?
+ $json = Oj.dump($obj, :indent => $indent, :circular => $circular)
+ File.open('sample.json', 'w') { |f| f.write($json) }
+ end
+else
+ puts "loading and parsing #{files}\n\n"
+ files.map do |f|
+ $xml = File.read(f)
+ $obj = Ox.load($xml);
+ $mars = Marshal.dump($obj)
+ $json = Oj.dump($obj, :indent => $indent, :circular => $circular) unless defined?(::Oj).nil?
+ end
+end
+
+Oj.default_options = { :mode => :object, :indent => $indent } unless defined?(::Oj).nil?
+
+if do_load
+ puts '-' * 80
+ puts "Load Performance"
+ perf = Perf.new()
+ perf.add('Ox', 'load') { Ox.load($xml, :mode => :object) }
+ perf.add('Oj', 'load') { Oj.load($json) } unless (defined?(::Oj).nil? || ox_only)
+ perf.add('Marshal', 'load') { Marshal.load($mars) } unless ox_only
+ perf.run($iter)
+end
+
+if do_dump
+ puts '-' * 80
+ puts "Dump Performance"
+ perf = Perf.new()
+ perf.add('Ox', 'dump') { Ox.dump($obj, :indent => $indent, :circular => $circular) }
+ perf.add('Oj', 'dump') { Oj.dump($obj) } unless (defined?(::Oj).nil? || ox_only)
+ perf.add('Marshal', 'dump') { Marshal.dump($obj) } unless ox_only
+ perf.run($iter)
+end
+
+if do_read
+ puts '-' * 80
+ puts "Read from file Performance"
+ perf = Perf.new()
+ perf.add('Ox', 'load_file') { Ox.load_file('sample.xml', :mode => :object) }
+ perf.add('Oj', 'load') { Oj.load_file('sample.json') } unless (defined?(::Oj).nil? || ox_only)
+ perf.add('Marshal', 'load') { Marshal.load(File.new('sample.marshal')) } unless ox_only
+ perf.run($iter)
+end
+
+if do_write
+ puts '-' * 80
+ puts "Write to file Performance"
+ perf = Perf.new()
+ perf.add('Ox', 'to_file') { Ox.to_file('sample.xml', $obj, :indent => $indent, :circular => $circular) }
+ perf.add('Oj', 'to_file') { Oj.to_file('sample.json', $obj) } unless (defined?(::Oj).nil? || ox_only)
+ perf.add('Marshal', 'dump') { Marshal.dump($obj, File.new('sample.marshal', 'w')) } unless ox_only
+ perf.run($iter)
+end
diff -Nru ruby-ox-2.11.0/test/perf.rb ruby-ox-2.14.9/test/perf.rb
--- ruby-ox-2.11.0/test/perf.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/perf.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,105 @@
+
+class Perf
+
+ def initialize()
+ @items = []
+ end
+
+ def add(title, op, &blk)
+ @items << Item.new(title, op, &blk)
+ end
+
+ def before(title, &blk)
+ @items.each do |i|
+ if title == i.title
+ i.set_before(&blk)
+ break
+ end
+ end
+ end
+
+ def run(iter)
+ base = Item.new(nil, nil) { }
+ base.run(iter, 0.0)
+ @items.each do |i|
+ i.run(iter, base.duration)
+ if i.error.nil?
+ puts "#{i.title}.#{i.op} #{iter} times in %0.3f seconds or %0.3f #{i.op}/sec." % [i.duration, iter / i.duration]
+ else
+ puts "***** #{i.title}.#{i.op} failed! #{i.error}"
+ end
+ end
+ summary()
+ end
+
+ def summary()
+ width = 6
+ @items.each do |i|
+ next if i.duration.nil?
+ width = i.title.size if width < i.title.size
+ end
+ iva = @items.clone
+ iva.delete_if { |i| i.duration.nil? }
+ iva = iva.sort_by { |i| i.duration }
+ puts
+ puts "Summary:"
+ puts "%*s time (secs) rate (ops/sec)" % [width, 'System']
+ puts "#{'-' * width} ----------- --------------"
+ iva.each do |i|
+ if i.duration.nil?
+ else
+ puts "%*s %11.3f %14.3f" % [width, i.title, i.duration, i.rate ]
+ end
+ end
+ puts
+ puts "Comparison Matrix\n(performance factor, 2.0 means row is twice as fast as column)"
+ puts(([' ' * width] + iva.map { |i| "%*s" % [width, i.title] }).join(' '))
+ puts((['-' * width] + iva.map { |i| '-' * width }).join(' '))
+ iva.each do |i|
+ line = ["%*s" % [width, i.title]]
+ iva.each do |o|
+ line << "%*.2f" % [width, o.duration / i.duration]
+ end
+ puts line.join(' ')
+ end
+ puts
+ end
+
+ class Item
+ attr_accessor :title
+ attr_accessor :op
+ attr_accessor :blk
+ attr_accessor :duration
+ attr_accessor :rate
+ attr_accessor :error
+
+ def initialize(title, op, &blk)
+ @title = title
+ @blk = blk
+ @op = op
+ @duration = nil
+ @rate = nil
+ @error = nil
+ @before = nil
+ end
+
+ def set_before(&blk)
+ @before = blk
+ end
+
+ def run(iter, base)
+ begin
+ GC.start
+ @before.call unless @before.nil?
+ start = Time.now
+ iter.times { @blk.call }
+ @duration = Time.now - start - base
+ @duration = 0.0 if @duration < 0.0
+ @rate = iter / @duration
+ rescue Exception => e
+ @error = "#{e.class}: #{e.message}"
+ end
+ end
+
+ end # Item
+end # Perf
diff -Nru ruby-ox-2.11.0/test/perf_sax.rb ruby-ox-2.14.9/test/perf_sax.rb
--- ruby-ox-2.11.0/test/perf_sax.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/perf_sax.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,188 @@
+#!/usr/bin/env ruby
+
+$: << '.'
+$: << '..'
+$: << '../lib'
+$: << '../ext'
+
+if __FILE__ == $0
+ while (i = ARGV.index('-I'))
+ x = ARGV.slice!(i, 2)
+ $: << x[1]
+ end
+end
+
+require 'optparse'
+require 'ox'
+require 'perf'
+require 'files'
+begin
+ require 'nokogiri'
+rescue Exception => e
+end
+begin
+ require 'libxml'
+rescue Exception => e
+end
+
+$verbose = 0
+$ox_only = false
+$all_cbs = false
+$filename = nil # nil indicates new file names perf.xml will be created and used
+$filesize = 1000 # KBytes
+$iter = 1000
+$strio = false
+$pos = false
+$smart = false
+
+opts = OptionParser.new
+opts.on("-v", "increase verbosity") { $verbose += 1 }
+opts.on("-x", "ox only") { $ox_only = true }
+opts.on("-a", "all callbacks") { $all_cbs = true }
+opts.on("-b", "html smart") { $smart = true }
+opts.on("-p", "update position") { $pos = true; $all_cbs = true }
+opts.on("-z", "use StringIO instead of file") { $strio = true }
+opts.on("-f", "--file [String]", String, "filename") { |f| $filename = f }
+opts.on("-i", "--iterations [Int]", Integer, "iterations") { |it| $iter = it }
+opts.on("-s", "--size [Int]", Integer, "file size in KBytes") { |s| $filesize = s }
+opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
+opts.parse(ARGV)
+
+$xml_str = nil
+
+# size is in Kbytes
+def create_file(filename, size)
+ head = %{
+
+
+
+}
+ tail = %{
+}
+ row = %{
+
+ 1234
+ A string.
+ This is a longer string that stretches over a larger number of characters.
+ -12.345
+ 2011-09-18 23:07:26 +0900
+
+
+}
+ cnt = (size * 1000 - head.size - tail.size) / row.size
+ File.open(filename, "w") do |f|
+ f.write(head)
+ cnt.times do |i|
+ f.write(row % [i,i])
+ end
+ f.write(tail)
+ end
+end
+
+class OxSax < ::Ox::Sax
+ def error(message, line, column); puts message; end
+end
+
+class OxAllSax < OxSax
+ def start_element(name); end
+ def attr(name, str); end
+ def attr_value(name, value); end
+ def end_element(name); end
+ def text(str); end
+ def value(value); end
+ def instruct(target); end
+ def doctype(value); end
+ def comment(value); end
+ def cdata(value); end
+end
+
+class OxPosAllSax < OxAllSax
+ def initialize()
+ @line = nil
+ @column = nil
+ end
+end
+
+unless defined?(::Nokogiri).nil?
+ class NoSax < Nokogiri::XML::SAX::Document
+ def error(message); puts message; end
+ def warning(message); puts message; end
+ end
+ class NoAllSax < NoSax
+ def start_element(name, attrs = []); end
+ def characters(text); end
+ def cdata_block(string); end
+ def comment(string); end
+ def end_document(); end
+ def end_element(name); end
+ def start_document(); end
+ def xmldecl(version, encoding, standalone); end
+ end
+end
+
+unless defined?(::LibXML).nil?
+ class LxSax
+ include LibXML::XML::SaxParser::Callbacks
+ end
+ class LxAllSax < LxSax
+ def on_start_element(element, attributes); end
+ def on_cdata_block(cdata); end
+ def on_characters(chars); end
+ def on_comment(msg); end
+ def on_end_document(); end
+ def on_end_element(element); end
+ def on_end_element_ns(name, prefix, uri); end
+ def on_error(msg); end
+ def on_external_subset(name, external_id, system_id); end
+ def on_has_external_subset(); end
+ def on_has_internal_subset(); end
+ def on_internal_subset(name, external_id, system_id); end
+ def on_is_standalone(); end
+ def on_processing_instruction(target, data); end
+ def on_reference(name); end
+ def on_start_document(); end
+ def on_start_element_ns(name, attributes, prefix, uri, namespaces); end
+ end
+end
+
+if $filename.nil?
+ create_file('perf.xml', $filesize)
+ $filename = 'perf.xml'
+end
+$xml_str = File.read($filename)
+
+puts "A #{$filesize} KByte XML file was parsed #{$iter} times for this test."
+
+$handler = nil
+perf = Perf.new
+
+perf.add('Ox::Sax', 'sax_parse') {
+ input = $strio ? StringIO.new($xml_str) : IO.open(IO.sysopen($filename))
+ Ox.sax_parse($handler, input, :smart => $smart)
+ input.close
+}
+perf.before('Ox::Sax') { $handler = $all_cbs ? ($pos ? OxPosAllSax.new() : OxAllSax.new()) : OxSax.new() }
+
+unless $ox_only
+ unless defined?(::Nokogiri).nil?
+ perf.add('Nokogiri::XML::Sax', 'parse') {
+ input = $strio ? StringIO.new($xml_str) : IO.open(IO.sysopen($filename))
+ $handler.parse(input)
+ input.close
+ }
+ perf.before('Nokogiri::XML::Sax') { $handler = Nokogiri::XML::SAX::Parser.new($all_cbs ? NoAllSax.new() : NoSax.new()) }
+ end
+
+ unless defined?(::LibXML).nil?
+ perf.add('LibXML::XML::Sax', 'parse') {
+ input = $strio ? StringIO.new($xml_str) : IO.open(IO.sysopen($filename))
+ parser = LibXML::XML::SaxParser.io(input)
+ parser.callbacks = $handler
+ parser.parse()
+ input.close
+ }
+ perf.before('LibXML::XML::Sax') { $handler = $all_cbs ? LxAllSax.new() : LxSax.new() }
+ end
+end
+
+perf.run($iter)
diff -Nru ruby-ox-2.11.0/test/sample/change.rb ruby-ox-2.14.9/test/sample/change.rb
--- ruby-ox-2.11.0/test/sample/change.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/change.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,14 @@
+
+module Sample
+ class Change
+ attr_accessor :time
+ attr_accessor :user
+ attr_accessor :comment
+
+ def initialize(comment=nil, time=nil, user=nil)
+ @user = user || ENV['USER']
+ @time = time || Time.now
+ @comment = comment
+ end
+ end # Change
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/dir.rb ruby-ox-2.14.9/test/sample/dir.rb
--- ruby-ox-2.11.0/test/sample/dir.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/dir.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,19 @@
+
+require 'etc'
+
+module Sample
+
+ class Dir < File
+ attr_accessor :files
+
+ def initialize(filename)
+ super
+ @files = []
+ end
+
+ def <<(f)
+ @files << f
+ end
+
+ end # Dir
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/doc.rb ruby-ox-2.14.9/test/sample/doc.rb
--- ruby-ox-2.11.0/test/sample/doc.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/doc.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,36 @@
+
+require 'sample/hasprops'
+require 'sample/group'
+require 'sample/layer'
+require 'sample/line'
+require 'sample/shape'
+require 'sample/oval'
+require 'sample/rect'
+require 'sample/text'
+require 'sample/change'
+
+module Sample
+ class Doc
+ include HasProps
+
+ attr_accessor :title
+ attr_accessor :create_time
+ attr_accessor :user
+ # Hash of layers in the document indexed by layer name.
+ attr_reader :layers
+ attr_reader :change_history
+
+ def initialize(title)
+ @title = title
+ @user = ENV['USER']
+ @create_time = Time.now
+ @layers = { }
+ @change_history = []
+ end
+
+ def add_change(comment, time=nil, user=nil)
+ @change_history << Change.new(comment, time, user)
+ end
+
+ end # Doc
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/file.rb ruby-ox-2.14.9/test/sample/file.rb
--- ruby-ox-2.11.0/test/sample/file.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/file.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,48 @@
+
+require 'etc'
+
+module Sample
+
+ class File
+ attr_accessor :name, :ctime, :mtime, :size, :owner, :group, :permissions
+
+ def initialize(filename)
+ @name = ::File.basename(filename)
+ stat = ::File.stat(filename)
+ @ctime = stat.ctime
+ @mtime = stat.mtime
+ @size = stat.size
+ @owner = Etc.getpwuid(stat.uid).name
+ @group = Etc.getgrgid(stat.gid).name
+ if false
+ @permissions = {
+ 'user' => {
+ 'read' => (0 != (stat.mode & 0x0100)),
+ 'write' => (0 != (stat.mode & 0x0080)),
+ 'execute' => (0 != (stat.mode & 0x0040))},
+ 'group' => {
+ 'read' => (0 != (stat.mode & 0x0020)),
+ 'write' => (0 != (stat.mode & 0x0010)),
+ 'execute' => (0 != (stat.mode & 0x0008))},
+ 'other' => {
+ 'read' => (0 != (stat.mode & 0x0004)),
+ 'write' => (0 != (stat.mode & 0x0002)),
+ 'execute' => (0 != (stat.mode & 0x0001))}
+ }
+ else
+ @permissions = {
+ 'user' => [(0 != (stat.mode & 0x0100)) ? 'r' : '-',
+ (0 != (stat.mode & 0x0080)) ? 'w' : '-',
+ (0 != (stat.mode & 0x0040)) ? 'x' : '-'].join(''),
+ 'group' => [(0 != (stat.mode & 0x0020)) ? 'r' : '-',
+ (0 != (stat.mode & 0x0010)) ? 'w' : '-',
+ (0 != (stat.mode & 0x0008)) ? 'x' : '-'].join(''),
+ 'other' => [(0 != (stat.mode & 0x0004)) ? 'r' : '-',
+ (0 != (stat.mode & 0x0002)) ? 'w' : '-',
+ (0 != (stat.mode & 0x0001)) ? 'x' : '-'].join('')
+ }
+ end
+ end
+
+ end # File
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/group.rb ruby-ox-2.14.9/test/sample/group.rb
--- ruby-ox-2.11.0/test/sample/group.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/group.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,16 @@
+
+module Sample
+ class Group
+ attr_reader :members
+
+ def initialize()
+ @members = []
+ end
+
+ def <<(member)
+ @members << member
+ end
+
+ end # Group
+end # Sample
+
diff -Nru ruby-ox-2.11.0/test/sample/hasprops.rb ruby-ox-2.14.9/test/sample/hasprops.rb
--- ruby-ox-2.11.0/test/sample/hasprops.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/hasprops.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,16 @@
+
+module Sample
+ module HasProps
+
+ def add_prop(key, value)
+ @props = { } unless self.instance_variable_defined?(:@props)
+ @props[key] = value
+ end
+
+ def props
+ @props = { } unless self.instance_variable_defined?(:@props)
+ @props
+ end
+
+ end # HasProps
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/layer.rb ruby-ox-2.14.9/test/sample/layer.rb
--- ruby-ox-2.11.0/test/sample/layer.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/layer.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,12 @@
+
+module Sample
+ class Layer < Group
+ attr_accessor :name
+
+ def initialize(name)
+ super()
+ @name = name
+ end
+
+ end # Layer
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/line.rb ruby-ox-2.14.9/test/sample/line.rb
--- ruby-ox-2.11.0/test/sample/line.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/line.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,20 @@
+module Sample
+
+ class Line
+ include HasProps
+
+ attr_accessor :x, :y, :dx, :dy
+ attr_accessor :color
+ attr_accessor :thick
+
+ def initialize(x, y, dx, dy, thick, color)
+ @x = x
+ @y = y
+ @dx = dx
+ @dy = dy
+ @thick = thick
+ @color = color
+ end
+
+ end # Line
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/oval.rb ruby-ox-2.14.9/test/sample/oval.rb
--- ruby-ox-2.11.0/test/sample/oval.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/oval.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,10 @@
+module Sample
+
+ class Oval < Shape
+
+ def initialize(left, top, wide, high, color=nil)
+ super
+ end
+
+ end # Oval
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/rect.rb ruby-ox-2.14.9/test/sample/rect.rb
--- ruby-ox-2.11.0/test/sample/rect.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/rect.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,10 @@
+
+module Sample
+ class Rect < Shape
+
+ def initialize(left, top, wide, high, color=nil)
+ super
+ end
+
+ end # Rect
+end # Sample
diff -Nru ruby-ox-2.11.0/test/sample/shape.rb ruby-ox-2.14.9/test/sample/shape.rb
--- ruby-ox-2.11.0/test/sample/shape.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/shape.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,35 @@
+
+module Sample
+ class Shape
+ include HasProps
+
+ attr_accessor :bounds
+ attr_accessor :color
+ attr_accessor :border, :border_color
+
+ def initialize(left, top, wide, high, color=nil)
+ @bounds = [[left, top], [left + wide, top + high]]
+ @color = color
+ @border = 1
+ @border_color = :black
+ end
+
+ def left
+ @bounds[0][0]
+ end
+
+ def top
+ @bounds[0][1]
+ end
+
+ def width
+ @bounds[1][0] - @bounds[0][0]
+ end
+
+ def height
+ @bounds[1][1] - @bounds[0][1]
+ end
+
+ end # Shape
+end # Sample
+
diff -Nru ruby-ox-2.11.0/test/sample/text.rb ruby-ox-2.14.9/test/sample/text.rb
--- ruby-ox-2.11.0/test/sample/text.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample/text.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,20 @@
+
+module Sample
+ class Text < Shape
+ attr_accessor :text
+ attr_accessor :font
+ attr_accessor :font_size
+ attr_accessor :just
+ attr_accessor :text_color
+
+ def initialize(text, left, top, wide, high, color=nil)
+ super(left, top, wide, high, color)
+ @text = text
+ @font = 'helvetica'
+ @font_size = 14
+ @just = 'left'
+ @text_color = 'black'
+ end
+
+ end # Text
+end # Sample
diff -Nru ruby-ox-2.11.0/test/Sample.graffle ruby-ox-2.14.9/test/Sample.graffle
--- ruby-ox-2.11.0/test/Sample.graffle 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/Sample.graffle 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,2318 @@
+
+
+
+
+ ActiveLayerIndex
+ 0
+ ApplicationVersion
+
+ com.omnigroup.OmniGrafflePro
+ 138.17.0.133677
+
+ AutoAdjust
+
+ BackgroundGraphic
+
+ Bounds
+ {{0, 0}, {559.28, 782.89}}
+ Class
+ SolidGraphic
+ ID
+ 2
+ Style
+
+ shadow
+
+ Draws
+ NO
+
+ stroke
+
+ Draws
+ NO
+
+
+
+ CanvasOrigin
+ {0, 0}
+ ColumnAlign
+ 1
+ ColumnSpacing
+ 36
+ CreationDate
+ 2010-12-30 19:08:43 +0900
+ Creator
+ Ohler Peter
+ DisplayScale
+ 1.000 cm = 1.000 cm
+ FileType
+ flat
+ GraphDocumentVersion
+ 6
+ GraphicsList
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 90
+
+ ID
+ 95
+ Points
+
+ {325.484, 404.043}
+ {298.136, 404.177}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 91
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 89
+
+ ID
+ 94
+ Points
+
+ {254.618, 404.043}
+ {227.27, 404.177}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 90
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 88
+
+ ID
+ 93
+ Points
+
+ {183.752, 404.043}
+ {156.404, 404.177}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 89
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 87
+
+ ID
+ 92
+ Points
+
+ {112.886, 404.043}
+ {85.538, 404.177}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 88
+
+
+
+ Bounds
+ {{325.984, 382.677}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 91
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{255.118, 382.677}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 90
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{184.252, 382.677}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 89
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{113.386, 382.677}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 88
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{42.5197, 382.677}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 87
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{311.811, 368.504}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 86
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0.501961
+ g
+ 0.501961
+ r
+ 0.501961
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{240.945, 368.504}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 85
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{170.079, 368.504}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 84
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 1
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{99.2127, 368.504}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 83
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 1
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{28.3465, 368.504}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 82
+ Shape
+ Rectangle
+ Style
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 76
+
+ ID
+ 81
+ Points
+
+ {325.484, 319.004}
+ {298.136, 319.138}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 77
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 75
+
+ ID
+ 80
+ Points
+
+ {254.618, 319.004}
+ {227.27, 319.138}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 76
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 74
+
+ ID
+ 79
+ Points
+
+ {183.752, 319.004}
+ {156.404, 319.138}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 75
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 73
+
+ ID
+ 78
+ Points
+
+ {112.886, 319.004}
+ {85.538, 319.138}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 74
+
+
+
+ Bounds
+ {{325.984, 297.638}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 77
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{255.118, 297.638}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 76
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{184.252, 297.638}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 75
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{113.386, 297.638}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 74
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{42.5197, 297.638}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 73
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{311.811, 283.465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 72
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0.501961
+ g
+ 0.501961
+ r
+ 0.501961
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{240.945, 283.465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 71
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{170.079, 283.465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 70
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 1
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{99.2127, 283.465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 69
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 1
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{28.3465, 283.465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 68
+ Shape
+ Rectangle
+ Style
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 62
+
+ ID
+ 67
+ Points
+
+ {325.484, 233.964}
+ {298.136, 234.098}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 63
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 61
+
+ ID
+ 66
+ Points
+
+ {254.618, 233.964}
+ {227.27, 234.098}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 62
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 60
+
+ ID
+ 65
+ Points
+
+ {183.752, 233.964}
+ {156.404, 234.098}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 61
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 59
+
+ ID
+ 64
+ Points
+
+ {112.886, 233.964}
+ {85.538, 234.098}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 60
+
+
+
+ Bounds
+ {{325.984, 212.598}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 63
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{255.118, 212.598}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 62
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{184.252, 212.598}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 61
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{113.386, 212.598}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 60
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{42.5197, 212.598}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 59
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{311.811, 198.425}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 58
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0.501961
+ g
+ 0.501961
+ r
+ 0.501961
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{240.945, 198.425}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 57
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{170.079, 198.425}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 56
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 1
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{99.2127, 198.425}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 55
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 1
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{28.3465, 198.425}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 54
+ Shape
+ Rectangle
+ Style
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 48
+
+ ID
+ 53
+ Points
+
+ {325.484, 148.823}
+ {298.138, 148.828}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 49
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 47
+
+ ID
+ 52
+ Points
+
+ {254.618, 148.823}
+ {227.272, 148.828}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 48
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 46
+
+ ID
+ 51
+ Points
+
+ {183.752, 148.823}
+ {156.406, 148.828}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 47
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 45
+
+ ID
+ 50
+ Points
+
+ {112.886, 148.823}
+ {85.5393, 148.828}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 46
+
+
+
+ Bounds
+ {{325.984, 127.559}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 49
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{255.118, 127.559}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 48
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{184.252, 127.559}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 47
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{113.386, 127.559}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 46
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{42.5197, 127.559}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 45
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{311.811, 113.386}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 44
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0.501961
+ g
+ 0.501961
+ r
+ 0.501961
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{240.945, 113.386}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 43
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{170.079, 113.386}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 42
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 1
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{99.2127, 113.386}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 41
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 1
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{28.3465, 113.386}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 40
+ Shape
+ Rectangle
+ Style
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 13
+
+ ID
+ 39
+ Points
+
+ {325.484, 63.8035}
+ {298.138, 63.8335}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 14
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 12
+
+ ID
+ 38
+ Points
+
+ {254.618, 63.8204}
+ {227.271, 63.8715}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 13
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 11
+
+ ID
+ 37
+ Points
+
+ {183.752, 63.8077}
+ {156.405, 63.843}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 12
+
+
+
+ Class
+ LineGraphic
+ Head
+
+ ID
+ 10
+
+ ID
+ 35
+ Points
+
+ {112.886, 63.7796}
+ {85.5394, 63.7796}
+
+ Style
+
+ stroke
+
+ HeadArrow
+ 0
+ LineType
+ 1
+ TailArrow
+ 0
+
+
+ Tail
+
+ ID
+ 11
+
+
+
+ Bounds
+ {{325.984, 42.5199}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 14
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{255.118, 42.5199}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 13
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{184.252, 42.5198}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 12
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{113.386, 42.5198}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 11
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{42.5197, 42.5197}, {42.5196, 42.5196}}
+ Class
+ ShapedGraphic
+ ID
+ 10
+ Shape
+ Circle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{311.811, 28.3465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 9
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0.501961
+ g
+ 0.501961
+ r
+ 0.501961
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{240.945, 28.3465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 8
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{170.079, 28.3465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 7
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 0
+ g
+ 1
+ r
+ 1
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{99.2127, 28.3465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 6
+ Shape
+ Rectangle
+ Style
+
+ fill
+
+ Color
+
+ b
+ 1
+ g
+ 0.501961
+ r
+ 0
+
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ Bounds
+ {{28.3465, 28.3465}, {70.8661, 70.8661}}
+ Class
+ ShapedGraphic
+ ID
+ 3
+ Shape
+ Rectangle
+ Style
+
+ shadow
+
+ Draws
+ NO
+
+
+
+
+ GridInfo
+
+ DrawMajorGrid
+ NO
+ GridSpacing
+ 28.346458435058594
+ MajorGridSpacing
+ 1
+ ShowsGrid
+ YES
+ SnapsToGrid
+ YES
+
+ GuidesLocked
+ NO
+ GuidesVisible
+ YES
+ HPages
+ 1
+ ImageCounter
+ 1
+ KeepToScale
+
+ Layers
+
+
+ Lock
+ NO
+ Name
+ Layer 1
+ Print
+ YES
+ View
+ YES
+
+
+ LayoutInfo
+
+ Animate
+ NO
+ circoMinDist
+ 18
+ circoSeparation
+ 0.0
+ layoutEngine
+ dot
+ neatoSeparation
+ 0.0
+ twopiSeparation
+ 0.0
+
+ LinksVisible
+ NO
+ MagnetsVisible
+ NO
+ MasterSheets
+
+ ModificationDate
+ 2010-12-30 19:12:47 +0900
+ Modifier
+ Ohler Peter
+ NotesVisible
+ NO
+ Orientation
+ 2
+ OriginVisible
+ NO
+ PageBreaks
+ YES
+ PrintInfo
+
+ NSBottomMargin
+
+ float
+ 41
+
+ NSLeftMargin
+
+ float
+ 18
+
+ NSPaperName
+
+ string
+ A4
+
+ NSPaperSize
+
+ size
+ {595.28, 841.89}
+
+ NSRightMargin
+
+ float
+ 18
+
+ NSTopMargin
+
+ float
+ 18
+
+
+ PrintOnePage
+
+ ReadOnly
+ NO
+ RowAlign
+ 1
+ RowSpacing
+ 36
+ SheetTitle
+ Canvas 1
+ SmartAlignmentGuidesActive
+ NO
+ SmartDistanceGuidesActive
+ NO
+ UniqueID
+ 1
+ UseEntirePage
+
+ VPages
+ 1
+ WindowInfo
+
+ CurrentSheet
+ 0
+ ExpandedCanvases
+
+
+ name
+ Canvas 1
+
+
+ Frame
+ {{976, 361}, {694, 902}}
+ ListView
+
+ OutlineWidth
+ 142
+ RightSidebar
+
+ ShowRuler
+
+ Sidebar
+
+ SidebarWidth
+ 120
+ VisibleRegion
+ {{0, 0}, {559, 783}}
+ Zoom
+ 1
+ ZoomValues
+
+
+ Canvas 1
+ 1
+ 1
+
+
+
+ saveQuickLookFiles
+ NO
+
+
diff -Nru ruby-ox-2.11.0/test/sample.rb ruby-ox-2.14.9/test/sample.rb
--- ruby-ox-2.11.0/test/sample.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sample.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,55 @@
+#!/usr/bin/env ruby -wW2
+
+if $0 == __FILE__
+ $: << '.'
+ $: << '..'
+ $: << '../lib'
+ $: << '../ext'
+end
+
+require 'pp'
+require 'sample/doc'
+
+
+def sample_doc(size=3)
+ colors = [ :black, :gray, :white, :red, :blue, :yellow, :green, :purple, :orange ]
+
+ d = ::Sample::Doc.new('Sample')
+
+ # add some history
+ (0..size * 10).each do |i|
+ d.add_change("Changed at t+#{i}.")
+ end
+
+ # add some layers
+ (1..size).each do |i|
+ layer = ::Sample::Layer.new("Layer-#{i}")
+ (1..size).each do |j|
+ g = ::Sample::Group.new
+ (1..size).each do |k|
+ g2 = ::Sample::Group.new
+ r = ::Sample::Rect.new(j * 40 + 10.0, i * 10.0,
+ 10.123456 / k, 10.0 / k, colors[(i + j + k) % colors.size])
+ r.add_prop(:part_of, layer.name)
+ g2 << r
+ g2 << ::Sample::Text.new("#{k} in #{j}", r.left, r.top, r.width, r.height)
+ g << g2
+ end
+ g2 = ::Sample::Group.new
+ (1..size).each do |k|
+ o = ::Sample::Oval.new(j * 40 + 12.0, i * 10.0 + 2.0,
+ 6.0 / k, 6.0 / k, colors[(i + j + k) % colors.size])
+ o.add_prop(:inside, true)
+ g << o
+ end
+ g << g2
+ layer << g
+ end
+ d.layers[layer.name] = layer
+ end
+
+ # some properties
+ d.add_prop(:purpose, 'an example')
+
+ d
+end
diff -Nru ruby-ox-2.11.0/test/sax/basic.xml ruby-ox-2.14.9/test/sax/basic.xml
--- ruby-ox-2.11.0/test/sax/basic.xml 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sax/basic.xml 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1 @@
+
diff -Nru ruby-ox-2.11.0/test/sax/handlers.rb ruby-ox-2.14.9/test/sax/handlers.rb
--- ruby-ox-2.11.0/test/sax/handlers.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sax/handlers.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,198 @@
+require 'ox'
+
+class StartSax < ::Ox::Sax
+ attr_accessor :calls
+
+ def initialize()
+ @calls = []
+ end
+
+ def start_element(name)
+ @calls << [:start_element, name]
+ end
+
+ def attr(name, value)
+ @calls << [:attr, name, value]
+ end
+end
+
+class AllSax < StartSax
+ def initialize()
+ super
+ end
+
+ def instruct(target)
+ @calls << [:instruct, target]
+ end
+
+ def end_instruct(target)
+ @calls << [:end_instruct, target]
+ end
+
+ def doctype(value)
+ @calls << [:doctype, value]
+ end
+
+ def comment(value)
+ @calls << [:comment, value]
+ end
+
+ def cdata(value)
+ @calls << [:cdata, value]
+ end
+
+ def text(value)
+ @calls << [:text, value]
+ end
+
+ def end_element(name)
+ @calls << [:end_element, name]
+ end
+
+ def error(message, line, column)
+ @calls << [:error, message, line, column]
+ end
+
+ def abort(name)
+ @calls << [:abort, name]
+ end
+end
+
+class LineColSax < StartSax
+ def initialize()
+ @pos = nil # this initializes the @pos variable which will then be set by the parser
+ @line = nil # this initializes the @line variable which will then be set by the parser
+ @column = nil # this initializes the @line variable which will then be set by the parser
+ super
+ end
+
+ def instruct(target)
+ @calls << [:instruct, target, @pos, @line, @column]
+ end
+
+ def start_element(name)
+ @calls << [:start_element, name, @pos, @line, @column]
+ end
+
+ def end_instruct(target)
+ @calls << [:end_instruct, target, @pos, @line, @column]
+ end
+
+ def doctype(value)
+ @calls << [:doctype, value, @pos, @line, @column]
+ end
+
+ def comment(value)
+ @calls << [:comment, value, @pos, @line, @column]
+ end
+
+ def cdata(value)
+ @calls << [:cdata, value, @pos, @line, @column]
+ end
+
+ def text(value)
+ @calls << [:text, value, @pos, @line, @column]
+ end
+
+ def end_element(name)
+ @calls << [:end_element, name, @pos, @line, @column]
+ end
+
+ def attr(name, value)
+ @calls << [:attr, name, value, @pos, @line, @column]
+ end
+
+ def attrs_done()
+ @calls << [:attrs_done, @pos, @line, @column]
+ end
+
+ def error(message, line, column)
+ @calls << [:error, message, line, column]
+ end
+end
+
+class TypeSax < ::Ox::Sax
+ attr_accessor :item
+ # method to call on the Ox::Sax::Value Object
+ attr_accessor :type
+
+ def initialize(type)
+ @item = nil
+ @type = type
+ end
+
+ def attr_value(name, value)
+ @item = value.send(name)
+ end
+
+ def value(value)
+ @item = value.send(@type)
+ end
+end
+
+class ErrorSax < ::Ox::Sax
+ attr_reader :errors
+
+ def initialize
+ @path = []
+ @tags = []
+ @errors = []
+ end
+
+ def start_element(tag)
+ @tags << tag
+ @path & @tags
+ end
+
+ def error(message, line, column)
+ @errors << message
+ end
+end
+
+class HtmlSax < ::Ox::Sax
+ attr_accessor :calls
+
+ def initialize()
+ @calls = []
+ end
+
+ def start_element(name)
+ @calls << [:start_element, name]
+ end
+
+ def end_element(name)
+ @calls << [:end_element, name]
+ end
+
+ def attr(name, value)
+ @calls << [:attr, name, value]
+ end
+
+ def instruct(target)
+ @calls << [:instruct, target]
+ end
+
+ def end_instruct(target)
+ @calls << [:end_instruct, target]
+ end
+
+ def doctype(value)
+ @calls << [:doctype, value]
+ end
+
+ def comment(value)
+ @calls << [:comment, value]
+ end
+
+ def cdata(value)
+ @calls << [:cdata, value]
+ end
+
+ def text(value)
+ @calls << [:text, value]
+ end
+
+ def error(message, line, column)
+ @calls << [:error, message, line, column]
+ end
+end
diff -Nru ruby-ox-2.11.0/test/sax/helpers.rb ruby-ox-2.14.9/test/sax/helpers.rb
--- ruby-ox-2.11.0/test/sax/helpers.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sax/helpers.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,48 @@
+require 'handlers'
+
+module SaxTestHelpers
+
+ # A helper method to initiate a sax parsing using a specified xml
+ # structure as input, an expected stack of calls, a handler class
+ # and an optional options hash to pass to the parser.
+ #
+ # The options that the parser recognizes are :convert_special and
+ # :smart, which have both boolean values.
+ #
+ # E.g.
+ # parse_compare(%{ This is some text.},
+ # [[:start_element, :top],
+ # [:text, " This is some text."],
+ # [:end_element, :top]
+ # ], AllSax, :convert_special => true, :smart => true)
+ #
+ def parse_compare(xml, expected, handler_class = AllSax, opts = {}, handler_attr = :calls)
+ handler = handler_class.new()
+ input = StringIO.new(xml)
+ options = {
+ :symbolize => true,
+ #:convert_special => true,
+ :smart => false
+ }.merge(opts)
+
+ Ox.sax_parse(handler, input, options)
+
+ actual = handler.send(handler_attr)
+
+ if expected != actual
+ expected.each_index { |i|
+ if expected[i] != actual[i]
+ puts "#{i}: #{expected[i]} != #{actual[i]}"
+ end
+ }
+ end
+ puts "\nexpected: #{expected}\n actual: #{actual}" if expected != actual
+ assert_equal(expected, actual)
+ end
+
+ # This is needed to stop test/unit from complaining that there is no test
+ # specified.
+ def test_hack
+ assert(true)
+ end
+end
diff -Nru ruby-ox-2.11.0/test/sax/sax_check.rb ruby-ox-2.14.9/test/sax/sax_check.rb
--- ruby-ox-2.11.0/test/sax/sax_check.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sax/sax_check.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,42 @@
+#!/usr/bin/env ruby
+# encoding: UTF-8
+
+# Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
+# required. That can be set in the RUBYOPT environment variable.
+# export RUBYOPT=-w
+
+$VERBOSE = true
+
+$: << File.join(File.dirname(__FILE__), "../../lib")
+$: << File.join(File.dirname(__FILE__), "../../ext")
+
+require 'ox'
+
+class QuietSax < Ox::Sax
+
+ def initialize()
+ @line = nil
+ @column = nil
+ end
+ def start_element(name)
+ puts "Start #{name} @ #{@column}"
+ end
+ def end_element(name)
+ puts "End #{name} @ #{@line}:#{@column}"
+ end
+ def attr(name, value); end
+ def instruct(target); end
+ def end_instruct(target); end
+ def doctype(value); end
+ def comment(value); end
+ def cdata(value); end
+ def text(value)
+ puts "text #{value.length} @ #{@line}:#{@column}"
+ end
+ def error(message, line, column)
+ puts "Error: #{message} @ #{line}:#{column}"
+ end
+end
+
+handler = QuietSax.new()
+Ox.sax_parse(handler, ARGF, :smart => true)
diff -Nru ruby-ox-2.11.0/test/sax/sax_test.rb ruby-ox-2.14.9/test/sax/sax_test.rb
--- ruby-ox-2.11.0/test/sax/sax_test.rb 1970-01-01 00:00:00.000000000 +0000
+++ ruby-ox-2.14.9/test/sax/sax_test.rb 2022-02-10 23:24:14.000000000 +0000
@@ -0,0 +1,1320 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+# Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
+# required. That can be set in the RUBYOPT environment variable.
+# export RUBYOPT=-w
+
+$VERBOSE = true
+
+$: << File.join(File.dirname(__FILE__), "../../lib")
+$: << File.join(File.dirname(__FILE__), "../../ext")
+$: << File.join(File.dirname(__FILE__), ".")
+
+require 'stringio'
+require 'bigdecimal'
+require 'test/unit'
+require 'optparse'
+
+require 'ox'
+
+require 'helpers'
+#require 'smart_test'
+
+opts = OptionParser.new
+opts.on("-h", "--help", "Show this display") { puts opts; Process.exit!(0) }
+opts.parse(ARGV)
+
+$ox_sax_options = {
+ :encoding=>nil,
+ :indent=>2,
+ :trace=>0,
+ :with_dtd=>false,
+ :with_xml=>false,
+ :with_instructions=>false,
+ :circular=>false,
+ :xsd_date=>false,
+ :mode=>:object,
+ :symbolize_keys=>true,
+ :skip=>:skip_return,
+ :smart=>false,
+ :convert_special=>true,
+ :effort=>:strict,
+ :invalid_replace=>'',
+ :strip_namespace=>false
+}
+
+class SaxBaseTest < ::Test::Unit::TestCase
+ include SaxTestHelpers
+
+ def test_sax_io_pipe
+ Ox::default_options = $ox_sax_options
+ handler = AllSax.new()
+ input,w = IO.pipe
+ w << %{}
+ w.close
+ Ox.sax_parse(handler, input)
+ assert_equal([[:start_element, :top],
+ [:end_element, :top]], handler.calls)
+ end
+
+ def test_sax_file
+ Ox::default_options = $ox_sax_options
+ handler = AllSax.new()
+ input = File.open(File.join(File.dirname(__FILE__), 'basic.xml'))
+ Ox.sax_parse(handler, input)
+ input.close
+ assert_equal([[:start_element, :top],
+ [:end_element, :top]], handler.calls)
+ end
+
+ def test_sax_file_line_col
+ Ox::default_options = $ox_sax_options
+ handler = LineColSax.new()
+ input = File.open(File.join(File.dirname(__FILE__), 'trilevel.xml'))
+ Ox.sax_parse(handler, input)
+ input.close
+ assert_equal([[:instruct, "xml", 1, 1, 1],
+ [:attr, :version, "1.0", 15, 1, 15],
+ [:attrs_done, 15, 1, 15],
+ [:end_instruct, "xml", 20, 1, 20],
+ [:start_element, :top, 23, 2, 1],
+ [:attrs_done, 23, 2, 1],
+ [:start_element, :child, 31, 3, 3],
+ [:attrs_done, 31, 3, 3],
+ [:start_element, :grandchild, 43, 4, 5],
+ [:attrs_done, 43, 4, 5],
+ [:end_element, :grandchild, 55, 4, 17],
+ [:end_element, :child, 59, 5, 3],
+ [:end_element, :top, 68, 6, 1]], handler.calls)
+ end
+
+ def test_sax_io_file
+ Ox::default_options = $ox_sax_options
+ handler = AllSax.new()
+ input = IO.open(IO.sysopen(File.join(File.dirname(__FILE__), 'basic.xml')))
+ Ox.sax_parse(handler, input)
+ input.close
+ assert_equal([[:start_element, :top],
+ [:end_element, :top]], handler.calls)
+ end
+
+ def test_sax_instruct_simple
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{}, [[:instruct, 'xml'],
+ [:end_instruct, 'xml']])
+ end
+
+ def test_sax_instruct_blank
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{}, [], StartSax)
+ end
+
+ def test_sax_instruct_attrs
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:attr, :encoding, "UTF-8"],
+ [:end_instruct, 'xml']])
+ end
+
+ def test_sax_instruct_noattrs
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{},
+ [[:instruct, 'bad'],
+ [:text, "something"],
+ [:end_instruct, 'bad']])
+ end
+
+ def test_sax_instruct_loose
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{ xml
+version = "1.0"
+encoding = "UTF-8" ?>},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:attr, :encoding, "UTF-8"],
+ [:end_instruct, 'xml']])
+ end
+
+ def test_sax_instruct_pi
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+ This is a string.
+
+
+},
+ [[:instruct, 'pro'],
+ [:attr, :cat, "quick"],
+ [:end_instruct, 'pro'],
+ [:start_element, :top],
+ [:start_element, :str],
+ [:text, "This is a "],
+ [:instruct, "attrs"],
+ [:attr, :dog, "big"],
+ [:end_instruct, "attrs"],
+ [:text, " string."],
+ [:end_element, :str],
+ [:instruct, "content"],
+ [:text, "dog is big"],
+ [:end_instruct, "content"],
+ [:end_element, :top]])
+ end
+
+ def test_sax_instruct_no_term
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{},
+ [[:instruct, "xml"],
+ [:error, "Not Terminated: instruction not terminated", 1, 6],
+ [:end_instruct, "xml"],
+ [:start_element, :top],
+ [:end_element, :top]])
+ end
+
+ def test_sax_element_simple
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{},
+ [[:start_element, :top],
+ [:end_element, :top]])
+ end
+
+ def test_sax_element_attrs
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{},
+ [[:start_element, :top],
+ [:attr, :x, "57"],
+ [:attr, :y, "42"],
+ [:error, "Unexpected Character: attribute value not in quotes", 1, 22],
+ [:attr, :z, "33/"],
+ [:error, "Start End Mismatch: element 'top' not closed", 1, 25],
+ [:end_element, :top]])
+ end
+
+ def test_sax_element_start_end
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{},
+ [[:start_element, :top],
+ [:text, ""],
+ [:end_element, :top]])
+ end
+
+ def test_sax_two_top
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{},
+ [[:start_element, :top],
+ [:end_element, :top],
+ [:error, "Out of Order: multiple top level elements", 1, 7],
+ [:start_element, :top],
+ [:end_element, :top]])
+ end
+
+ def test_sax_nested_elements
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+
+
+},
+ [[:start_element, :top],
+ [:start_element, :child],
+ [:start_element, :grandchild],
+ [:end_element, :grandchild],
+ [:end_element, :child],
+ [:end_element, :top],
+ ])
+ end
+
+ def test_sax_elements_case
+ Ox::default_options = $ox_sax_options
+ Ox::default_options = { :smart => true, :skip => :skip_white }
+ parse_compare(%{
+
+
+
+
+},
+ [[:start_element, :top],
+ [:start_element, :Child],
+ [:start_element, :grandchild],
+ [:end_element, :grandchild],
+ [:end_element, :Child],
+ [:end_element, :top],
+ ], AllSax, { :smart => true })
+ end
+
+ def test_sax_nested1
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+
+
+
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, 'xml'],
+ [:start_element, :top],
+ [:start_element, :child],
+ [:start_element, :grandchild],
+ [:end_element, :grandchild],
+ [:end_element, :child],
+ [:end_element, :top],
+ ])
+ end
+
+ def test_sax_line_col
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+
+
+
+
+ Some text.
+
+
+
+
+},
+ [[:instruct, 'xml', 1, 1, 1],
+ [:attr, :version, "1.0", 15, 1, 15],
+ [:attrs_done, 15, 1, 15],
+ [:end_instruct, 'xml', 20, 1, 20],
+ [:doctype, " top PUBLIC \"top.dtd\"", 23, 2, 1],
+ [:start_element, :top, 55, 3, 1],
+ [:attr, :attr, "one", 65, 3, 11],
+ [:attrs_done, 65, 3, 11],
+ [:comment, " family ", 74, 4, 3],
+ [:start_element, :child, 92, 5, 3],
+ [:attrs_done, 92, 5, 3],
+ [:start_element, :grandchild, 104, 6, 5],
+ [:attrs_done, 104, 6, 5],
+ [:end_element, :grandchild, 116, 6, 17],
+ [:cdata, "see data.", 122, 7, 5],
+ [:end_element, :child, 146, 8, 3],
+ [:text, "Some text.\n", 154, 8, 10],
+ [:end_element, :top, 165, 9, 1],
+ [:error, "Out of Order: multiple top level elements", 10, 1],
+ [:start_element, :second, 172, 10, 1],
+ [:attrs_done, 172, 10, 1],
+ [:start_element, :inside, 183, 11, 3],
+ [:attrs_done, 183, 11, 3],
+ [:end_element, :inside, 191, 11, 11],
+ [:end_element, :second, 193, 12, 1],
+ ], LineColSax)
+ end
+
+ def test_sax_element_name_mismatch
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+
+
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, "xml"],
+ [:start_element, :top],
+ [:start_element, :child],
+ [:start_element, :grandchild],
+ [:end_element, :grandchild],
+ [:error, "Start End Mismatch: element 'parent' closed but not opened", 5, 3],
+ [:start_element, :parent],
+ [:end_element, :parent],
+ [:error, "Start End Mismatch: element 'top' close does not match 'child' open", 6, 1],
+ [:end_element, :child],
+ [:end_element, :top],
+ ])
+ end
+
+ def test_sax_nested
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+
+
+
+
+
+
+
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, 'xml'],
+ [:start_element, :top],
+ [:start_element, :child],
+ [:start_element, :grandchild],
+ [:end_element, :grandchild],
+ [:end_element, :child],
+ [:start_element, :child],
+ [:start_element, :grandchild],
+ [:end_element, :grandchild],
+ [:start_element, :grandchild],
+ [:end_element, :grandchild],
+ [:end_element, :child],
+ [:end_element, :top],
+ ])
+ end
+
+ def test_sax_element_no_term
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+},
+ [[:start_element, :top],
+ [:start_element, :child],
+ [:end_element, :child],
+ [:error, "Start End Mismatch: element 'top' not closed", 4, 0],
+ [:end_element, :top],
+ ])
+ end
+
+ def test_sax_text
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{This is some text.},
+ [[:start_element, :top],
+ [:text, "This is some text."],
+ [:end_element, :top]
+ ])
+ end
+
+ def test_sax_open_close_element
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{},
+ [[:start_element, :top],
+ [:start_element, :mid],
+ [:text, ""],
+ [:end_element, :mid],
+ [:end_element, :top]
+ ])
+ end
+
+ def test_sax_empty_element
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{ },
+ [[:start_element, :top],
+ [:text, " "],
+ [:end_element, :top]
+ ], AllSax, { :skip => :skip_white })
+ end
+
+ def test_sax_empty_element_name
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+ <>
+ zzz
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, "xml"],
+ [:start_element, :top],
+ [:error, "Invalid Format: empty element", 3, 3],
+ [:start_element, :abc],
+ [:attr, :x, "1"],
+ [:text, "zzz"],
+ [:end_element, :abc],
+ [:end_element, :top],
+ ])
+ end
+
+ def test_sax_empty_element_noskip
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{ },
+ [[:text, " "],
+ [:start_element, :top],
+ [:text, " "],
+ [:end_element, :top]
+ ], AllSax, { :skip => :skip_none })
+ end
+
+ def test_sax_special
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{This is <some> π text.},
+ [[:start_element, :top],
+ [:attr, :name, 'A&Z'],
+ [:text, "This is \u03c0 text."],
+ [:end_element, :top]
+ ], AllSax, :convert_special => true)
+ end
+
+ def test_sax_bad_special
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{&example;},
+ [[:start_element, :top],
+ [:error, "Invalid Format: Invalid special character sequence", 1, 11],
+ [:attr, :name, '&example;'],
+ [:error, "Invalid Format: Invalid special character sequence", 1, 22],
+ [:text, "&example;"],
+ [:end_element, :top]
+ ], AllSax, :convert_special => true)
+ end
+
+ def test_sax_ignore_special
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{&example;},
+ [[:start_element, :top],
+ [:attr, :name, '&example;'],
+ [:text, "&example;"],
+ [:end_element, :top]
+ ], AllSax, :convert_special => false)
+ end
+
+ def test_sax_not_special
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{This is <some> text.},
+ [[:start_element, :top],
+ [:attr, :name, 'A&Z'],
+ [:text, "This is <some> text."],
+ [:end_element, :top]
+ ], AllSax, :convert_special => false)
+ end
+
+ def test_sax_special_default
+ Ox::default_options = $ox_sax_options
+ orig = Ox::default_options[:convert_special]
+ Ox::default_options = { :convert_special => true }
+ parse_compare(%{This is <some> text.},
+ [[:start_element, :top],
+ [:attr, :name, 'A&Z'],
+ [:text, "This is text."],
+ [:end_element, :top]
+ ], AllSax)
+ Ox::default_options = { :convert_special => orig }
+ end
+
+ def test_sax_not_special_default
+ Ox::default_options = $ox_sax_options
+ orig = Ox::default_options[:convert_special]
+ Ox::default_options = { :convert_special => false }
+ parse_compare(%{This is <some> text.},
+ [[:start_element, :top],
+ [:attr, :name, 'A&Z'],
+ [:text, "This is <some> text."],
+ [:end_element, :top]
+ ], AllSax)
+ Ox::default_options = { :convert_special => orig }
+ end
+
+ def test_sax_whitespace
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{ This is some text.},
+ [[:start_element, :top],
+ [:text, " This is some text."],
+ [:end_element, :top]
+ ])
+ end
+
+ def test_sax_text_no_term
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{This is some text.},
+ [[:start_element, :top],
+ [:error, "Not Terminated: text not terminated", 1, 23],
+ [:text, "This is some text."],
+ [:error, "Start End Mismatch: element 'top' not closed", 1, 23],
+ [:end_element, :top]])
+ end
+ # TBD invalid chacters in text
+
+ def test_sax_doctype
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, 'xml'],
+ [:doctype, ' top PUBLIC "top.dtd"'],
+ [:start_element, :top],
+ [:end_element, :top]])
+ end
+
+ def test_sax_doctype_big
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+]>
+
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, 'xml'],
+ [:doctype, %{ Objects []}],
+ [:start_element, :top],
+ [:end_element, :top]])
+ end
+
+ def test_sax_doctype_bad_order
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, 'xml'],
+ [:start_element, :top],
+ [:end_element, :top],
+ [:error, "Out of Order: DOCTYPE can not come after an element", 3, 10],
+ [:doctype, ' top PUBLIC "top.dtd"']])
+ end
+
+ def test_sax_doctype_space
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+
+},
+ [[:error, "Unexpected Character:
+
+
+},
+ [[:start_element, :top],
+ [:error, "Unexpected Character: DOCTYPE, CDATA, or comment expected", 2, 6],
+ [:end_element, :top]])
+ end
+
+ def test_sax_comment_simple
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, 'xml'],
+ [:comment, 'First comment.']])
+ end
+
+ def test_sax_comment
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+
+BeforeAfter
+},
+ [[:instruct, 'xml'],
+ [:attr, :version, "1.0"],
+ [:end_instruct, 'xml'],
+ [:comment, 'First comment.'],
+ [:start_element, :top],
+ [:text, 'Before'],
+ [:comment, 'Nested comment.'],
+ [:text, 'After'],
+ [:end_element, :top]])
+ end
+
+ def test_sax_comment_no_term
+ Ox::default_options = $ox_sax_options
+ parse_compare(%{
+