Today I did the first 0.1.0 preview release of libxmlb. We’re at the “probably API stable, but no promises” stage. This is the library I introduced a couple of weeks ago, and since then I’ve been porting both fwupd and gnome-software to use it. The former is almost complete, and nearly ready to merge, but the latter is still work in progress with a fair bit of code to write. I did manage to launch gnome-software with libxmlb yesterday, and modulo a bit of brokenness it’s both faster to start (over 800ms faster from cold boot!) and uses an amazing 90Mb less RSS at runtime. I’m planning to merge the libxmlb branch into the unstable branch of fwupd in the next few weeks, so I need volunteers to package up the new hard dep for Debian, Ubuntu and Arch.
The tarball is in the usual place – it’s a simple Meson-built library that doesn’t do anything crazy. I’ve imported and built it already for Fedora, much thanks to Kalev for the super speedy package review.
I guess I should explain how applications are expected to use this library. At its core, there are just five different kinds of objects you need to care about:
XbSilo– a deduplicated string pool and a read only node tree. This is typically kept alive for the application lifetime.
XbNode– a “Gobject wrapped” immutable node available as a query result from
XbBuilder– a “compiler” to build the
XbBuilderSource’s. This is typically created and destroyed at startup or when the blob needs regenerating.
XbBuilderNode– a mutable node that can have a parent, children, attributes and a value
XbBuilderSource– a source of data for XbBuilder, e.g. a .xml.gz file or just a raw XML string
The way most applications will use libxmlb is to create a local
XbBuilder instance, add some
XbBuilderSource’s and then “ensure” a local cache file. The “ensure” process either mmap loads the binary blob if all the file mtimes are the same, or compiles a blob saving it to a new file. You can also tell the
XbSilo to watch all the sources that it was built with, so that if any files change at runtime the
valid property gets set to FALSE and the application can
xb_builder_ensure() at a convenient time.
XbBuilder has been compiled, a
XbSilo pops out. With the
XbSilo you can query using most common XPath statements – I actually ended up implementing a FORTH-style stack interpreter so we can now do queries like
/components/component/id[contains(upper-case(text()),'GIMP')] – I’ll say a bit more on that in a minute. Queries can limit the number of results (for speed), and are deduplicated in a sane way so it’s really quite a simple process to achieve something that would be a lot of C code. It’s possible to directly query an attribute or text value from a node, so the silo doesn’t have to be passed around either.
In the process of porting gnome-software, I had to make libxmlb thread-safe – which required some internal organisation. We now have an non-exported
XbMachine stack interpreter, and then the
XbSilo actually registers the XML-specific methods (like
contains()) and functions (like
~=). These get passed some per-method user data, and also some per-query private data that is shared with the node tree – allowing things like
position()=3 to work. The function callbacks just get passed an query-specific stack, which means you can allow things like comparing
1.00f This makes it easy to support more of XPath in the future, or to support something completely application specific like
gnome-software-search() without editing the library.
If anyone wants to do any API or code review I’d be super happy to answer any questions. Coverity and valgrind seem happy enough with all the self tests, but that’s no replacement for a human asking tricky questions. Thanks!