Skip to content

yamlchan: Add libfyaml support as an alternative to libyaml - #67

Open
embray wants to merge 16 commits into
Starlink:masterfrom
embray:libfyaml-support
Open

yamlchan: Add libfyaml support as an alternative to libyaml#67
embray wants to merge 16 commits into
Starlink:masterfrom
embray:libfyaml-support

Conversation

@embray

@embray embray commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Something I've been working on off-and-on for a few weeks and finally had a chance to finish.

Motivation

AST uses the older (but still actively maintained) lower-level libyaml to parse and emit YAML. My project, libasdf, on the other hand uses the newer libfyaml. And then, in particular, the libasdf-gwcs plugin for libasdf which is using AST as an evaluation backend, in turn also requires libfyaml and via AST libyaml.

In effect, any code that depends on libasdf-gwcs must also detect two separate YAML libraries, which is a bit onerous. It's not the end of the world--both libraries are widely available. It just feels a bit rough.

On the other hand I'd also understand if AST doesn't want to maintain support for both. I just wanted to see if it could be done, since both libraries are implementing the same recommended YAML parser/emitter interfaces, just with slight API differences.

Implementation

This adds a yaml_backend.h header-only library providing astYaml functions that roughly map to the equivalent yaml_ functions originally used by yamlchan.c. This is an almost direct drop-in replacement, so the changes to yamlchan.c itself are minimal; the only major difference is in error handling which is improved over the original implementation anyways (better reporting of line number context for errors).

Between the two backends the libyaml one is still much simpler--it's almost a direct wrapper. The libfyaml backend needs to do a little extra work to adhere to the original libyaml interface.

The backend to use can be selected explicitly when configuring the build. If not specified the build system chooses, with priority given for libyaml for backwards-compatibility: The outputs of the two libraries are not bytewise-identical, though they are of course equivalent YAML.

Shortcomings

Compile-time backend selection: The YAML backend (libyaml, libfyaml, or none) is selected at CMake/autoconf configure time and compiled into libast. Packagers who want to ship both variants must build separate conflicting packages (there is precedent for this, however.). A cleaner long-term approach might be a satellite shared library, analogous to the existing libast_grf_*.so graphics plugins. Both backends could co-exist with one selected and dlopen'd at runtime, though this would be a more complex refactor.

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.47876% with 92 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.46%. Comparing base (8d4f526) to head (8f782dc).

Files with missing lines Patch % Lines
src/yamlchan.c 48.50% 23 Missing and 46 partials ⚠️
src/yaml_backend.h 81.60% 14 Missing and 9 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #67      +/-   ##
==========================================
+ Coverage   61.42%   61.46%   +0.04%     
==========================================
  Files          83       84       +1     
  Lines       96467    96545      +78     
  Branches    30582    30597      +15     
==========================================
+ Hits        59253    59340      +87     
+ Misses      21066    21055      -11     
- Partials    16148    16150       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@embray
embray force-pushed the libfyaml-support branch 2 times, most recently from d717399 to 8a9db61 Compare July 7, 2026 11:55
@timj

timj commented Jul 7, 2026

Copy link
Copy Markdown
Member

Since the YAML handling is mostly motivated by the ASDF needs I don't have a problem with adding support for a new YAML library. If I was a packager I don't really understand why I would have to build both variants -- wouldn't I always choose fyaml? There is a conda-forge package of AST which would need tweaking if we switched YAML library. We picked libyaml originally because we didn't really know any better.

@embray

embray commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

wouldn't I always choose fyaml?

Possibly. Right now the only reason to do so would be my specific use-case really, so it has to be an informed choice.

There is a conda-forge package of AST which would need tweaking if we switched YAML library. We picked libyaml originally because we didn't really know any better.

I'd be more fair to yourself than that :) At the time libfyaml was quite new and not at all mature yet (its first release on github was only in 2019).

Depending on how dependent upstream users are on byte-for-byte identical outputs from AST, it might still matter, as there are cosmetic differences in what the two libraries output. I actually find that some of libfyaml's default output formatting is a bit ugly (compromises it makes, I think, as a performance tradeoff).

