Xpact Milestone: 177,347 Real-World XML Files with Zero Failures — 100% Agreement

by Finnian Reilly (modified: 2026 Aug 18)

Part I: a 1.37 million file correctness sweep

Rustification — make it memory-safe.
Contractification — make it correct by contract.

A new milestone has been reached in an effort to implement the goal described in this article:

Finding a billion-user project for Eiffel: How DbC catches the security flaws that Rust misses

The goal is to follow the "Rustification trend" and make a security hardened Eiffel XML parser as a drop in replacement for the popular eXpat program found in tens of thouands of software packages. The gateway to popularize the implementation is through the Python community who rely heavily on eXpat. Like all C programs, eXpat is a potential source of security vulnerabilities many of which have been documented.

It is worth being concrete about what a successful xpact project would change for Eiffel's visibility.

Phase 1: credible release. xpact passes the complete libexpat test suite. The benchmark results are published — honestly, including areas where performance is not yet competitive and why. The contract annotations are visible in the public repository. This is achievable by a small team and represents the minimum credible statement.

Phase 2: Python package. The PyPI package ships. Python developers can install xpact and use it as an alternative XML parsing backend. Security-conscious Python teams have a concrete choice to make. This is where community discovery begins.

Phase 3: security community engagement. The essay arguing that xpact's DbC methodology would have prevented specific historical CVEs is published. The argument is presented at a security conference — PyCon, linux.conf.au, or a dedicated security track. The comparison with sudo-rs's logic-error CVEs makes the argument timely and concrete.

Phase 4: OSS-Fuzz integration. xpact joins libexpat in the OSS-Fuzz continuous fuzzing infrastructure. Any vulnerabilities found are fixed quickly and transparently. A clean OSS-Fuzz record over time is a durable credibility signal.

Phase 5: downstream adoption. A Linux distribution packages xpact as an alternative to libexpat. A Python project of note switches its XML processing to xpact and publishes the reasoning. These are the events that generate the kind of coverage that introduces Eiffel to developers who have never encountered it.

None of this requires Eiffel to become the next Python or Rust. It requires one well-executed, well-documented, well-argued project to demonstrate that Eiffel is a serious choice for real systems programming work. The visibility benefit accrues to the whole language from a single flagship project.

Important Python packages/modules relying on Expat
Package / Module Expat dependency How Expat is used Importance
xml.parsers.expat Direct Python's direct binding to the Expat parser via pyexpat Very high
xml.etree.ElementTree Direct/indirect XML parsing through CPython's Expat-backed parser Very high
xml.sax Direct/indirect Default SAX parser is Expat-based Very high
xml.dom.minidom Indirect Uses SAX/XML parsing machinery High
xml.dom.pulldom Indirect Built on SAX parsing Medium
xmlrpc.client Indirect Parses XML-RPC requests and responses using Python's XML stack High
plistlib Indirect Uses ElementTree for XML-format property lists Medium
defusedxml Indirect Security-hardened wrappers around Python's XML parsers, including ElementTree and SAX High
lxml No Uses libxml2, not Expat N/A
expatriate Direct Python XML library built on Expat Medium
BeautifulSoup Possible/indirect Can use Python's XML/SAX machinery depending on parser configuration Medium
feedparser Possible/indirect XML/RSS/Atom feed parsing; parser implementation varies by version/configuration High

A New Verification Tool

A class FILE_SYSTEM_XML_HUNTER was developed for the purpose of separately scanning a Linux Mint 22 partition and a mounted Windows 11 partition for anything that might conceiveably be an xml file, and then run a comprehensive Xpact — eXpat comparison.

The selection criteria was case-insensitive extension matching of over 100 common XML extensions curated by Claude AI.

new_xml_extensions: STRING do Result := "3mf;adml;admx;apk;appx;appxbundle;atom;axaml;config;csproj;dae;docm;docx;dotm;dotx;eant;ecf;% %epub;fb2;fodg;fodp;fods;fodt;fsproj;glade;gml;gpx;html;iml;ivy;kml;kmz;manifest;mathml;mml;msix;% %msixbundle;mum;ncx;nuspec;odb;odc;odf;odg;odi;odm;odp;ods;odt;opf;opml;otg;otp;ots;ott;owl;plist;% %pom;potm;potx;ppsm;ppsx;pptm;pptx;props;pubxml;rdf;resw;resx;rng;rss;ruleset;saml;sitemap;soap;svg;% %svgz;targets;tld;tmx;vbproj;vcxproj;vsixmanifest;wadl;wsdl;wsp;wxi;wxs;x3d;xacml;xaml;xamlx;% %xbrl;xht;xhtml;xlam;xlf;xliff;xlsm;xlsx;xltm;xltx;xml;xsd;xsl;xslt;xsp;xul" end

