summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
authorsteven-vd <steven@vandorp.lu>2026-08-16 15:00:32 +0200
committersteven-vd <steven@vandorp.lu>2026-08-17 09:31:42 +0200
commitee081ca70322cd01d0642fdb1860e6bfbfb48e85 (patch)
tree295fdba93b08b24562c6d49e971bbf3425eac50c /README.md
Initial commitHEAD0.1.0master
Diffstat (limited to 'README.md')
-rw-r--r--README.md161
1 files changed, 161 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a803092
--- /dev/null
+++ b/README.md
@@ -0,0 +1,161 @@
+# EXPERIMENTAL
+This library is still in an experimental stage and should not be used in
+environments where stability/safety/security/reliability are important. Some
+minor features like normalization and recursive entity references aren't
+supported yet.
+
+While this library is in an experimental state, semantic versioning
+conventions will not be guaranteed and minor updates may introduce breaking
+API/ABI changes. Semantic versioning will be guaranteed once this library
+hits 1.0.
+
+# Why use imxml?
+imxml is a portable, high-performance, zero-allocation, immediate-mode XML
+parser designed for applications that know the approximate structure of the XML
+they consume.
+
+The rationale of this library is that you generally don't need to parse
+arbitrary XML. Instead, you're targeting some subset. This goes beyond just
+disabling certain features, as even the structure of your XML is typically
+bounded. For example, if you're parsing OpenStreetMap (OSM) data, you know that
+you'll have one root <osm> element, which contains one <bounds> element,
+followed by several <node> elements, which may contain <tag> elements, then
+several <way> elements, and so on. This "immediate-mode" API design allows you
+to skip parsing anything you're not interested in, while also indicating your
+data's expected layout to the compiler, which allows the optimizer to do a
+better job.
+
+## Performance
+benchmarks/imxml/benchmark-results.xml contains benchmark results for some
+workloads on an AMD Ryzen 5 3600. In these benchmarks, imxml achieves a minimum
+2.9x performance improvement compared to quick-xml (the fastest mainstream
+parser I could find) for these workloads, and up to 12x in extreme cases. Note
+that these are synthetic benchmarks designed to stress the raw parsing
+throughput and nothing else. Your code likely contains all sorts of extra
+business logic that will change your perceived throughput, though imxml's
+immediate-mode API design should give your optimizer the best chance to
+generate fast code.
+
+## Memory
+imxml's memory usage is completely static, there are no allocations, and it
+doesn't modify the input buffer. Returned strings are simply views into the
+input buffer or, in the case of character references, views into a static
+(optionally thread-local) buffer.
+
+## Compatibility
+Standard C89 (ANSI C) and C++98 are supported. The library has been tested with
+clang, gcc, and tcc. Every standards-compliant C compiler should work, though
+if emmintrin.h/immintrin.h isn't supported, only the fallback scalar
+implementation will work (unless you define your own instruction set).
+
+UTF-8, ISO-Latin-1, ASCII, and generally all char/byte-based encodings are
+supported. UTF-16 is not supported.
+
+The library itself has no dependencies, not even libc, but optional
+platform-specific convenience functions can be enabled with flags. Right now,
+there is only a Linux helper implementation for loading files.
+
+## Extensibility
+Currently imxml supports the MMX, SSE2, and AVX2 instruction sets, as well as a
+fallback scalar configuration for compilers that don't support SIMD intrinsics
+(e.g. tcc). You can define custom instruction set definitions if you want SIMD
+acceleration on more exotic platforms.
+
+## Features
+ - [X] Entities
+ - [X] Standard entity references
+ - [X] Unicode character references
+ - [X] DTD-defined entity references
+ - [X] CDATA
+ - [X] Comments
+ - [X] Namespaces
+ - [X] Customizable SIMD instruction sets
+ - [ ] Content normalization
+ - [ ] Platform-specific helper functions
+ - [X] Linux
+ - [ ] Windows
+ - [ ] OSX
+
+# Usage
+Download imxml.h and add this to your code:
+```
+#define IMXML_IMPLEMENTATION
+#include "imxml.h"
+```
+
+## Example
+```
+#define IMXML_LINUX // We're using Linux, of course
+#define IMXML_NO_SUPPORT_SINGLE_QUOTES // OSM always uses double-quotes
+#define IMXML_NO_SUPPORT_COMMENTS // OSM doesn't include comments
+#define IMXML_NO_SUPPORT_CDATA // OSM doesn't include CDATA
+#define IMXML_NO_SUPPORT_ENTITIES // We don't need to parse entities
+#define IMXML_NO_SUPPORT_NAMESPACES // OSM doesn't include namespaces
+#define IMXML_NO_CHECK_BOUNDS // We know our data will end with </osm>
+#define THREADLOCAL // We disable thread-local by defining it as nothing
+#define IMXML_IMPLEMENTATION
+#include "imxml.h"
+#include <stdio.h> // for printf
+
+int main (void) {
+ size_t file_size;
+ // We need to use this file_open function because the input data needs to
+ // be padded to make sure we're not reading out-of-bounds
+ char* const file = imxml_linux_file_open("dat/yellowstone.osm", &file_size, false);
+ if (file == 0) return -1;
+ XmlParser p = {0}; // zero-init is important
+ p.head = file;
+
+ // Verify we're really dealing with osm data. This is mostly unnecessary,
+ // but it doesn't cost much and might catch some dumb bugs
+ if (!xml_parse_header(&p, NULL)) return -1;
+ if (!xml_tag_expect(&p, "osm")) return -1;
+ if (!xml_has_children(&p)) return -1;
+ if (!xml_tag_expect(&p, "bounds")) return -1;
+ if (xml_has_children(&p)) return -1;
+
+ size_t highway_crossing_count = 0;
+ while (true) {
+ ImxmlString tag = xml_tag(&p);
+ if (imxml_streql(tag, imxml_strlit("node"))) {
+ if (xml_has_children(&p)) {
+ while (true) {
+ tag = xml_tag(&p);
+ if (imxml_streql(tag, imxml_strlit("/node"))) break;
+ ImxmlString key = xml_value(&p);
+ if (!imxml_streql(key, imxml_strlit("highway"))) continue;
+ ImxmlString value = xml_value(&p);
+ if (imxml_streql(value, imxml_strlit("crossing"))) {
+ highway_crossing_count += 1;
+ }
+ }
+ }
+ } else {
+ // encountered non-node tag, which means we can early-out (because
+ // OSM groups all of the <node> elements up-front).
+ // Alternatively, we could check if tag == "/osm", but then we'd
+ // just be skipping over all the <way> and <relation> tags, which
+ // would be a waste of time
+ break;
+ }
+ }
+
+ printf("%zu\n", highway_crossing_count);
+ imxml_linux_file_close(file, file_size);
+ return 0;
+}
+```
+
+See the examples/ and benchmarks/imxml/ directories for more examples.
+
+# Contributing
+
+If you have any ideas or issues, send them to steven@vandorp.lu.
+
+If you want to modify code, make your changes in a separate branch,
+generate the patch(es) (`git format-patch master` from inside your branch)
+and send the patch file(s) to steven@vandorp.lu.
+
+# Documentation
+See the DOCUMENTATION section in the source code.
+