For example, even short flow-style sequences like [a, b, c] are not collapsed onto a single line; instead it outputs:

[
  a,
  b,
  c
]

which I find a bit annoying. Of course, for the event stream interface doing this requires some buffering/look-ahead of events.

Those cosmetic output differences are my only concern though.

I also plan when I have time to propose some fixes to libfyaml for these formatting issues. I think some options to tweak this might be acceptable, where performance is less of a concern--its maintainer has been amenable to some of my past suggestions.

embray added a commit to asdf-format/libasdf-gwcs that referenced this pull request Jul 13, 2026
- Starlink/ast#37
- Starlink/ast#51
- Starlink/ast#66
- Starlink/ast#67

This includes the experimental SIMD support (enabled by default, and
support in AST for libfyaml, allowing us to drop the libyaml
requirement).

Also updates the minimum supported versions of the ASDF tags supported
by AST.
@embray

embray commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator Author

The ASAN build is revealing a number of memory leaks. Strangely some of them don't seem directly related to this branch, while others do, which suggests to me that the correct flags aren't being passed to ASAN to cause tests to halt, but I'll have to have a look at that.

At least one of the leaks is a bug in libfyaml itself, fixed in v0.9, though Ubuntu <= 24.04 has a version of libfyaml with this bug and it doesn't appear to be patched. I'll keep investigating but that one might not be anything that can be easily worked around and should maybe just be documented. It's a small leak that only occurs while writing.

@timj

timj commented Jul 14, 2026

Copy link
Copy Markdown
Member

Interesting. As I have expanded test coverage I have found some ASAN issues. Sometimes it's the system not including an astBegin/astEnd in the right place but others have been real. So you are saying that some of these are in general mapping code?

@embray

embray commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

So you are saying that some of these are in general mapping code?

No, just some specific to yamlchan.c. One that I introduced in #39 which I'm fixing now, and a couple unique to this PR, also being fixed. There is one leak caused by a bug in libfyaml mentioned above. I'm not sure I can work around it so I might just make an exception for it (it's fixed in newer versions).

@embray

embray commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Fixed all leaks exposed in testyamlchan that I could find--some pre-existing and some introduced originally by this PR. For the one upstream bug I couldn't workaround, so I added suppression file: https://github.com/embray/ast/blob/88b2aa90ea1e428455fb4b5e056f260d0c84a402/ast_tester/libfyaml.supp

ASAN_OPTIONS=detect_leaks=1;LSAN_OPTIONS=suppressions=../../ast_tester/libfyaml.supp ./testyamlchan
  parser error message (expected):
AST: Error in routine test_parser_error at line 858 in file /home/embray/src/asdf-format/starlink/ast/ast_tester/testyamlchan.c.
astRead(YamlChan): libfyaml parse error:
libfyaml parser: flow sequence without a closing bracket at line 4, column 1
 All YamlChan tests passed (libfyaml)
-----------------------------------------------------
Suppressions used:
  count      bytes template
    183       4487 fy_emit_event_vcreate
-----------------------------------------------------

(the error in the output comes from one of the tests and is expected)

embray added 16 commits August 25, 2026 20:15
This adds a new abstracted interface for the underlying YAML library
used for YamlChan.  It hews close to the original libyaml API with
a few additions to smooth over differences between libraries, and adds
support for libfyaml as an alternative backend.

The build system supports using either libyaml or libfyaml. It is
possible to specify one of the other explicitly, otherwise the build
system will select one automatically, with preference given to libyaml
for compatibility's sake, as there are slight differences in libyaml and
libfyaml outputs (both still equivalent YAML though).

This commit is a squashed commit of several WIP commits on this.

wip: style tweaks

wip: interface cleanup of libyaml vs libfyaml minor differences

build: support libfyaml as alternative yaml backend at configure time