The two reports validate that the list is doing real work. The NTFS scan surfaced .manifest (38,356 occurrences) and .svg (13,063) as the two largest single categories. The ext4 scan by contrast surfaced .svg as overwhelmingly dominant (83,411, about 81% of that partition's XML total), consistent with icon themes under /usr/share/icons.

Target Filesystem System files scanned XML files tested Zipped office packages tested Failures
Linux root (/) ext4 1,023,441 103,270 451 0
Mounted Windows 11 partition NTFS 347,550 74,077 582 0
Combined 1,370,991 177,347 1033 0

That combined XML figure, 177,000 plus real-world files pulled found on two production partitions rather than a curated test corpus, is the number that matters. It is roughly 47,000 more than the previous 130,000 file milestone.

What actually gets compared, and how agreement is decided

Each qualifying non-archive file is handed to XT_EXPAT_COMPARISON.execute, which loops over Parse_data_types, the seven parse-event categories text, cdata, comment, tag, attribute, pi-name and pi-data. For each one it:

  1. Runs Xpact's own XT_CRC_32_GENERATOR over the file, filtering the callback stream down to that one data type and folding it into a CRC-32 digest.
  2. Shells out to the reference xml_crc_32 C program, built against libexpat, with the same file and the same -type, and parses its standard output and standard error.
  3. Compares the two.

The interesting design decision is in both_agree, which has to handle three outcomes rather than two: both parsers succeed and the checksums match; both parsers succeed and the checksums disagree, indicating a real bug; or both parsers fail to parse the document at all, in which case the question becomes whether they failed for the same reason.

both_agree: BOOLEAN local index_colon: INTEGER do if both_failed then index_colon := expat_error.index_of (':', 1) if index_colon > 0 then Result := expat_error.same_caseless_characters (xpact_error, 1, xpact_error.count, index_colon + 2) end elseif pass_count = Parse_data_types.count then Result := True end end

This is where the "same basic error message" behaviour is implemented, and it is cleverer than a plain string equality test. The reference C program formats its failure as:

fprintf(stderr, "Parse error: %s at line %lu\n", XML_ErrorString(XML_GetErrorCode(parser)), ...);

so expat_error looks like Parse error: not well-formed (invalid token) at line 5327. index_colon finds the first colon, immediately after "Parse error", and same_caseless_characters then compares only as many characters as xpact_error actually has, starting two positions after that colon. In other words it compares expat's message text against Xpact's message text for exactly the length of Xpact's string, and simply stops before reaching expat's trailing at line 5327. Xpact does not as yet produce a line number but for the time being it only needs to produce the same diagnosis.

That in turn works only because Error_descriptions in XT_PARSE_ERROR_CONSTANTS is a AI-transcribed copy of expat's own XML_ErrorString table, holding "Not well-formed (invalid token)", "Undefined entity", "Duplicate attribute" and the rest in the same order as expat's XML_Error enum, with a random_order_check postcondition on the once function that spot-checks several entries by index to guard against the list silently drifting out of sync during editing. Zero failures across 187,000 files means that every parse-error diagnosis Xpact produced, on every malformed document it encountered in the wild, matched expat's own wording. That is a considerably stronger claim than "both parsers reject the same files".

The undefined-entity permission logic

XT_XML_PROLOG_PARSER.permit_undefined_entities is a good example of extra code whose entire purpose is specification-compliance nuance with no available shortcut:

permit_undefined_entities: BOOLEAN local parameter: XT_PARAMETER_ENTITY do if is_standalone then Result := False elseif DTD_uri.starts_with (Http) then Result := True elseif attached parameter_entity_table as table then from table.start until table.after or Result loop parameter := table.item_for_iteration if Valid_external_id_list.has (parameter.external_id) then Result := parameter.is_referenced end table.forth end end end

The rule in the XML Recommendation is that a non-validating processor is not required to read an external DTD subset, but if it does not, it also cannot know for certain whether a given entity was declared there. It is therefore explicitly permitted, though not obligated, to treat an otherwise-undefined entity reference as non-fatal, provided the document's structure indicates that external markup declarations might exist and might matter. Xpact implements that as a three-way decision rather than a single flag:

  1. If the document declares itself standalone="yes" there is no ambiguity. No external subset is in play, so an undefined entity is unconditionally an error.
  2. If the DOCTYPE's external identifier is an HTTP URI, Xpact treats that as a strong signal that an external subset exists and is expected to be consulted, even though Xpact itself never fetches it, and so is lenient.
  3. Otherwise it inspects the internal subset for parameter-entity references pulling in external content, of the shape noted in the code's own comment:

<!DOCTYPE xsl:stylesheet [ <!ENTITY % selectors SYSTEM "db-selectors.mod"> %selectors; ]>

If a parameter entity with a recognised external identifier (Valid_external_id_list) is actually referenced (is_referenced) rather than merely declared, leniency is switched on for the rest of the document.

The result is cached once per prolog into attribute_intervals.permit_undefined_entities, via attributes.set_permit_undefined_entities (permit_undefined_entities) fired the moment the parser transitions out of the prolog at Tok_instance_start. It is consumed downstream in XT_ATTRIBUTE_BUFFER_INTERVALS exactly where an entity reference inside an attribute value gets expanded:

if entity_table.undefined_entity_found and then not permit_undefined_entities then Result := Error_undefined_entity else -- expand and continue end

This is a good illustration in miniature of the central claim in the DbC essay. Getting this wrong in either direction is a genuine correctness bug rather than a style choice. Reject too eagerly and Xpact fails documents expat accepts, which shows up immediately as a false positive in the CRC-32 comparison harness. Reject too rarely and it has silently accepted a document containing a genuinely undefined entity, which is exactly the kind of "parses fine but is subtly wrong" defect a memory-safe rewrite would not catch, because it is not a memory-safety question at all. It is a specification-conformance question, decided by document structure examined during parsing. Design by Contract does not make this function easier to write, but it does make the three branches auditable as explicit conditions rather than folded into scanner state, and it means a future contributor changing the logic gets an immediate, local, mechanically checked signal rather than a downstream symptom hundreds of lines away if the change breaks one of the invariants the rest of the attribute-expansion code assumes about when leniency applies.

The archive-recursion path

Files that are not XML themselves but are ZIP containers (.docx, .odt, .epub, .pptx and so on, 1033 of them combining both runs) are detected by magic bytes rather than extension, via XT_FILE_ROUTINES_I.is_zip_archive checking for the PK signature and any of the three known local-header, end-of-central-directory or spanned-archive byte pairs. Each is handed off to FILE_PACKAGE_TESTS, which shells out to unzip -q into a scratch directory under Environment.temporary_command_path, re-derives the internal wildcard set appropriate to that specific package type via Internal_extension_table (an OOXML .docx contains .xml and .rels; an ODF .odt contains content.xml, styles.xml, meta.xml), and then recursively re-enters the same do_with handler machinery. testing_package is set to True so that the recursive call records occurrences into occurrence_table without re-triggering archive detection on the contents.

On full success, where l_fail_count = 0, the scratch extraction directory and its log directory are removed immediately, so a clean 1033-package run leaves no residue. Only failing packages leave extracted content and a log behind for inspection. fail_count is accumulated back up into the outer hunter via package_tests.sum_fail_count, so a single malformed content.xml three directories deep inside a .docx still surfaces as a top-level failure in the final report. The reports show that none did.

HTML as a source of edge cases, and the regression corpus it built

Across the full-disk sweep, .html files turned out to be a disproportionately good source of failures. Not because HTML itself is unusual, but because real-world HTML tolerates constructs the XML Recommendation simply forbids, and the two-parser comparison surfaces every one of them as a discrepancy rather than a shared shrug. Lower-cased <!doctype html> declarations, content following the closing root element (trailing comments, stray text, repeated closing tags), attribute lists with genuine duplicate names, entities assumed rather than declared, and external DTD identifiers with no real external subset behind them all showed up first as real-world .html failures during the file-system runs.

Each one, once diagnosed, was trimmed down to a minimal reproducing case and added to the regression corpus at tools/data. That directory now holds 30 files, a full third of them .html, spanning exactly this category of edge case alongside the existing encoding, entity and DTD-default tests. scripts/basic_test_comparison.sh runs xml_reader -test_files "tools/data/*.*" against all 30 in a fraction of a second, giving an effectively instant regression check against eXpat every time a scanner or attribute-handling change is made. The full-disk run finds the incompatibility once; the trimmed corpus makes sure it never comes back unnoticed.

Binary files masquerading as XML: the invalid-token vs syntax-error distinction

The corpus walk on Windows turned up a specific, recurring trap: files with legitimate XML extensions — .admx, .manifest, .mum — that are not actually XML at all.

A representative case is:

C:\Windows\WinSxS\amd64_microsoft-windows-deviceaccess_31bf3856ad364e35_10.0.26100.4202_none_a94ac2308a15fa4a\r\AppPrivacy.admxThis is a WinSxS servicing artifact whose contents are binary — Microsoft delta-compression payload, not policy XML — despite the extension promising otherwise. eXpat and Xpact both correctly reject the file, but "reject" is not a single outcome: eXpat distinguishes between XML_ERROR_INVALID_TOKEN, raised when a byte sequence fails classification at the tokenizer's byte-class layer before any grammar rule is even consulted, and XML_ERROR_SYNTAX, raised when the byte stream tokenizes cleanly but the resulting token sequence violates the XML grammar. Which one fires depends on exactly where in the binary blob the first disqualifying byte pattern happens to land — and that is determined entirely by the byte's position relative to expat's internal scanner state, not by anything a human would call "the type of error."

Getting Xpact to agree with eXpat on this distinction, byte for byte, turned into a genuine game of whack-a-mole. A binary file might parse several thousand bytes deep — happily consuming whatever incidentally-valid-looking byte sequences appear in the compressed payload — before hitting a byte that breaks tokenization, and whether that byte trips the tokenizer or survives long enough to reach the grammar layer depends on scanner state that has to be replicated with total fidelity, not approximated. Fixing one divergent file by correcting a byte-classification predicate would routinely surface a new divergence in a different file, where the previous behavior had been silently compensating for the bug now being fixed. The only way through was empirical: run the corpus, isolate the exact byte offset of disagreement, trace it back to the specific expat macro governing that byte class, and confirm the fix against the full sweep rather than the single file — since a predicate correct for one binary-masquerading-as-XML file could easily be wrong for the next.

Why this is a milestone

Three things separate this run from the earlier 130,000 file corpus test.

  1. It is opportunistic rather than curated. These are two working partitions' actual contents: package caches, IDE project files, icon themes, WinSxS manifests, LibreOffice document formats, Android APKs, an .epub collection. Nobody selected these files for parser-friendliness. Real-world XML dialects skew heavily toward a handful of shapes, such as SVG icon sets, build-tool project files and office-suite content, and a full disk walk samples that population honestly in a way a hand-built corpus cannot claim to.
  2. It spans the two filesystems that matter most for a general-purpose C-replacement library. libexpat is consumed on both platforms. A systematic advantage or defect on one filesystem that did not show up on the other would be exactly the kind of thing a synthetic test suite misses.
  3. Agreement was tested at two independent levels simultaneously. Bit-for-bit CRC-32 identity on seven distinct categories of parsed content for every well-formed file, and identical error diagnosis for every malformed one. Zero failures on either axis across 186,726 files is a materially stronger correctness claim than "parses the same files". It is "extracts the same content, and rejects for the same reason, at native scale".

For the thesis of the DbC essay, that Design by Contract catches security-relevant logic errors which expat-equivalent code, and by extension a naive memory-safe rewrite, could pass through unnoticed, this result matters because it retires the obvious rebuttal: fine on your test files, but does it hold up on messy real-world XML? It also directly de-risks two items on the horizon. The Python pyexpat replacement lives or dies on drop-in behavioural fidelity against real-world documents rather than on a specification-conformance test suite.

The next goal: Defusing the "billion-LOL" Bomb With Eiffel

One of the most common attacks is the XML Bomb, also known as the billion laughs attack. The attack exploits entity expansion in DTD to blow up the memory and occupy the CPU for as long as possible. All you need to stop an unprotected web server from receiving new traffic are these few lines of XML code:

<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ELEMENT lolz (#PCDATA)> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz>We need to demonstrate what an Eiffel solution to this classic attack would look like.

Part II: performance analysis against document structure

Methodology

Each entry in benchmark-report.txt comes from XT_BENCHMARK_COMPARISON.execute, which repeatedly re-parses the same file in process for a fixed wall-clock window (duration_ms), incrementing pass_count each time, and asserting that the checksum is stable across repeats as a guard against non-determinism. The reported ratio is pass_count for Xpact divided by pass_count for eXpat, for the same category, same file and same window. These are therefore pure parse-throughput comparisons, not measurements contaminated by process-launch overhead, since both engines are looped from inside a long-running process on their respective sides, and not measurements of a single lucky run.

The nine files in XML-benchmark-files.tar.Z were extracted and inspected directly rather than characterised from their filenames:

File Size Tags Attributes Comments CDATA Entity refs DOCTYPE
wordnet_glossary-20010201.rdf 15.2 MB 252,027 142,982 0 2,484 99,645 yes
ns_att_test.xml 35.7 MB 50,004 1,388,346 0 0 0 no
recset.xml 30.3 MB 50,105 1,388,671 0 0 0 no
nes96.xml 3.0 MB 74,511 29,479 0 0 39 yes
DTD-attlist-default-values.xml 2.3 MB 82,771 43,841 111 0 164 yes (24 ATTLIST)
recursive-entity-expansion.xml 159 KB 4,702 381 39 15 538 (38 declared) yes
Legislation.xml 424 KB 11,636 13,669 0 0 20 yes
mandarin-names-and-text.xsl 244 KB 5,377 3,086 1 0 0 no
vision2.ecf 12 KB 315 238 0 0 0 no

wordnet_glossary-20010201.rdf: the clearest structural story in the set

This file has by a wide margin the highest speedup ratios in the whole benchmark, x1.25 to x1.45, and is uniformly high across all seven categories rather than favouring one. It is also the slowest file to parse in absolute terms for both engines, with only 31 to 48 passes completing in the fixed window against more than 29,000 for the tiny vision2.ecf. That makes the consistent advantage of roughly 1.3x more significant rather than less, since it is amortised over real and substantial per-pass work rather than over noise.

The structural driver is unambiguous once the contents are examined. The file declares two internal general entities that exist purely to abbreviate long, repeated URIs:

<!ENTITY a 'http://www.cogsci.princeton.edu/~wn/concept#'> <!ENTITY b 'http://www.cogsci.princeton.edu/~wn/schema/'>

and then references &a; 99,643 times, essentially once per RDF resource in the file, in constructions such as rdf:about="&a;100001740". Nearly every element's attribute value in this document therefore routes through entity expansion, the exact code path described under the undefined-entity permission logic in Part I, via entity_table.expanded_value checked against entity_table.undefined_entity_found.

This is precisely the workload the name-interning architecture is built for: expansion of a small, fixed set of entities referenced an enormous number of times, which is exactly what a 512-bucket interning cache with a well-mixed hash (the 0x85EBCA6B multiplicative constant) is designed to make cheap. Repeated lookups of the same short byte sequence collapse to cache hits rather than re-parsing or re-allocating the replacement text each time. Because the benchmark's seven categories all still parse and tokenize the entire document regardless of which callback's output gets hashed, the category changing only what feeds the CRC-32 rather than how much of the file is scanned, this single structural feature of entity-reference density in attribute values explains why the speedup appears almost identically across text, attribute, tag, comment, pi-name and pi-data alike. All of them ride on the same underlying win in the attribute-value expansion hot path.

Testing Chinese Characters: mandarin-names-and-text.xsl

This file is a LibreOffice XSLT stylesheet performing UOF to ODF conversion, under the Mozilla Public License. Only about 2.4% of the characters in the file are non-ASCII, 5,541 of 233,299. This is not a file with a large volume of Mandarin text. What it does have is Mandarin and other CJK characters used as element names, attribute names and namespace prefixes rather than only as text content:

<uof:UOF xmlns:uof="..." xmlns:图="..." xmlns:数="..." xmlns:字="..."> <uof:书签集 uof:locID="u0027"> <uof:书签 uof:名称="{@text:name}" uof:locID="u0028" uof:attrList="名称"> <uof:文本位置 uof:区域引用="{generate-id(.)}" .../> </uof:书签> </uof:书签集> </uof:UOF>

This is the Chinese national UOF (Uniform Office Format) schema, with genuinely Chinese-language QNames: 书签集 for "bookmark set", 名称 for "name", 文本位置 for "text position", all appearing as markup rather than prose. That distinction matters because it is markup names, not character data, that pass through the name-interning cache (name_cache.item, the 512-bucket table) on every single occurrence of every tag and attribute. A multi-byte UTF-8 name costs the interning path no more per byte than an ASCII one, because interning is keyed on raw UTF-8 bytes rather than on decoded code points. That is the "move UTF-8 encoding upstream, hash before allocate" design principle applied to the one place in this corpus where it comes under real pressure: markup names that are themselves multi-byte. The eXpat hashing and interning path was not written with this workload in mind, so the gap opens specifically here.

Two things are worth flagging as counter-evidence to a simpler reading, namely that small files with CJK content simply do better, so that the explanation stays honest:

  • The speedup is uniform across all seven categories, x1.25 to x1.45 with a narrow spread, rather than being concentrated in tag and pi-name where a naming-specific win would be expected to show most sharply. This suggests the benefit is a general full-document parse-throughput effect on this document, most plausibly driven by name interning given the content, but not cleanly isolated to it by the current benchmark design, since every category re-tokenizes the whole file regardless of which callback is hashed. A follow-up benchmark varying only the CJK-in-names fraction while holding size and general structure constant would isolate this more rigorously than the present corpus can.
  • vision2.ecf is a useful counter-example against a pure "small file, high fixed-overhead advantage" story. It is the smallest file in the set at 12 KB and has the lowest speedup, x1.08 to x1.18, rather than the highest. It is plain ASCII and moderately attribute-dense, 238 attributes across 315 tags, with nothing structurally unusual. That the smallest and structurally plainest file shows the smallest gain, while a similarly small but structurally distinctive file shows the largest, is further evidence that the win tracks document structure, specifically CJK QNames stressing the interning path, rather than file size or fixed-overhead amortisation.

The rest of the set

  • recursive-entity-expansion.xml at 159 KB, with 38 declared entities, 538 references and 39 comments, shows a wide internal spread: strong gains on pi-data, cdata and comment at x1.29 to x1.34, but tag_count essentially at parity at x1.01, the lowest figure of any file. That is consistent with a file purpose-built to stress entity-expansion depth. Once expansion cost dominates, the cost is largely shared machinery on both sides, and the categories least entangled with expansion, tag_count being effectively just element counting, show the least differentiation.
  • DTD-attlist-default-values.xml at 2.3 MB, with 24 <!ATTLIST> declarations including #FIXED and #IMPLIED defaults, apparently derived from a freedesktop shared-mime-info style vocabulary, shows the most modest and tightly clustered gains in the set at x1.05 to x1.25. Its distinguishing workload is DTD-driven default attribute injection, with context.default_attribute_values supplying values for attributes the element did not explicitly carry. That is a different code path from both the interning win and pure structural size, and evidently a smaller relative advantage for the current architecture than entity-reference-heavy or CJK-QName-heavy content.
  • ns_att_test.xml and recset.xml at 35.7 MB and 30.3 MB, both Microsoft ADO rowset dumps with single-quoted, heavily namespace-prefixed attributes running to roughly 1.39 million attributes across 50,000 <z:row> records each, show moderate size-amortised gains of x1.11 to x1.29, broadly in line with their attribute density rather than with any single standout category.
  • Legislation.xml and nes96.xml sit in the middle of the pack, x1.02 to x1.16 and x1.04 to x1.31 respectively, without a single dominant structural feature to point to. Both are plain, moderately tag-dense documents without heavy entity or naming pressure, which is consistent with their producing the least distinctive numbers in the set.

The cost of catching duplicate attribute names

One of the cases that came out of the .html sweep described in Part I was genuine duplicate attribute names on a single element. The XML Recommendation treats this as a well-formedness error, but it turns up more often than one would like in hand-authored or legacy HTML. Handling it correctly required has_duplicate_name to check every new attribute against every attribute already seen on that element before accepting it. Because the check relies on reference equality against interned name strings rather than on a hash lookup, it is a linear scan per attribute, which makes it effectively quadratic in the number of attributes on a single element.

That is a real cost at the attribute-dense end of the benchmark corpus. Some files that previously parsed at close to twice the speed of eXpat saw that gap close once the check was switched on, since a document with dozens of attributes per element now pays for dozens of comparisons per element rather than a constant-time accept. It is a textbook correctness-versus-throughput trade, and accepting the file-specific slowdown is exactly what the eXpat-agreement testing in Part I exists to force, rather than parsing a malformed document faster than a conforming parser is entitled to.

The aggregate picture remains comfortably in Xpact's favour. Averaged across the full benchmark suite, Xpact still outperforms eXpat by a factor of 1.231, even with the duplicate-name check paid for on every attribute of every file. The chart below summarises the per-file averages behind that figure, with the low-to-high range across the seven categories shown as a whisker on each bar.

Overall read

The average of x1.231 across the full suite is a fair headline number, but the file-by-file breakdown tells a more useful story for future work. The largest wins in this corpus are not simply a function of file size, but of specific structural features that land squarely on the parts of the architecture that have been deliberately optimised: entity-reference density hitting the interning and expansion path in wordnet_glossary-20010201.rdf, and multi-byte markup names, as opposed to multi-byte text, hitting the same interning path from a different angle in mandarin-names-and-text.xsl. Files with neither feature cluster much closer to the average of 1.2x or below.

A useful next step would be a synthetic benchmark file isolating CJK-QName density from overall file size, holding structure otherwise constant. That would allow the interning advantage to be stated as a clean, controlled number rather than inferred from a real-world file that also has several other things going on.

Part III: Name Cache: Validating Hash Distribution Empirically

Xpact interns every element and attribute name it encounters in a fixed-size hash table XT_NAME_CACHE (607 buckets) rather than allocating a fresh string on each occurrence. For a file like ns_att_test.xml, where a single tag (z:row) repeats 50,000 times, this is the difference between one string allocation and 50,000 of them. The cache also has to hold up across wildly different vocabulary shapes — from files with three distinct names repeated tens of thousands of times, to a file like mandarin-names-and-text.xsl with 263 distinct names spanning ASCII xsl:* elements and multi-byte UTF-8 CJK element names side by side.

Crucially, the lookup itself never touches a STRING object on the hot path. What gets passed in is a window into the raw parse buffer — a start and end index over the same SPECIAL [CHARACTER] array the tokenizer is already scanning — and both the hash and the equality check operate directly against those bytes. A STRING_8 is only ever constructed once, at the moment a name is discovered not to exist in the cache yet, and interned for all future occurrences to reference. Repeated names, however frequent, cost a hash computation and a byte-for-byte comparison against an existing bucket entry — never an allocation.

The hash function that selects a bucket has two properties worth calling out. First, for namespace-qualified names it hashes only the portion after the colon (z:row hashes on row, not z:row), on the reasoning that namespace prefixes are typically low-cardinality and contribute little to spreading names across buckets — the local name is where the real entropy is. This is safe by construction: the hash only decides which bucket to search, while the actual lookup still compares the full qualified name, so two different prefixes sharing a local name simply coexist as separate entries in the same bucket rather than colliding incorrectly. Second, for names five bytes or longer, the function decodes the full leading UTF-8 code point — up to four bytes — before mixing it into the hash, rather than hashing on the raw lead byte alone. This matters specifically for CJK text: most 3-byte UTF-8 sequences share the same lead-byte range, so a naive first-byte hash would cluster nearly every Chinese element name into a handful of buckets.

The corpus data bears this out. Across the nine benchmark files, load factors (distinct names per occupied bucket) ranged from 1.00 to 1.30, with collision rates from 0% up to 23.8% on the most demanding file. Even mandarin-names-and-text.xsl — 263 distinct names, mixing ASCII and multi-byte Unicode identifiers under a shared table — never produced a bucket chain longer than four entries. Given the 607-bucket table is sized for vocabularies well beyond what's realistic in practice (XHTML sits around 120 element types, TEI over 500, DITA 200+), the observed collision behavior on real-world files sits comfortably inside the range the table was designed for, with room to spare.

One design choice is worth being explicit about: the code carries a contract asserting that no bucket chain exceeds four entries, with a comment noting it was tuned against the mandarin-names-and-text.xsl result specifically. This isn't a claim that five-entry chains are impossible or unsafe — a longer chain would simply mean a marginally longer linear scan. Its purpose is narrower: it's a tripwire for the development loop, so that a future change to the hash function which quietly degrades distribution gets flagged immediately inside the IDE, rather than being noticed only after the fact in a benchmark printout.

PART IV: GENERATED REPORTS

Windows 11 Partition Report

Total system files: 347,550 Total zipped archive packages: 582 Extensions sorted in order of occurrence count (Highest first) <msix>: occurrences 136 <msixbundle>: occurrences 102 <ott>: occurrences 79 <odt>: occurrences 65 <dotx>: occurrences 42 <ods>: occurrences 39 <appx>: occurrences 29 <otp>: occurrences 23 <docx>: occurrences 21 <ots>: occurrences 18 <potx>: occurrences 8 <xltx>: occurrences 7 <xlam>: occurrences 5 <odb>: occurrences 4 <xlsx>: occurrences 2 <appxbundle>: occurrences 1 <otg>: occurrences 1 Total XML files: 74,077 Extensions sorted in order of occurrence count (Highest first) <manifest>: occurrences 38356 <svg>: occurrences 13063 <xml>: occurrences 9873 <mum>: occurrences 4727 <html>: occurrences 4308 <xsd>: occurrences 660 <adml>: occurrences 656 <admx>: occurrences 596 <resw>: occurrences 518 <config>: occurrences 468 <resx>: occurrences 270 <xsl>: occurrences 212 <xaml>: occurrences 205 <targets>: occurrences 66 <plist>: occurrences 51 <ruleset>: occurrences 12 <x3d>: occurrences 10 <props>: occurrences 6 <wxs>: occurrences 3 <odc>: occurrences 2 <nuspec>: occurrences 2 <odf>: occurrences 2 <xslt>: occurrences 2 <fodt>: occurrences 1 <odg>: occurrences 1 <msixbundle>: occurrences 1 <appx>: occurrences 1 <docx>: occurrences 1 <odt>: occurrences 1 <pptx>: occurrences 1 <ods>: occurrences 1 <odp>: occurrences 1 Zero failures

Linux Mint 22 Partition Report

Total system files: 1_023_441 Total zipped archive packages: 451 Extensions sorted in order of occurrence count (Highest first) <odt>: occurrences 156 <ods>: occurrences 107 <ott>: occurrences 77 <docx>: occurrences 55 <otp>: occurrences 23 <ots>: occurrences 18 <xlsx>: occurrences 7 <epub>: occurrences 3 <odb>: occurrences 2 <odp>: occurrences 1 <pptx>: occurrences 1 <otg>: occurrences 1 Total XML files: 103270 Extensions sorted in order of occurrence count (Highest first) <svg>: occurrences 83411 <xml>: occurrences 10531 <html>: occurrences 4937 <ecf>: occurrences 2070 <eant>: occurrences 1181 <xsl>: occurrences 244 <config>: occurrences 231 <manifest>: occurrences 209 <targets>: occurrences 142 <vcxproj>: occurrences 88 <svgz>: occurrences 82 <xsd>: occurrences 62 <props>: occurrences 26 <rng>: occurrences 14 <glade>: occurrences 9 <xhtml>: occurrences 7 <opf>: occurrences 5 <ncx>: occurrences 4 <csproj>: occurrences 4 <plist>: occurrences 4 <rdf>: occurrences 3 <pom>: occurrences 2 <fods>: occurrences 2 <xslt>: occurrences 1 <fodt>: occurrences 1 Zero failures

Name Cache Hash Distribution

Parsing: vision2.ecf

<custom>: occurrences 35 <platform>: occurrences 34 <condition>: occurrences 34 <exclude>: occurrences 15 <concurrency>: occurrences 13 <dotnet>: occurrences 11 <external_linker_flag>: occurrences 10 <file_rule>: occurrences 8 <cluster>: occurrences 7 <library>: occurrences 7 <target>: occurrences 6 <option>: occurrences 5 <variable>: occurrences 4 <assembly>: occurrences 4 <setting>: occurrences 4 <external_include>: occurrences 4 <external_object>: occurrences 3 <warning>: occurrences 2 <external_cflag>: occurrences 2 <description>: occurrences 1 <system>: occurrences 1 <root>: occurrences 1 <capability>: occurrences 1

Name Caching

Buckets used count: 41 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 1

Parsing: mandarin-names-and-text.xsl

<xsl:attribute>: occurrences 509 <xsl:when>: occurrences 477 <xsl:value-of>: occurrences 433 <xsl:template>: occurrences 192 <xsl:choose>: occurrences 165 <xsl:if>: occurrences 161 <xsl:with-param>: occurrences 131 <xsl:otherwise>: occurrences 126 <xsl:variable>: occurrences 119 <xsl:call-template>: occurrences 118 <xsl:element>: occurrences 93 <演:其他>: occurrences 87 <xsl:copy-of>: occurrences 87 <xsl:for-each>: occurrences 54 <xsl:param>: occurrences 37 <xsl:apply-templates>: occurrences 29 <字:文本串>: occurrences 7 <字:句>: occurrences 6 <uof:其他对象>: occurrences 4 <xsl:number>: occurrences 4 <uof:数据>: occurrences 4 <字:区域结束>: occurrences 3 <字:绝对>: occurrences 3 <字:区域开始>: occurrences 3 <图:颜色>: occurrences 3 <演:母版>: occurrences 3 <图:高度>: occurrences 3 <图:宽度>: occurrences 3 <演:背景>: occurrences 3 <演:随机效果>: occurrences 2 <演:百叶窗>: occurrences 2 <演:轮子>: occurrences 2 <演:随机线条>: occurrences 2 <uof:锚点>: occurrences 2 <字:段落>: occurrences 2 <图:式样>: occurrences 2 <演:声音>: occurrences 2 <图:大小>: occurrences 2 <图:图形>: occurrences 2 <演:扇形展开>: occurrences 2 <演:盒状>: occurrences 2 <演:阶梯状>: occurrences 2 <uof:路径>: occurrences 2 <演:强调>: occurrences 2 <字:绝对值>: occurrences 2 <演:圆形扩展>: occurrences 2 <演:擦除>: occurrences 2 <演:菱形>: occurrences 2 <字:句属性>: occurrences 2 <演:棋盘>: occurrences 2 <演:闪烁一次>: occurrences 2 <演:劈裂>: occurrences 2 <演:十字形扩展>: occurrences 2 <字:空格符>: occurrences 2 <字:上下标>: occurrences 1 <演:页面版式>: occurrences 1 <演:动画文本>: occurrences 1 <演:布局>: occurrences 1 <演:占位符>: occurrences 1 <演:序列>: occurrences 1 <xsl:output>: occurrences 1 <演:动画播放后>: occurrences 1 <演:定时>: occurrences 1 <演:增强>: occurrences 1 <演:动画>: occurrences 1 <演:幻灯片>: occurrences 1 <演:幻灯片备注>: occurrences 1 <演:切换>: occurrences 1 <演:方式>: occurrences 1 <演:时间间隔>: occurrences 1 <演:单击鼠标>: occurrences 1 <演:页面设置集>: occurrences 1 <演:配色方案集>: occurrences 1 <uof:文本位置>: occurrences 1 <演:页面版式集>: occurrences 1 <演:公用处理规则>: occurrences 1 <演:放映设置>: occurrences 1 <uof:演示文稿>: occurrences 1 <演:显示比例>: occurrences 1 <演:文本式样集>: occurrences 1 <演:文本式样>: occurrences 1 <演:效果>: occurrences 1 <uof:书签>: occurrences 1 <uof:链接集>: occurrences 1 <演:幻灯片序列>: occurrences 1 <uof:对象集>: occurrences 1 <uof:超级链接>: occurrences 1 <uof:式样集>: occurrences 1 <字:编号格式表示>: occurrences 1 <uof:UOF>: occurrences 1 <uof:书签集>: occurrences 1 <演:文本和线条>: occurrences 1 <xsl:key>: occurrences 1 <演:阴影>: occurrences 1 <xsl:stylesheet>: occurrences 1 <演:配色方案>: occurrences 1 <演:幻灯片集>: occurrences 1 <演:强调和尾随超级链接>: occurrences 1 <演:强调和超级链接>: occurrences 1 <演:标题文本>: occurrences 1 <演:填充>: occurrences 1 <演:放映间隔>: occurrences 1 <演:母版集>: occurrences 1 <演:全屏放映>: occurrences 1 <演:手动方式>: occurrences 1 <演:循环放映>: occurrences 1 <演:放映顺序>: occurrences 1 <演:主体>: occurrences 1 <演:前端显示>: occurrences 1 <演:导航帮助>: occurrences 1 <演:放映动画>: occurrences 1 <演:背景色>: occurrences 1 <uof:标题>: occurrences 1 <uof:元数据>: occurrences 1 <uof:默认字体>: occurrences 1 <uof:字体集>: occurrences 1 <xsl:text>: occurrences 1 <uof:创建者>: occurrences 1 <uof:主题>: occurrences 1 <uof:创建应用程序>: occurrences 1 <uof:摘要>: occurrences 1 <字:下划线>: occurrences 1 <字:字体>: occurrences 1 <图:翻转>: occurrences 1 <图:组合位置>: occurrences 1 <uof:作者>: occurrences 1 <字:段后距>: occurrences 1 <字:段前距>: occurrences 1 <字:尾注>: occurrences 1 <字:脚注>: occurrences 1 <字:段间距>: occurrences 1 <uof:关键字集>: occurrences 1 <图:渐变>: occurrences 1 <图:图片>: occurrences 1 <uof:对象数>: occurrences 1 <uof:中文字符数>: occurrences 1 <uof:字数>: occurrences 1 <uof:创建日期>: occurrences 1 <字:首行>: occurrences 1 <字:右>: occurrences 1 <字:左>: occurrences 1 <图:图案>: occurrences 1 <uof:编辑次数>: occurrences 1 <uof:关键字>: occurrences 1 <uof:最后作者>: occurrences 1 <图:控制点>: occurrences 1 <uof:用户自定义元数据>: occurrences 1 <uof:编辑时间>: occurrences 1 <uof:段落数>: occurrences 1 <uof:页数>: occurrences 1 <uof:用户自定义元数据集>: occurrences 1 <uof:文档模板>: occurrences 1 <演:切出>: occurrences 1 <演:陀螺旋>: occurrences 1 <演:更改线条颜色>: occurrences 1 <演:更改字形>: occurrences 1 <演:透明>: occurrences 1 <演:消失>: occurrences 1 <演:缓慢移出>: occurrences 1 <演:飞出>: occurrences 1 <演:向外溶解>: occurrences 1 <演:更改字号>: occurrences 1 <演:向内溶解>: occurrences 1 <演:飞入>: occurrences 1 <演:出现>: occurrences 1 <演:退出>: occurrences 1 <演:动作路径>: occurrences 1 <演:进入>: occurrences 1 <演:更改填充颜色>: occurrences 1 <演:更改字体颜色>: occurrences 1 <演:缓慢飞入>: occurrences 1 <演:缩放>: occurrences 1 <演:切入>: occurrences 1 <图:X-缩放比例>: occurrences 1 <图:文本内容>: occurrences 1 <图:旋转角度>: occurrences 1 <图:透明度>: occurrences 1 <图:Y-缩放比例>: occurrences 1 <图:打印对象>: occurrences 1 <图:锁定纵横比>: occurrences 1 <图:预定义图形>: occurrences 1 <图:相对原始比例>: occurrences 1 <图:后端箭头>: occurrences 1 <图:Web文字>: occurrences 1 <图:生成软件>: occurrences 1 <图:名称>: occurrences 1 <图:类别>: occurrences 1 <图:前端箭头>: occurrences 1 <图:关键点坐标>: occurrences 1 <图:属性>: occurrences 1 <图:线型>: occurrences 1 <图:线粗细>: occurrences 1 <图:填充>: occurrences 1 <图:线颜色>: occurrences 1

Name Caching

Buckets used count: 202 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 37 <3>: occurrences 9 <4>: occurrences 2

Parsing: recursive-entity-expansion.xml

<p>: occurrences 313 <nt>: occurrences 307 <termref>: occurrences 209 <code>: occurrences 196 <head>: occurrences 128 <rhs>: occurrences 102 <lhs>: occurrences 89 <prod>: occurrences 89 <item>: occurrences 66 <termdef>: occurrences 63 <term>: occurrences 60 <sitem>: occurrences 45 <titleref>: occurrences 44 <kw>: occurrences 43 <td>: occurrences 37 <scrap>: occurrences 36 <eg>: occurrences 34 <def>: occurrences 31 <emph>: occurrences 31 <gitem>: occurrences 31 <label>: occurrences 31 <div2>: occurrences 30 <vc>: occurrences 27 <name>: occurrences 21 <vcnote>: occurrences 21 <prodgroup>: occurrences 19 <div3>: occurrences 18 <member>: occurrences 18 <bibl>: occurrences 16 <bibref>: occurrences 14 <specref>: occurrences 14 <loc>: occurrences 13 <wfc>: occurrences 13 <ulist>: occurrences 10 <wfcnote>: occurrences 10 <div1>: occurrences 8 <tr>: occurrences 7 <role>: occurrences 6 <glist>: occurrences 5 <inform-div1>: occurrences 5 <olist>: occurrences 4 <email>: occurrences 3 <affiliation>: occurrences 3 <author>: occurrences 3 <com>: occurrences 2 <language>: occurrences 2 <blist>: occurrences 2 <prevlocs>: occurrences 1 <version>: occurrences 1 <w3c-designation>: occurrences 1 <header>: occurrences 1 <w3c-doctype>: occurrences 1 <day>: occurrences 1 <pubdate>: occurrences 1 <month>: occurrences 1 <publoc>: occurrences 1 <year>: occurrences 1 <title>: occurrences 1 <latestloc>: occurrences 1 <abstract>: occurrences 1 <note>: occurrences 1 <htable>: occurrences 1 <htbody>: occurrences 1 <orglist>: occurrences 1 <body>: occurrences 1 <back>: occurrences 1 <sourcedesc>: occurrences 1 <status>: occurrences 1 <spec>: occurrences 1 <pubstmt>: occurrences 1 <revisiondesc>: occurrences 1 <authlist>: occurrences 1 <slist>: occurrences 1 <langusage>: occurrences 1

Name Caching

Buckets used count: 82 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 11 <3>: occurrences 1

Parsing: DTD-attlist-default-values.xml

<comment>: occurrences 37362 <glob>: occurrences 1225 <match>: occurrences 1201 <mime-type>: occurrences 908 <magic>: occurrences 507 <sub-class-of>: occurrences 496 <generic-icon>: occurrences 445 <alias>: occurrences 323 <acronym>: occurrences 259 <expanded-acronym>: occurrences 259 <root-XML>: occurrences 30 <treematch>: occurrences 25 <treemagic>: occurrences 12 <mime-info>: occurrences 1

Name Caching

Buckets used count: 37 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 1

Parsing: Legislation.xml

<p>: occurrences 1767 <afada>: occurrences 748 <i>: occurrences 646 <ifada>: occurrences 633 <ufada>: occurrences 577 <efada>: occurrences 476 <ofada>: occurrences 421 <td>: occurrences 291 <emdash>: occurrences 215 <xref>: occurrences 191 <title>: occurrences 188 <b>: occurrences 186 <font>: occurrences 181 <tr>: occurrences 120 <l1>: occurrences 104 <cdq>: occurrences 93 <odq>: occurrences 93 <nbsp>: occurrences 89 <page>: occurrences 89 <number>: occurrences 66 <sect>: occurrences 65 <Ofada>: occurrences 28 <csq>: occurrences 27 <osq>: occurrences 27 <su>: occurrences 26 <col>: occurrences 26 <pc>: occurrences 23 <Afada>: occurrences 15 <fn>: occurrences 14 <marker>: occurrences 14 <hr1>: occurrences 13 <tbody>: occurrences 12 <type>: occurrences 12 <commentary>: occurrences 12 <table>: occurrences 12 <colgroup>: occurrences 12 <commentarycontent>: occurrences 12 <Efada>: occurrences 11 <bull>: occurrences 10 <div>: occurrences 10 <part>: occurrences 9 <sectionreference>: occurrences 9 <commentaries>: occurrences 9 <schedule>: occurrences 9 <Ifada>: occurrences 7 <Ufada>: occurrences 7 <pound>: occurrences 7 <graphic>: occurrences 4 <narrative>: occurrences 3 <body>: occurrences 1 <metadata>: occurrences 1 <year>: occurrences 1 <act>: occurrences 1 <dateofenactment>: occurrences 1 <euro>: occurrences 1 <frontmatter>: occurrences 1 <coverpage>: occurrences 1 <backmatter>: occurrences 1

Name Caching

Buckets used count: 76 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 9

Parsing: testdata/largefiles/nes96.xml

<catgry>: occurrences 8844 <catValu>: occurrences 8844 <txt>: occurrences 8450 <item>: occurrences 2310 <location>: occurrences 1393 <var>: occurrences 1393 <labl>: occurrences 1393 <qstn>: occurrences 1392 <qstnLit>: occurrences 1392 <catgryGrp>: occurrences 1367 <invalrng>: occurrences 1339 <valrng>: occurrences 971 <codeBook>: occurrences 1 <prodStmt>: occurrences 1 <producer>: occurrences 1 <prodDate>: occurrences 1 <titlStmt>: occurrences 1 <titl>: occurrences 1 <sumDscr>: occurrences 1 <stdyDscr>: occurrences 1 <citation>: occurrences 1 <stdyInfo>: occurrences 1 <logRecL>: occurrences 1 <caseQnty>: occurrences 1 <dataDscr>: occurrences 1 <recPrCas>: occurrences 1 <fileStrc>: occurrences 1 <fileDscr>: occurrences 1 <fileTxt>: occurrences 1 <dataKind>: occurrences 1 <dimensns>: occurrences 1

Name Caching

Buckets used count: 43 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 1

Parsing: testdata/largefiles/ns_att_test.xml

<z:row>: occurrences 50000 <rs:data>: occurrences 1 <xml>: occurrences 1

Name Caching

Buckets used count: 75 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 6

Parsing: testdata/largefiles/ns_att_test.xml

<z:row> occurrences 50000 <rs:data> occurrences 1 <xml> occurrences 1

Name Caching

Buckets used count: 75 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 6

Parsing: testdata/largefiles/recset.xml

<z:row>: occurrences 50000 <s:AttributeType>: occurrences 32 <s:datatype>: occurrences 32 <xml>: occurrences 1 <s:Schema>: occurrences 1 <s:ElementType>: occurrences 1 <rs:data>: occurrences 1 <s:extends>: occurrences 1

Name Caching

Buckets used count: 61 Average hash bucket count: 1 Hash bucket counts greater than 1 <2>: occurrences 3

Parsing: testdata/largefiles/wordnet_glossary-20010201.rdf

<rdf:Description>: occurrences 99642 <b:glossaryEntry>: occurrences 49964 <rdf:RDF>: occurrences 1

Name CachingBuckets used count: 11 Average hash bucket count: 1 Hash bucket counts greater than 1 None