Both cmake and autotools build get support: auto (the default) uses the
first YAML library detected, preferring libyaml at first (for
backwards-compat) and falling back on libfyaml.  Both cases also allow
an explicit backend (yaml or fyaml) to be specified.

For the tests, decided to keep testyamlchan.c compiling even if there is
no backend supported, in case we also want to add tests that work even
in this case (even just testing the appropriate errors raised)

wip: several small improvments and bug fixes, esp. in error handling

- Clean up the error handling situation; rather than trying to directly
  smudge the interface into the previous libyaml error handling, produce
  equivalent sensible error messages from both backends (libfyaml
  doesn't have numeric error codes like libyaml so that details is
  folded into the libyaml error messages).  The exact error messages
  between libraries are of course not exactly the same, but the format
  of the error messages in AST is made similar between the two,
  including correct line/column number context (which was not handled
  well to begin with).

- Add a provisional test for error handling; could still be improved.

- Smooth over differences between backends w.r.t. how node output styles
  are handled, by intrducing a new common style enum that abstracts out
  what's supported by each backend.

- Fixes to the libfyaml backend write callback: libfyaml calls the
  emitter callback once *per token*, but our write callback is expected
  to produce full lines (without terminating newline), so _astFyamlWriteAdapter
  now maintains an internal line buffer to account for that, which is a
  bit more overcomplication than I'd like...

wip: fix emission of explicit str tags for YAML strings

There are still some issues with how tagged scalars and flow sequences
are emitted by libfyaml but that seems to be more a libfyaml limitation
that needs to poked at upstream...

wip: improve the test to check the exact error message and line no

Previously had a bug with the line numbering in libfyaml so this would
catch that.
Not sure if we want to add yet two more build variants to the CI but it
would be necessary for maximum code coverage.  Also worth doing on both
mac and linux because the homebrew coverage for libfyaml tends to be at
the bleeding edge, while the Ubuntu version is older and more
conservative.
- Adds the %YAML directive and %TAG directive defining the handle
  for core/asdf tags

- Fix cludgy tag formatting in libfyaml, and handle shortening tags that
  can use the handle.  Newer versions of libfyaml can handle this better
  automatically IIRC, but older versions like the 0.8.1 on Ubuntu need
  extra intervention :(
Unfortunate workaround for a bug in libfyaml 0.8 which mishandled a root
node event with a non-default tag immediately following the document
start.  This was fixed in libfyaml 0.9+, but as ubuntu-latest (24.04)
has libfyaml 0.8 it's best to try to support it as a minimum version.
Newer versions of Ubuntu have 0.9+.
Most of these are difficult to simulate in tests, especially the emitter
errors.  ReadYAMLSequence's parse error does happen to be tested in
test_parser_error.
Moves the YAML backend detection into a dedicated macro in
m4/ast_yaml.m4 `AST_CHECK_YAML`

This handles things like setting precious variables (F)YAML_CFLAGS,
(F)YAML_LIBS consistently between backends; keeping some of the messier
logic directly out of configure.ac.

Pulled this in from a branch that tested merging this with my autotools
branch for PR Starlink#37.

As a follow-up we might want to consider adding optional pkg-config
support here, which would make our lives easier.  The new cmake build
system already uses pkg-config.  I just resisted adding it for now to
the autotools build since there was no prior precedent of its use, and I
worry about unintended breakage of the building within Starlink.
In the case where the ref_map is assigned a Mapping for the first time
the returned (borrowed) reference was not annulled.
These two event cases did not invoke astYamlEventDelete before breaking
out of the loop.
libyaml's input is line-oriented, whereas with libfyaml we would like to
use fy_parser_set_input_callback which byte-oriented, but much better
than the previous astYamlParserSetInput which required buffering the
entire YAML into memory to pass to fy_parser_set_input_string.

Providing YamlChan with a line buffer for its reads gives the best of
both worlds.
Suppresses leak reports from the one known upstream bug in older
versions of libfyaml that leak into AST via testyamlchan.

This is mainly for the CI which still runs older versions of Ubuntu that
have this leak in its system libfyaml.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants