Add libcyaml
This commit is contained in:
parent
3f1e47b054
commit
286eca58d6
1
libs/libcyaml/.gitignore
vendored
Normal file
1
libs/libcyaml/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build/
|
114
libs/libcyaml/CHANGES.md
Normal file
114
libs/libcyaml/CHANGES.md
Normal file
@ -0,0 +1,114 @@
|
||||
LibCYAML: Change Log
|
||||
====================
|
||||
|
||||
## LibCYAML v1.4.1
|
||||
|
||||
* **Loading**:
|
||||
* Log valid enum values on error parsing enums.
|
||||
* Split out storage of scaler values to client data structure.
|
||||
* **General**:
|
||||
* Updated code coverage CI to latest action versions.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.4.0
|
||||
|
||||
* **Loading**:
|
||||
* Reject numerical values with trailing junk.
|
||||
* New `CYAML_CFG_IGNORED_KEY_WARNING` to generate warnings when mapping
|
||||
keys are ignored either explicitly by the `CYAML_IGNORE` type or implicitly
|
||||
by the `CYAML_CFG_IGNORE_UNKNOWN_KEYS` config setting.
|
||||
* **Buildsystem**:
|
||||
* Avoid using GNU `-D` install flag.
|
||||
* **General**:
|
||||
* Improved API documentation for `CYAML_SEQUENCE_FIXED`.
|
||||
* Minor optimisation for host endian detection for older GCCs.
|
||||
* Minor CI updates.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.3.1
|
||||
|
||||
* **Loading**:
|
||||
* Fixed value out-of-range detection limits for signed integers.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.3.0
|
||||
|
||||
* **Saving**:
|
||||
* New flags allow control over scalar output style.
|
||||
- For example to force single or double quote style.
|
||||
* **General**:
|
||||
* Buildsystem changes to allow use of CPPFLAGS from the environment.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.2.1
|
||||
|
||||
* **General**:
|
||||
* Support for dynamic library build on Mac OS X.
|
||||
* Ordered designated initialisers in public header for C++ compatibility.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.2.0
|
||||
|
||||
* **Loading**:
|
||||
* Allow mappings with zero fields in the schema.
|
||||
* Improved logging of errors.
|
||||
* `CYAML_BOOL` type now treats "Off" as false.
|
||||
* Allow loading of float values that overflow or underflow unless
|
||||
`CYAML_FLAG_STRICT` set.
|
||||
* Added line and column numbers to backtraces.
|
||||
* **General**:
|
||||
* Update tests to handle libyaml 0.2.5 output format change.
|
||||
* Buildsystem improvements.
|
||||
* Made public header C++ compatible.
|
||||
* Test runner supports running individual tests.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.1.0
|
||||
|
||||
* **Loading**:
|
||||
* Significantly optimised handling of aliases and anchors.
|
||||
* Fixed handling of duplicate mapping keys.
|
||||
* **Saving**:
|
||||
* Increased precision for double precision floating point values.
|
||||
* **General**:
|
||||
* Fixed data handling on big endian systems.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.0.2
|
||||
|
||||
* **Loading**:
|
||||
* Fixed invalid read on error path for bitfield handling.
|
||||
* **Buildsystem**:
|
||||
* Fixed to link against libraries after listing objects.
|
||||
* Added `check` target as alias for `test`.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.0.1
|
||||
|
||||
* **Loading**:
|
||||
* Fixed mapping and sequence values with `CYAML_FLAG_POINTER_NULL`.
|
||||
* **Buildsystem**:
|
||||
* Installation: Explicitly create leading directories.
|
||||
|
||||
No changes are required for client applications to upgrade.
|
||||
|
||||
|
||||
## LibCYAML v1.0.0
|
||||
|
||||
* Initial release.
|
14
libs/libcyaml/LICENSE
Normal file
14
libs/libcyaml/LICENSE
Normal file
@ -0,0 +1,14 @@
|
||||
Copyright (c) 2017-2021, Michael Drake
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
|
209
libs/libcyaml/Makefile
Normal file
209
libs/libcyaml/Makefile
Normal file
@ -0,0 +1,209 @@
|
||||
# SPDX-License-Identifier: ISC
|
||||
#
|
||||
# Copyright (C) 2017-2023 Michael Drake <tlsa@netsurf-browser.org>
|
||||
|
||||
# CYAML's versioning is <MAJOR>.<MINOR>.<PATCH>[-DEVEL]
|
||||
# The main branch will usually be DEVEL. The release process is:
|
||||
# 0. Create branch for release.
|
||||
# 1. Update the CHANGES.md file.
|
||||
# 2. Set MAJOR, MINOR, and PATCH versions appropriately for changes.
|
||||
# 3. Create PR, review, and merge to main.
|
||||
# 4. Set VESION_DEVEL to 0, commit to main.
|
||||
# 5. Tag the release: `git tag -a vN.N.N -m "libcyaml N.N.N"`
|
||||
# 6. Set VESION_DEVEL to 1, commit to main.
|
||||
VERSION_MAJOR = 1
|
||||
VERSION_MINOR = 4
|
||||
VERSION_PATCH = 1
|
||||
VERSION_DEVEL = 0
|
||||
VERSION_STR = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)
|
||||
|
||||
# Default variant depends on whether it's a development build.
|
||||
ifeq ($(VERSION_DEVEL), 1)
|
||||
VARIANT = debug
|
||||
else
|
||||
VARIANT = release
|
||||
endif
|
||||
|
||||
# Unfortunately ASan is incompatible with valgrind, so we have a special
|
||||
# variant for running with sanitisers.
|
||||
VALID_VARIANTS := release debug san
|
||||
ifneq ($(filter $(VARIANT),$(VALID_VARIANTS)),)
|
||||
else
|
||||
$(error VARIANT must be 'debug' (default), 'san', or 'release')
|
||||
endif
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
|
||||
LIB_NAME = libcyaml
|
||||
LIB_PKGCON = $(LIB_NAME).pc
|
||||
LIB_STATIC = $(LIB_NAME).a
|
||||
LIB_SHARED = $(LIB_NAME).so
|
||||
LIB_SH_MAJ = $(LIB_SHARED).$(VERSION_MAJOR)
|
||||
LIB_SH_VER = $(LIB_SHARED).$(VERSION_STR)
|
||||
|
||||
.IMPLICIT =
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
LIBDIR ?= lib
|
||||
INCLUDEDIR ?= include
|
||||
|
||||
Q ?= @
|
||||
|
||||
CC ?= gcc
|
||||
AR ?= ar
|
||||
MKDIR = mkdir -p
|
||||
INSTALL ?= install -c
|
||||
VALGRIND = valgrind --leak-check=full --track-origins=yes
|
||||
|
||||
VERSION_FLAGS = -DVERSION_MAJOR=$(VERSION_MAJOR) \
|
||||
-DVERSION_MINOR=$(VERSION_MINOR) \
|
||||
-DVERSION_PATCH=$(VERSION_PATCH) \
|
||||
-DVERSION_DEVEL=$(VERSION_DEVEL)
|
||||
|
||||
LIBYAML = yaml-0.1
|
||||
LIBYAML_CFLAGS := $(if $(PKG_CONFIG),$(shell $(PKG_CONFIG) --cflags $(LIBYAML)),)
|
||||
LIBYAML_LIBS := $(if $(PKG_CONFIG),$(shell $(PKG_CONFIG) --libs $(LIBYAML)),-lyaml)
|
||||
|
||||
INCLUDE = -I include
|
||||
CPPFLAGS += $(VERSION_FLAGS) -MMD -MP
|
||||
CFLAGS += $(INCLUDE) $(LIBYAML_CFLAGS)
|
||||
CFLAGS += -std=c11 -Wall -Wextra -pedantic \
|
||||
-Wconversion -Wwrite-strings -Wcast-align -Wpointer-arith \
|
||||
-Winit-self -Wshadow -Wstrict-prototypes -Wmissing-prototypes \
|
||||
-Wredundant-decls -Wundef -Wvla -Wdeclaration-after-statement
|
||||
LDFLAGS += $(LIBYAML_LIBS)
|
||||
LDFLAGS_SHARED = -Wl,-soname=$(LIB_SH_MAJ) -shared
|
||||
|
||||
ifeq ($(VARIANT), debug)
|
||||
CFLAGS += -O0 -g
|
||||
else ifeq ($(VARIANT), san)
|
||||
CFLAGS += -O0 -g -fsanitize=address -fsanitize=undefined -fno-sanitize-recover
|
||||
LDFLAGS += -fsanitize=address -fsanitize=undefined -fno-sanitize-recover
|
||||
else
|
||||
CFLAGS += -O2 -DNDEBUG
|
||||
endif
|
||||
|
||||
ifneq ($(filter coverage,$(MAKECMDGOALS)),)
|
||||
BUILDDIR = build/coverage/$(VARIANT)
|
||||
CFLAGS_COV = --coverage -DNDEBUG
|
||||
LDFLAGS_COV = --coverage
|
||||
else
|
||||
BUILDDIR = build/$(VARIANT)
|
||||
CFLAGS_COV =
|
||||
LDFLAGS_COV =
|
||||
endif
|
||||
|
||||
BUILDDIR_SHARED = $(BUILDDIR)/shared
|
||||
BUILDDIR_STATIC = $(BUILDDIR)/static
|
||||
|
||||
LIB_SRC_FILES = mem.c free.c load.c save.c util.c utf8.c
|
||||
LIB_SRC := $(addprefix src/,$(LIB_SRC_FILES))
|
||||
LIB_OBJ = $(patsubst %.c,%.o, $(addprefix $(BUILDDIR)/,$(LIB_SRC)))
|
||||
LIB_DEP = $(patsubst %.c,%.d, $(addprefix $(BUILDDIR)/,$(LIB_SRC)))
|
||||
LIB_OBJ_SHARED = $(patsubst $(BUILDDIR)%,$(BUILDDIR_SHARED)%,$(LIB_OBJ))
|
||||
LIB_OBJ_STATIC = $(patsubst $(BUILDDIR)%,$(BUILDDIR_STATIC)%,$(LIB_OBJ))
|
||||
LIB_DEP_SHARED = $(patsubst $(BUILDDIR)%,$(BUILDDIR_SHARED)%,$(LIB_DEP))
|
||||
LIB_DEP_STATIC = $(patsubst $(BUILDDIR)%,$(BUILDDIR_STATIC)%,$(LIB_DEP))
|
||||
|
||||
LIB_PATH = LD_LIBRARY_PATH=$(BUILDDIR)
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
LIB_SHARED = $(LIB_NAME).dylib
|
||||
LIB_SH_MAJ = $(LIB_NAME).$(VERSION_MAJOR).dylib
|
||||
LIB_SH_VER = $(LIB_NAME).$(VERSION_STR).dylib
|
||||
LDFLAGS_SHARED = -dynamiclib -install_name "$(LIB_SH_MAJ)" -current_version $(VERSION_STR)
|
||||
LIB_PATH = DYLD_FALLBACK_LIBRARY_PATH=$(BUILDDIR)
|
||||
endif
|
||||
|
||||
TEST_SRC_FILES = units/free.c units/load.c units/test.c units/util.c \
|
||||
units/errs.c units/file.c units/save.c units/utf8.c
|
||||
TEST_SRC := $(addprefix test/,$(TEST_SRC_FILES))
|
||||
TEST_OBJ = $(patsubst %.c,%.o, $(addprefix $(BUILDDIR)/,$(TEST_SRC)))
|
||||
TEST_DEP = $(patsubst %.c,%.d, $(addprefix $(BUILDDIR)/,$(TEST_SRC)))
|
||||
|
||||
TEST_BINS = \
|
||||
$(BUILDDIR)/test/units/cyaml-shared \
|
||||
$(BUILDDIR)/test/units/cyaml-static
|
||||
|
||||
all: $(BUILDDIR)/$(LIB_SH_MAJ) $(BUILDDIR)/$(LIB_STATIC) examples
|
||||
|
||||
coverage: test-verbose
|
||||
$(Q)$(MKDIR) $(BUILDDIR)
|
||||
$(Q)gcovr -e 'test/.*' -r .
|
||||
$(Q)gcovr -e 'test/.*' -x -o build/coverage.xml -r .
|
||||
$(Q)gcovr -e 'test/.*' --html --html-details -o build/coverage.html -r .
|
||||
|
||||
test test-quiet test-verbose test-debug: $(TEST_BINS)
|
||||
$(Q)for i in $(^); do $(LIB_PATH) $$i $(subst test,,$(subst test-,--,$@)) "$(TESTLIST)" || exit; done
|
||||
|
||||
valgrind valgrind-quiet valgrind-verbose valgrind-debug: $(TEST_BINS)
|
||||
$(Q)for i in $(^); do $(LIB_PATH) $(VALGRIND) $$i $(subst valgrind,,$(subst valgrind-,--,$@)) "$(TESTLIST)" || exit; done
|
||||
|
||||
check: test
|
||||
|
||||
$(BUILDDIR)/$(LIB_PKGCON): $(LIB_PKGCON).in
|
||||
sed \
|
||||
-e 's#PREFIX#$(PREFIX)#' \
|
||||
-e 's#LIBDIR#$(LIBDIR)#' \
|
||||
-e 's#INCLUDEDIR#$(INCLUDEDIR)#' \
|
||||
-e 's#VERSION#$(VERSION_STR)#' \
|
||||
$(LIB_PKGCON).in >$(BUILDDIR)/$(LIB_PKGCON)
|
||||
|
||||
$(BUILDDIR)/$(LIB_STATIC): $(LIB_OBJ_STATIC)
|
||||
$(AR) -rcs $@ $^
|
||||
|
||||
$(BUILDDIR)/$(LIB_SH_MAJ): $(LIB_OBJ_SHARED)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(LDFLAGS_COV) $(LDFLAGS_SHARED)
|
||||
|
||||
$(LIB_OBJ_STATIC): $(BUILDDIR_STATIC)/%.o : %.c
|
||||
$(Q)$(MKDIR) $(dir $@)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_COV) -c -o $@ $<
|
||||
|
||||
$(LIB_OBJ_SHARED): $(BUILDDIR_SHARED)/%.o : %.c
|
||||
$(Q)$(MKDIR) $(dir $@)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC $(CFLAGS_COV) -c -o $@ $<
|
||||
|
||||
docs:
|
||||
$(MKDIR) build/docs/api
|
||||
$(MKDIR) build/docs/devel
|
||||
doxygen docs/api.doxygen.conf
|
||||
doxygen docs/devel.doxygen.conf
|
||||
|
||||
clean:
|
||||
rm -rf build/
|
||||
|
||||
install: $(BUILDDIR)/$(LIB_SH_MAJ) $(BUILDDIR)/$(LIB_STATIC) $(BUILDDIR)/$(LIB_PKGCON)
|
||||
$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(LIBDIR)
|
||||
$(INSTALL) $(BUILDDIR)/$(LIB_SH_MAJ) $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_SH_VER)
|
||||
(cd $(DESTDIR)$(PREFIX)/$(LIBDIR) && { ln -s -f $(LIB_SH_VER) $(LIB_SH_MAJ) || { rm -f $(LIB_SH_MAJ) && ln -s $(LIB_SH_VER) $(LIB_SH_MAJ); }; })
|
||||
(cd $(DESTDIR)$(PREFIX)/$(LIBDIR) && { ln -s -f $(LIB_SH_VER) $(LIB_SHARED) || { rm -f $(LIB_SHARED) && ln -s $(LIB_SH_VER) $(LIB_SHARED); }; })
|
||||
$(INSTALL) $(BUILDDIR)/$(LIB_STATIC) $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_STATIC)
|
||||
chmod 644 $(DESTDIR)$(PREFIX)/$(LIBDIR)/$(LIB_STATIC)
|
||||
$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(INCLUDEDIR)/cyaml
|
||||
$(INSTALL) -m 644 include/cyaml/* $(DESTDIR)$(PREFIX)/$(INCLUDEDIR)/cyaml
|
||||
$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(LIBDIR)/pkgconfig
|
||||
$(INSTALL) -m 644 $(BUILDDIR)/$(LIB_PKGCON) $(DESTDIR)$(PREFIX)/$(LIBDIR)/pkgconfig/$(LIB_PKGCON)
|
||||
|
||||
examples: $(BUILDDIR)/planner $(BUILDDIR)/numerical
|
||||
|
||||
$(BUILDDIR)/planner: examples/planner/main.c $(BUILDDIR)/$(LIB_STATIC)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(BUILDDIR)/numerical: examples/numerical/main.c $(BUILDDIR)/$(LIB_STATIC)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
-include $(LIB_DEP_SHARED) $(LIB_DEP_STATIC) $(TEST_DEP)
|
||||
|
||||
.PHONY: all test test-quiet test-verbose test-debug \
|
||||
valgrind valgrind-quiet valgrind-verbose valgrind-debug \
|
||||
clean coverage docs install examples check
|
||||
|
||||
$(BUILDDIR)/test/units/cyaml-static: $(TEST_OBJ) $(BUILDDIR)/$(LIB_STATIC)
|
||||
$(CC) $(LDFLAGS_COV) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(BUILDDIR)/test/units/cyaml-shared: $(TEST_OBJ) $(BUILDDIR)/$(LIB_SH_MAJ)
|
||||
$(CC) $(LDFLAGS_COV) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(TEST_OBJ): $(BUILDDIR)/%.o : %.c
|
||||
$(Q)$(MKDIR) $(dir $@)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_COV) -c -o $@ $<
|
112
libs/libcyaml/README.md
Normal file
112
libs/libcyaml/README.md
Normal file
@ -0,0 +1,112 @@
|
||||
LibCYAML: Schema-based YAML parsing and serialisation
|
||||
=====================================================
|
||||
|
||||
[](https://github.com/tlsa/libcyaml/actions) [](https://github.com/tlsa/libcyaml/actions/workflows/static-analysis.yaml) [](https://codecov.io/gh/tlsa/libcyaml)
|
||||
|
||||
LibCYAML is a C library for reading and writing structured YAML documents.
|
||||
It is written in ISO C11 and licensed under the ISC licence.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The fundamental idea behind CYAML is to allow applications to construct
|
||||
schemas which describe both the permissible structure of the YAML documents
|
||||
to read/write, and the C data structure(s) in which the loaded data is
|
||||
arranged in memory.
|
||||
|
||||
### Goals
|
||||
|
||||
* Make it easy to load YAML into client's custom C data structures.
|
||||
* Good compromise between flexibility and simplicity.
|
||||
|
||||
### Features
|
||||
|
||||
* Load, Save and Free functions.
|
||||
* Reads and writes arbitrary client data structures.
|
||||
* Schema-driven, allowing control over permitted YAML, for example:
|
||||
- Required / optional mapping fields.
|
||||
- Allowed / disallowed values.
|
||||
- Minimum / maximum sequence entry count.
|
||||
- etc...
|
||||
* Enumerations and flag words.
|
||||
* YAML backtraces make it simple for users to fix their YAML to
|
||||
conform to your schema.
|
||||
* Uses standard [`libyaml`](https://github.com/yaml/libyaml) library for
|
||||
low-level YAML read / write.
|
||||
* Support for YAML aliases and anchors.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
To build the library, run:
|
||||
|
||||
make
|
||||
|
||||
You can control the optimisation and building of asserts by setting
|
||||
the build variant:
|
||||
|
||||
make VARIANT=debug
|
||||
make VARIANT=release
|
||||
|
||||
Another debug build variant which is built with address sanitiser (incompatible
|
||||
with valgrind) can be built with:
|
||||
|
||||
make VARIANT=san
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install a release version of the library, run:
|
||||
|
||||
make install VARIANT=release
|
||||
|
||||
It will install to the PREFIX `/usr/local` by default, and it will use
|
||||
DESTDIR and PREFIX from the environment if set.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
To run the tests, run any of the following, which generate various
|
||||
levels of output verbosity (optionally setting `VARIANT=release`, or
|
||||
`VARIANT=san`):
|
||||
|
||||
make test
|
||||
make test-quiet
|
||||
make test-verbose
|
||||
make test-debug
|
||||
|
||||
To run the tests under `valgrind`, a similar set of targets is available:
|
||||
|
||||
make valgrind
|
||||
make valgrind-quiet
|
||||
make valgrind-verbose
|
||||
make valgrind-debug
|
||||
|
||||
To run a single test or a subset of tests, use the `TESTLIST` variable, which
|
||||
expects a space and/or comma separated list of test names:
|
||||
|
||||
make test-debug TESTLIST=test_load_mapping_without_any_fields
|
||||
make valgrind-debug TESTLIST="test_load_no_log test_util_state_invalid"
|
||||
|
||||
To generate a test coverage report, `gcovr` is required:
|
||||
|
||||
make coverage
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
To generate both public API documentation, and documentation of CYAML's
|
||||
internals, `doxygen` is required:
|
||||
|
||||
make docs
|
||||
|
||||
Alternatively, the read the API documentation directly from the
|
||||
[cyaml.h](https://github.com/tlsa/libcyaml/blob/main/include/cyaml/cyaml.h)
|
||||
header file.
|
||||
|
||||
There is also a [tutorial](docs/guide.md).
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
In addition to the documentation, you can study the [examples](examples/).
|
15
libs/libcyaml/docs/api.doxygen.conf
Normal file
15
libs/libcyaml/docs/api.doxygen.conf
Normal file
@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: ISC
|
||||
|
||||
# This generates documentation for developers using libcyaml.
|
||||
|
||||
PROJECT_NAME = "CYAML"
|
||||
OUTPUT_DIRECTORY = "build/docs/api"
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
EXTRACT_STATIC = NO
|
||||
MARKDOWN_SUPPORT = YES
|
||||
INPUT = include README.md docs/guide.md
|
||||
RECURSIVE = YES
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
GENERATE_HTML = YES
|
||||
GENERATE_LATEX = NO
|
||||
QUIET = YES
|
15
libs/libcyaml/docs/devel.doxygen.conf
Normal file
15
libs/libcyaml/docs/devel.doxygen.conf
Normal file
@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: ISC
|
||||
|
||||
# This generates documentation for developers developing libcyaml.
|
||||
|
||||
PROJECT_NAME = "CYAML Internals"
|
||||
OUTPUT_DIRECTORY = "build/docs/devel"
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
EXTRACT_STATIC = YES
|
||||
MARKDOWN_SUPPORT = YES
|
||||
INPUT = include src README.md docs/guide.md
|
||||
RECURSIVE = YES
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
GENERATE_HTML = YES
|
||||
GENERATE_LATEX = NO
|
||||
QUIET = YES
|
160
libs/libcyaml/docs/guide.md
Normal file
160
libs/libcyaml/docs/guide.md
Normal file
@ -0,0 +1,160 @@
|
||||
LibCYAML: Tutorial
|
||||
==================
|
||||
|
||||
This document is intended for C developers wishing to make use of
|
||||
[LibCYAML](https://github.com/tlsa/libcyaml).
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
If you want to use LibCYAML you'll need to have two things:
|
||||
|
||||
1. A consistent structure to the sort of YAML you want to load/save.
|
||||
2. Some C data structure to load/save to/from.
|
||||
|
||||
LibCYAML's aim is to make this as simple as possible for the programmer.
|
||||
However, LibCYAML knows nothing about either your data structure or the
|
||||
"shape" of the YAML you want to load. You provide this information by
|
||||
defining "schemas", and passing them to LibCYAML.
|
||||
|
||||
> **Note**: If you need to handle arbitrary "free-form" YAML (e.g. for a tool
|
||||
> to convert between YAML and JSON), then LibCYAML would not be much help.
|
||||
> In such a case, I'd recommend using [libyaml](https://github.com/yaml/libyaml)
|
||||
> directly.
|
||||
|
||||
A simple example: loading YAML
|
||||
------------------------------
|
||||
|
||||
Let's say you want to load the following YAML document:
|
||||
|
||||
```yaml
|
||||
name: Fibonacci
|
||||
data:
|
||||
- 1
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 5
|
||||
- 8
|
||||
```
|
||||
|
||||
And you want to load it into the following C data structure:
|
||||
|
||||
```c
|
||||
struct numbers {
|
||||
char *name;
|
||||
int *data;
|
||||
unsigned data_count;
|
||||
};
|
||||
```
|
||||
|
||||
Then we need to define a CYAML schema to describe these to LibCYAML.
|
||||
|
||||
> **Note**: Use the doxygen API documentation, or else the documentation in
|
||||
> [cyaml.h](https://github.com/tlsa/libcyaml/blob/main/include/cyaml/cyaml.h)
|
||||
> in conjunction with this guide.
|
||||
|
||||
At the top level of the YAML is a mapping with two fields, "name" and
|
||||
"data".
|
||||
|
||||
```yaml
|
||||
name:
|
||||
data:
|
||||
```
|
||||
|
||||
The first field is just a simple scalar value (it's neither
|
||||
a mapping nor a sequence). The second field has a sequence value.
|
||||
|
||||
We'll start by defining the CYAML schema for the "data" sequence,
|
||||
since since that's the "deepest" non-scalar type. The reason for
|
||||
starting here will become clear later.
|
||||
|
||||
```c
|
||||
/* CYAML value schema for entries of the data sequence. */
|
||||
static const cyaml_schema_value_t data_entry = {
|
||||
CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int),
|
||||
};
|
||||
```
|
||||
|
||||
Here we're making a `cyaml_schema_value_t` for the entries in the
|
||||
sequence. There are various `CYAML_VALUE_{TYPE}` macros to assist with
|
||||
this. Here we're using `CYAML_VALUE_INT`, because the value is a signed
|
||||
integer. The parameters passed to the macro are `enum cyaml_flag`, and
|
||||
the C data type of the value.
|
||||
|
||||
Now we can write the schema for the mapping. First we'll construct
|
||||
an array of `cyaml_schema_field_t` entries that describe each
|
||||
field in the mapping.
|
||||
|
||||
```c
|
||||
/* CYAML mapping schema fields array for the top level mapping. */
|
||||
static const cyaml_schema_field_t top_mapping_schema[] = {
|
||||
CYAML_FIELD_STRING_PTR(
|
||||
"name", CYAML_FLAG_POINTER, struct numbers, name, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_SEQUENCE(
|
||||
"data", CYAML_FLAG_POINTER, struct numbers, data, &data_entry, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
```
|
||||
|
||||
There are `CYAML_FIELD_{TYPE}` helper macros to construct the mapping field
|
||||
entries. The array must be terminated by a `CYAML_FIELD_END` entry.
|
||||
The helper macro parameters are specific to each `CYAML_FIELD_{TYPE}` macro.
|
||||
|
||||
The entry for the name field is of type string pointer. You can consult the
|
||||
documentation for the `CYAML_FIELD_{TYPE}` macros to see what the parameters
|
||||
mean.
|
||||
|
||||
> **Note**: The field for the sequence takes a pointer to the sequence entry
|
||||
> data type that we defined earlier as `data_entry`.
|
||||
|
||||
Finally we can define the schema for the top level value that gets passed to
|
||||
the LibCYAML.
|
||||
|
||||
```c
|
||||
/* CYAML value schema for the top level mapping. */
|
||||
static const cyaml_schema_value_t top_schema = {
|
||||
CYAML_VALUE_MAPPING(
|
||||
CYAML_FLAG_POINTER, struct numbers, top_mapping_schema),
|
||||
};
|
||||
```
|
||||
|
||||
In this case our top level value is a mapping type. One of the parameters
|
||||
needed for mappings is the array of field definitions. In this case we're
|
||||
passing the `top_mapping_schema` that we defined above.
|
||||
|
||||
```c
|
||||
/* Create our CYAML configuration. */
|
||||
static const cyaml_config_t config = {
|
||||
.log_fn = cyaml_log, /* Use the default logging function. */
|
||||
.mem_fn = cyaml_mem, /* Use the default memory allocator. */
|
||||
.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
|
||||
};
|
||||
|
||||
/* Where to store the loaded data */
|
||||
struct numbers *n;
|
||||
|
||||
/* Load the file into n */
|
||||
cyaml_err_t err = cyaml_load_file(argv[ARG_PATH_IN], &config,
|
||||
&top_schema, (cyaml_data_t **)&n, NULL);
|
||||
if (err != CYAML_OK) {
|
||||
/* Handle error */
|
||||
}
|
||||
|
||||
/* Use the data. */
|
||||
printf("%s:\n", n->name);
|
||||
for (unsigned i = 0; i < n->data_count; i++) {
|
||||
printf(" - %i\n", n->data[i]);
|
||||
}
|
||||
|
||||
/* Free the data */
|
||||
err = cyaml_free(&config, &top_schema, n, 0);
|
||||
if (err != CYAML_OK) {
|
||||
/* Handle error */
|
||||
}
|
||||
```
|
||||
|
||||
And that's it, the YAML is loaded into the custom C data structure.
|
||||
|
||||
You can find the code for this in the "numerical" example in the
|
||||
[examples](../examples) directory.
|
7
libs/libcyaml/examples/numerical/README.md
Normal file
7
libs/libcyaml/examples/numerical/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
Simple LibCYAML example
|
||||
=======================
|
||||
|
||||
This has a simple YAML document for the Fibonacci sequence, and an example
|
||||
LibCYAML binding to load/free the data from C.
|
||||
|
||||
The [tutorial](../../docs/guide.md) explains how this works.
|
8
libs/libcyaml/examples/numerical/fibonacci.yaml
Normal file
8
libs/libcyaml/examples/numerical/fibonacci.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
name: Fibonacci
|
||||
data:
|
||||
- 1
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 5
|
||||
- 8
|
107
libs/libcyaml/examples/numerical/main.c
Normal file
107
libs/libcyaml/examples/numerical/main.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2018 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cyaml/cyaml.h>
|
||||
|
||||
/******************************************************************************
|
||||
* C data structure for storing a project plan.
|
||||
*
|
||||
* This is what we want to load the YAML into.
|
||||
******************************************************************************/
|
||||
|
||||
/* Structure for storing numerical sequence */
|
||||
struct numbers {
|
||||
char *name;
|
||||
int *data;
|
||||
unsigned data_count;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* CYAML schema to tell libcyaml about both expected YAML and data structure.
|
||||
*
|
||||
* (Our CYAML schema is just a bunch of static const data.)
|
||||
******************************************************************************/
|
||||
|
||||
/* CYAML value schema for entries of the data sequence. */
|
||||
static const cyaml_schema_value_t data_entry = {
|
||||
CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int),
|
||||
};
|
||||
|
||||
/* CYAML mapping schema fields array for the top level mapping. */
|
||||
static const cyaml_schema_field_t top_mapping_schema[] = {
|
||||
CYAML_FIELD_STRING_PTR("name", CYAML_FLAG_POINTER,
|
||||
struct numbers, name,
|
||||
0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_SEQUENCE("data", CYAML_FLAG_POINTER,
|
||||
struct numbers, data, &data_entry,
|
||||
0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
|
||||
/* CYAML value schema for the top level mapping. */
|
||||
static const cyaml_schema_value_t top_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct numbers, top_mapping_schema),
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Actual code to load and save YAML doc using libcyaml.
|
||||
******************************************************************************/
|
||||
|
||||
/* Our CYAML config.
|
||||
*
|
||||
* If you want to change it between calls, don't make it const.
|
||||
*
|
||||
* Here we have a very basic config.
|
||||
*/
|
||||
static const cyaml_config_t config = {
|
||||
.log_fn = cyaml_log, /* Use the default logging function. */
|
||||
.mem_fn = cyaml_mem, /* Use the default memory allocator. */
|
||||
.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
|
||||
};
|
||||
|
||||
/* Main entry point from OS. */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
cyaml_err_t err;
|
||||
struct numbers *n;
|
||||
enum {
|
||||
ARG_PROG_NAME,
|
||||
ARG_PATH_IN,
|
||||
ARG__COUNT,
|
||||
};
|
||||
|
||||
/* Handle args */
|
||||
if (argc != ARG__COUNT) {
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, " %s <INPUT>\n", argv[ARG_PROG_NAME]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Load input file. */
|
||||
err = cyaml_load_file(argv[ARG_PATH_IN], &config,
|
||||
&top_schema, (cyaml_data_t **)&n, NULL);
|
||||
if (err != CYAML_OK) {
|
||||
fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Use the data. */
|
||||
printf("%s:\n", n->name);
|
||||
for (unsigned i = 0; i < n->data_count; i++) {
|
||||
printf(" - %i\n", n->data[i]);
|
||||
}
|
||||
|
||||
/* Free the data */
|
||||
cyaml_free(&config, &top_schema, n, 0);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
8
libs/libcyaml/examples/planner/README.md
Normal file
8
libs/libcyaml/examples/planner/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
Simple LibCYAML example
|
||||
=======================
|
||||
|
||||
This has a simple YAML document for project planning, and an example
|
||||
LibCYAML binding to load/modify/save/free the data from C.
|
||||
|
||||
The C source code is heavliy documented to help explain how to
|
||||
write a CYAML schema data structure.
|
468
libs/libcyaml/examples/planner/main.c
Normal file
468
libs/libcyaml/examples/planner/main.c
Normal file
@ -0,0 +1,468 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2019 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cyaml/cyaml.h>
|
||||
|
||||
/******************************************************************************
|
||||
* C data structure for storing a project plan.
|
||||
*
|
||||
* This is what we want to load the YAML into.
|
||||
******************************************************************************/
|
||||
|
||||
/* Enumeration of months of the year */
|
||||
enum months {
|
||||
MONTH_JAN = 1,
|
||||
MONTH_FEB,
|
||||
MONTH_MAR,
|
||||
MONTH_APR,
|
||||
MONTH_MAY,
|
||||
MONTH_JUN,
|
||||
MONTH_JUL,
|
||||
MONTH_AUG,
|
||||
MONTH_SEP,
|
||||
MONTH_OCT,
|
||||
MONTH_NOV,
|
||||
MONTH_DEC
|
||||
};
|
||||
|
||||
/* Structure for storing dates */
|
||||
struct date {
|
||||
uint8_t day;
|
||||
enum months month;
|
||||
uint16_t year;
|
||||
};
|
||||
|
||||
/* Structure for storing durations */
|
||||
struct duration {
|
||||
uint8_t hours;
|
||||
unsigned days;
|
||||
unsigned weeks;
|
||||
unsigned years;
|
||||
};
|
||||
|
||||
/* Enumeration of task flags */
|
||||
enum task_flags {
|
||||
FLAGS_NONE = 0,
|
||||
FLAGS_IMPORTANT = (1 << 0),
|
||||
FLAGS_ENGINEERING = (1 << 1),
|
||||
FLAGS_DOCUMENTATION = (1 << 2),
|
||||
FLAGS_MANAGEMENT = (1 << 3),
|
||||
};
|
||||
|
||||
/* Structure for storing a task */
|
||||
struct task {
|
||||
const char *name;
|
||||
enum task_flags flags;
|
||||
struct duration estimate;
|
||||
|
||||
const char **depends;
|
||||
unsigned depends_count;
|
||||
|
||||
const char **people;
|
||||
unsigned n_people;
|
||||
};
|
||||
|
||||
/* Top-level structure for storing a plan */
|
||||
struct plan {
|
||||
const char *name;
|
||||
struct date *start;
|
||||
|
||||
const char **people;
|
||||
unsigned n_people;
|
||||
|
||||
struct task *tasks;
|
||||
uint64_t tasks_count;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* CYAML schema to tell libcyaml about both expected YAML and data structure.
|
||||
*
|
||||
* (Our CYAML schema is just a bunch of static const data.)
|
||||
******************************************************************************/
|
||||
|
||||
/* Mapping from "task_flags" strings to enum values for schema. */
|
||||
static const cyaml_strval_t task_flags_strings[] = {
|
||||
{ "None", FLAGS_NONE },
|
||||
{ "Important", FLAGS_IMPORTANT },
|
||||
{ "Engineering", FLAGS_ENGINEERING },
|
||||
{ "Documentation", FLAGS_DOCUMENTATION },
|
||||
{ "Management", FLAGS_MANAGEMENT },
|
||||
};
|
||||
|
||||
/* Mapping from "month" strings to flag values for schema. */
|
||||
static const cyaml_strval_t month_strings[] = {
|
||||
{ "January", MONTH_JAN },
|
||||
{ "February", MONTH_FEB },
|
||||
{ "March", MONTH_MAR },
|
||||
{ "April", MONTH_APR },
|
||||
{ "May", MONTH_MAY },
|
||||
{ "June", MONTH_JUN },
|
||||
{ "July", MONTH_JUL },
|
||||
{ "August", MONTH_AUG },
|
||||
{ "September", MONTH_SEP },
|
||||
{ "October", MONTH_OCT },
|
||||
{ "November", MONTH_NOV },
|
||||
{ "December", MONTH_DEC },
|
||||
};
|
||||
|
||||
/* Schema for string pointer values (used in sequences of strings). */
|
||||
static const cyaml_schema_value_t string_ptr_schema = {
|
||||
CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
|
||||
};
|
||||
|
||||
/* The duration mapping's field definitions schema is an array.
|
||||
*
|
||||
* All the field entries will refer to their parent mapping's structure,
|
||||
* in this case, `struct duration`.
|
||||
*/
|
||||
static const cyaml_schema_field_t duration_fields_schema[] = {
|
||||
/* The fields here are all optional unsigned integers.
|
||||
*
|
||||
* Note that if an optional field is unset in the YAML, its value
|
||||
* will be zero in the C data structure.
|
||||
*
|
||||
* In all cases here, the YAML mapping key name (first parameter to
|
||||
* the macros) matches the name of the associated member of the
|
||||
* `duration` structure (fourth parameter).
|
||||
*/
|
||||
CYAML_FIELD_UINT(
|
||||
"hours", CYAML_FLAG_OPTIONAL,
|
||||
struct duration, hours),
|
||||
CYAML_FIELD_UINT(
|
||||
"days", CYAML_FLAG_OPTIONAL,
|
||||
struct duration, days),
|
||||
CYAML_FIELD_UINT(
|
||||
"weeks", CYAML_FLAG_OPTIONAL,
|
||||
struct duration, weeks),
|
||||
CYAML_FIELD_UINT(
|
||||
"years", CYAML_FLAG_OPTIONAL,
|
||||
struct duration, years),
|
||||
|
||||
/* The field array must be terminated by an entry with a NULL key.
|
||||
* Here we use the CYAML_FIELD_END macro for that. */
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
|
||||
/* The date mapping's field definitions schema is an array.
|
||||
*
|
||||
* All the field entries will refer to their parent mapping's structure,
|
||||
* in this case, `struct date`.
|
||||
*/
|
||||
static const cyaml_schema_field_t date_fields_schema[] = {
|
||||
/* The "day" and "year" fields are just normal UNIT CYAML types.
|
||||
* See the comments for duration_fields_schema for details.
|
||||
* The only difference is neither of these are optional.
|
||||
* Note: The order of the fields in this array doesn't matter.
|
||||
*/
|
||||
CYAML_FIELD_UINT(
|
||||
"day", CYAML_FLAG_DEFAULT,
|
||||
struct date, day),
|
||||
|
||||
CYAML_FIELD_UINT(
|
||||
"year", CYAML_FLAG_DEFAULT,
|
||||
struct date, year),
|
||||
|
||||
/* The month field is an enum.
|
||||
*
|
||||
* YAML key: "month".
|
||||
* C structure member for this key: "month".
|
||||
* Array mapping strings to values: month_strings
|
||||
*
|
||||
* Its CYAML type is ENUM, so an array of cyaml_strval_t must be
|
||||
* provided to map from string to values.
|
||||
* Note that we're not setting the strict flag here so both strings and
|
||||
* numbers are accepted in the YAML. (For example, both "4" and "April"
|
||||
* would be accepted.)
|
||||
*/
|
||||
CYAML_FIELD_ENUM(
|
||||
"month", CYAML_FLAG_DEFAULT,
|
||||
struct date, month, month_strings,
|
||||
CYAML_ARRAY_LEN(month_strings)),
|
||||
|
||||
/* The field array must be terminated by an entry with a NULL key.
|
||||
* Here we use the CYAML_FIELD_END macro for that. */
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
|
||||
/* The task mapping's field definitions schema is an array.
|
||||
*
|
||||
* All the field entries will refer to their parent mapping's structure,
|
||||
* in this case, `struct task`.
|
||||
*/
|
||||
static const cyaml_schema_field_t task_fields_schema[] = {
|
||||
/* The first field in the mapping is a task name.
|
||||
*
|
||||
* YAML key: "name".
|
||||
* C structure member for this key: "name".
|
||||
*
|
||||
* Its CYAML type is string pointer, and we have no minimum or maximum
|
||||
* string length constraints.
|
||||
*/
|
||||
CYAML_FIELD_STRING_PTR(
|
||||
"name", CYAML_FLAG_POINTER,
|
||||
struct task, name, 0, CYAML_UNLIMITED),
|
||||
|
||||
/* The flags field is a flags value.
|
||||
*
|
||||
* YAML key: "flags".
|
||||
* C structure member for this key: "flags".
|
||||
* Array mapping strings to values: task_flags_strings
|
||||
*
|
||||
* In the YAML a CYAML flags value should be a sequence of scalars.
|
||||
* The values of each set scalar is looked up the in array of
|
||||
* string/value mappings, and the values are bitwise ORed together.
|
||||
*
|
||||
* Note that we're setting the strict flag here so only strings
|
||||
* present in task_flags_strings are allowed, and numbers are not.
|
||||
*
|
||||
* We make the field optional so when there are no flags set, the field
|
||||
* can be omitted from the YAML.
|
||||
*/
|
||||
CYAML_FIELD_FLAGS(
|
||||
"flags", CYAML_FLAG_OPTIONAL | CYAML_FLAG_STRICT,
|
||||
struct task, flags, task_flags_strings,
|
||||
CYAML_ARRAY_LEN(task_flags_strings)),
|
||||
|
||||
/* The next field is the task estimate.
|
||||
*
|
||||
* YAML key: "estimate".
|
||||
* C structure member for this key: "estimate".
|
||||
*
|
||||
* Its CYAML type is a mapping.
|
||||
*
|
||||
* Since it's a mapping type, the schema for its mapping's fields must
|
||||
* be provided too. In this case, it's `duration_fields_schema`.
|
||||
*/
|
||||
CYAML_FIELD_MAPPING(
|
||||
"estimate", CYAML_FLAG_DEFAULT,
|
||||
struct task, estimate, duration_fields_schema),
|
||||
|
||||
/* The next field is the tasks that this task depends on.
|
||||
*
|
||||
* YAML key: "depends".
|
||||
* C structure member for this key: "depends".
|
||||
*
|
||||
* Its CYAML type is a sequence.
|
||||
*
|
||||
* Since it's a sequence type, the value schema for its entries must
|
||||
* be provided too. In this case, it's string_ptr_schema.
|
||||
*
|
||||
* Since it's not a sequence of a fixed-length, we must tell CYAML
|
||||
* where the sequence entry count is to be stored. In this case, it
|
||||
* goes in the "depends_count" C structure member in `struct task`.
|
||||
* Since this is "depends" with the "_count" postfix, we can use
|
||||
* the following macro, which assumes a postfix of "_count" in the
|
||||
* struct member name.
|
||||
*/
|
||||
CYAML_FIELD_SEQUENCE(
|
||||
"depends", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
|
||||
struct task, depends,
|
||||
&string_ptr_schema, 0, CYAML_UNLIMITED),
|
||||
|
||||
/* The next field is the task people.
|
||||
*
|
||||
* YAML key: "people".
|
||||
* C structure member for this key: "people".
|
||||
*
|
||||
* Its CYAML type is a sequence.
|
||||
*
|
||||
* Since it's a sequence type, the value schema for its entries must
|
||||
* be provided too. In this case, it's string_ptr_schema.
|
||||
*
|
||||
* Since it's not a sequence of a fixed-length, we must tell CYAML
|
||||
* where the sequence entry count is to be stored. In this case, it
|
||||
* goes in the "n_people" C structure member in `struct plan`.
|
||||
*/
|
||||
CYAML_FIELD_SEQUENCE_COUNT(
|
||||
"people", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
|
||||
struct task, people, n_people,
|
||||
&string_ptr_schema, 0, CYAML_UNLIMITED),
|
||||
|
||||
/* The field array must be terminated by an entry with a NULL key.
|
||||
* Here we use the CYAML_FIELD_END macro for that. */
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
|
||||
/* The value for a task is a mapping.
|
||||
*
|
||||
* Its fields are defined in task_fields_schema.
|
||||
*/
|
||||
static const cyaml_schema_value_t task_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
|
||||
struct task, task_fields_schema),
|
||||
};
|
||||
|
||||
/* The plan mapping's field definitions schema is an array.
|
||||
*
|
||||
* All the field entries will refer to their parent mapping's structure,
|
||||
* in this case, `struct plan`.
|
||||
*/
|
||||
static const cyaml_schema_field_t plan_fields_schema[] = {
|
||||
/* The first field in the mapping is a project name.
|
||||
*
|
||||
* YAML key: "project".
|
||||
* C structure member for this key: "name".
|
||||
*
|
||||
* Its CYAML type is string pointer, and we have no minimum or maximum
|
||||
* string length constraints.
|
||||
*/
|
||||
CYAML_FIELD_STRING_PTR(
|
||||
"project", CYAML_FLAG_POINTER,
|
||||
struct plan, name, 0, CYAML_UNLIMITED),
|
||||
|
||||
/* The next field is the project start date.
|
||||
*
|
||||
* YAML key: "start".
|
||||
* C structure member for this key: "start".
|
||||
*
|
||||
* Its CYAML type is a mapping pointer.
|
||||
*
|
||||
* Since it's a mapping type, the schema for its mapping's fields must
|
||||
* be provided too. In this case, it's `date_fields_schema`.
|
||||
*/
|
||||
CYAML_FIELD_MAPPING_PTR(
|
||||
"start", CYAML_FLAG_POINTER,
|
||||
struct plan, start, date_fields_schema),
|
||||
|
||||
/* The next field is the project people.
|
||||
*
|
||||
* YAML key: "people".
|
||||
* C structure member for this key: "people".
|
||||
*
|
||||
* Its CYAML type is a sequence.
|
||||
*
|
||||
* Since it's a sequence type, the value schema for its entries must
|
||||
* be provided too. In this case, it's string_ptr_schema.
|
||||
*
|
||||
* Since it's not a sequence of a fixed-length, we must tell CYAML
|
||||
* where the sequence entry count is to be stored. In this case, it
|
||||
* goes in the "n_people" C structure member in `struct plan`.
|
||||
*/
|
||||
CYAML_FIELD_SEQUENCE_COUNT(
|
||||
"people", CYAML_FLAG_POINTER,
|
||||
struct plan, people, n_people,
|
||||
&string_ptr_schema, 0, CYAML_UNLIMITED),
|
||||
|
||||
/* The next field is the project tasks.
|
||||
*
|
||||
* YAML key: "tasks".
|
||||
* C structure member for this key: "tasks".
|
||||
*
|
||||
* Its CYAML type is a sequence.
|
||||
*
|
||||
* Since it's a sequence type, the value schema for its entries must
|
||||
* be provided too. In this case, it's task_schema.
|
||||
*
|
||||
* Since it's not a sequence of a fixed-length, we must tell CYAML
|
||||
* where the sequence entry count is to be stored. In this case, it
|
||||
* goes in the "tasks_count" C structure member in `struct plan`.
|
||||
* Since this is "tasks" with the "_count" postfix, we can use
|
||||
* the following macro, which assumes a postfix of "_count" in the
|
||||
* struct member name.
|
||||
*/
|
||||
CYAML_FIELD_SEQUENCE(
|
||||
"tasks", CYAML_FLAG_POINTER,
|
||||
struct plan, tasks,
|
||||
&task_schema, 0, CYAML_UNLIMITED),
|
||||
|
||||
/* If the YAML contains a field that our program is not interested in
|
||||
* we can ignore it, so the value of the field will not be loaded.
|
||||
*
|
||||
* Note that unless the OPTIONAL flag is set, the ignored field must
|
||||
* be present.
|
||||
*
|
||||
* Another way of handling this would be to use the config flag
|
||||
* to ignore unknown keys. This config is passed to libcyaml
|
||||
* separately from the schema.
|
||||
*/
|
||||
CYAML_FIELD_IGNORE("irrelevant", CYAML_FLAG_OPTIONAL),
|
||||
|
||||
/* The field array must be terminated by an entry with a NULL key.
|
||||
* Here we use the CYAML_FIELD_END macro for that. */
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
|
||||
/* Top-level schema. The top level value for the plan is a mapping.
|
||||
*
|
||||
* Its fields are defined in plan_fields_schema.
|
||||
*/
|
||||
static const cyaml_schema_value_t plan_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct plan, plan_fields_schema),
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Actual code to load and save YAML doc using libcyaml.
|
||||
******************************************************************************/
|
||||
|
||||
/* Our CYAML config.
|
||||
*
|
||||
* If you want to change it between calls, don't make it const.
|
||||
*
|
||||
* Here we have a very basic config.
|
||||
*/
|
||||
static const cyaml_config_t config = {
|
||||
.log_fn = cyaml_log, /* Use the default logging function. */
|
||||
.mem_fn = cyaml_mem, /* Use the default memory allocator. */
|
||||
.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
|
||||
};
|
||||
|
||||
/* Main entry point from OS. */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
cyaml_err_t err;
|
||||
struct plan *plan;
|
||||
enum {
|
||||
ARG_PROG_NAME,
|
||||
ARG_PATH_IN,
|
||||
ARG_PATH_OUT,
|
||||
ARG__COUNT,
|
||||
};
|
||||
|
||||
/* Handle args */
|
||||
if (argc != ARG__COUNT) {
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, " %s <INPUT> <OUTPUT>\n", argv[ARG_PROG_NAME]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Load input file. */
|
||||
err = cyaml_load_file(argv[ARG_PATH_IN], &config,
|
||||
&plan_schema, (void **) &plan, NULL);
|
||||
if (err != CYAML_OK) {
|
||||
fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Use the data. */
|
||||
printf("Project: %s\n", plan->name);
|
||||
for (unsigned i = 0; i < plan->tasks_count; i++) {
|
||||
printf("%u. %s\n", i + 1, plan->tasks[i].name);
|
||||
}
|
||||
|
||||
/* Modify the data */
|
||||
plan->tasks[0].estimate.days += 3;
|
||||
plan->tasks[0].estimate.weeks += 1;
|
||||
|
||||
/* Save data to new YAML file. */
|
||||
err = cyaml_save_file(argv[ARG_PATH_OUT], &config,
|
||||
&plan_schema, plan, 0);
|
||||
if (err != CYAML_OK) {
|
||||
fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err));
|
||||
cyaml_free(&config, &plan_schema, plan, 0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Free the data */
|
||||
cyaml_free(&config, &plan_schema, plan, 0);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
58
libs/libcyaml/examples/planner/project.yaml
Normal file
58
libs/libcyaml/examples/planner/project.yaml
Normal file
@ -0,0 +1,58 @@
|
||||
project: Write new browser layout engine.
|
||||
start:
|
||||
day: 1
|
||||
month: June
|
||||
year: 2018
|
||||
people:
|
||||
- Stephen
|
||||
- Neil
|
||||
- Alex
|
||||
irrelevant:
|
||||
- details:
|
||||
- the app doesn't need this stuff
|
||||
- so it should be able to ignore it
|
||||
|
||||
tasks:
|
||||
- name: Read the HTML and CSS specs.
|
||||
flags:
|
||||
- Important
|
||||
estimate:
|
||||
weeks: 2
|
||||
people:
|
||||
- Stephen
|
||||
- Neil
|
||||
|
||||
- name: Think of name for library.
|
||||
estimate:
|
||||
hours: 1
|
||||
|
||||
- name: Create project repo.
|
||||
estimate:
|
||||
hours: 1
|
||||
depends:
|
||||
- Think of name for library.
|
||||
people:
|
||||
- Alex
|
||||
|
||||
- name: Initial design of library API.
|
||||
flags:
|
||||
- Important
|
||||
- Engineering
|
||||
depends:
|
||||
- Read the HTML and CSS specs.
|
||||
- Create project repo.
|
||||
estimate:
|
||||
days: 1
|
||||
|
||||
- name: Plan the initial implementation
|
||||
flags:
|
||||
- Engineering
|
||||
- Documentation
|
||||
- Management
|
||||
depends:
|
||||
- Initial design of library API.
|
||||
estimate:
|
||||
days: 6
|
||||
people:
|
||||
- Stephen
|
||||
- Alex
|
1696
libs/libcyaml/include/cyaml/cyaml.h
Normal file
1696
libs/libcyaml/include/cyaml/cyaml.h
Normal file
File diff suppressed because it is too large
Load Diff
10
libs/libcyaml/libcyaml.pc.in
Normal file
10
libs/libcyaml/libcyaml.pc.in
Normal file
@ -0,0 +1,10 @@
|
||||
prefix=PREFIX
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/LIBDIR
|
||||
includedir=${prefix}/INCLUDEDIR
|
||||
|
||||
Name: libcyaml
|
||||
Description: Schema-based YAML parsing and serialisation
|
||||
Version: VERSION
|
||||
Libs: -L${libdir} -lcyaml -lyaml
|
||||
Cflags: -I${includedir}
|
126
libs/libcyaml/src/data.h
Normal file
126
libs/libcyaml/src/data.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief CYAML functions for manipulating client data structures.
|
||||
*/
|
||||
|
||||
#ifndef CYAML_DATA_H
|
||||
#define CYAML_DATA_H
|
||||
|
||||
#include "cyaml/cyaml.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* Write a value of up to eight bytes to data_target.
|
||||
*
|
||||
* \param[in] value The value to write.
|
||||
* \param[in] entry_size The number of bytes of value to write.
|
||||
* \param[in] data_tgt The address to write to.
|
||||
* \return \ref CYAML_OK on success, or appropriate error code otherwise.
|
||||
*/
|
||||
static inline cyaml_err_t cyaml_data_write(
|
||||
uint64_t value,
|
||||
uint64_t entry_size,
|
||||
uint8_t *data_tgt)
|
||||
{
|
||||
const uint8_t *value_bytes = (uint8_t *)&value;
|
||||
|
||||
if (entry_size == 0 || entry_size > sizeof(value)) {
|
||||
return CYAML_ERR_INVALID_DATA_SIZE;
|
||||
}
|
||||
|
||||
if (cyaml__host_is_big_endian()) {
|
||||
value_bytes += sizeof(value) - entry_size;
|
||||
}
|
||||
|
||||
memcpy(data_tgt, value_bytes, entry_size);
|
||||
|
||||
return CYAML_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a pointer to data.
|
||||
*
|
||||
* This is a wrapper for \ref cyaml_data_write that does a compile time
|
||||
* assertion on the pointer size, so it can never return a runtime error.
|
||||
*
|
||||
* \param[in] ptr The pointer address to write.
|
||||
* \param[in] data_target The address to write to.
|
||||
*/
|
||||
static inline void cyaml_data_write_pointer(
|
||||
const void *ptr,
|
||||
uint8_t *data_target)
|
||||
{
|
||||
/* Refuse to build on platforms where sizeof pointer would
|
||||
* lead to \ref CYAML_ERR_INVALID_DATA_SIZE. */
|
||||
static_assert(sizeof(char *) > 0, "Incompatible pointer size.");
|
||||
static_assert(sizeof(char *) <= sizeof(uint64_t),
|
||||
"Incompatible pointer size.");
|
||||
|
||||
CYAML_UNUSED(cyaml_data_write((uint64_t)ptr, sizeof(ptr), data_target));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a value of up to eight bytes from data.
|
||||
*
|
||||
* \param[in] entry_size The number of bytes to read.
|
||||
* \param[in] data The address to read from.
|
||||
* \param[out] error_out Returns the error code. \ref CYAML_OK on success,
|
||||
* or appropriate error otherwise.
|
||||
* \return On success, returns the value read from data.
|
||||
* On failure, returns 0.
|
||||
*/
|
||||
static inline uint64_t cyaml_data_read(
|
||||
uint64_t entry_size,
|
||||
const uint8_t *data,
|
||||
cyaml_err_t *error_out)
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
uint8_t *ret_bytes = (uint8_t *)&ret;
|
||||
|
||||
if (entry_size == 0 || entry_size > sizeof(ret)) {
|
||||
*error_out = CYAML_ERR_INVALID_DATA_SIZE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (cyaml__host_is_big_endian()) {
|
||||
ret_bytes += sizeof(ret) - entry_size;
|
||||
}
|
||||
|
||||
memcpy(ret_bytes, data, entry_size);
|
||||
|
||||
*error_out = CYAML_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a pointer from data.
|
||||
*
|
||||
* This is a wrapper for \ref cyaml_data_read that does a compile time
|
||||
* assertion on the pointer size, so it can never return a runtime error.
|
||||
*
|
||||
* \param[in] data The address to read from.
|
||||
* \return Returns the value read from data.
|
||||
*/
|
||||
static inline uint8_t * cyaml_data_read_pointer(
|
||||
const uint8_t *data)
|
||||
{
|
||||
cyaml_err_t err;
|
||||
|
||||
/* Refuse to build on platforms where sizeof pointer would
|
||||
* lead to \ref CYAML_ERR_INVALID_DATA_SIZE. */
|
||||
static_assert(sizeof(char *) > 0, "Incompatible pointer size.");
|
||||
static_assert(sizeof(char *) <= sizeof(uint64_t),
|
||||
"Incompatible pointer size.");
|
||||
|
||||
return (void *)cyaml_data_read(sizeof(char *), data, &err);
|
||||
}
|
||||
|
||||
#endif
|
161
libs/libcyaml/src/free.c
Normal file
161
libs/libcyaml/src/free.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2017-2019 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief Free data structures created by the CYAML load functions.
|
||||
*
|
||||
* As described in the public API for \ref cyaml_free(), it is preferable for
|
||||
* clients to write their own free routines, tailored for their data structure.
|
||||
*
|
||||
* Recursion and stack usage
|
||||
* -------------------------
|
||||
*
|
||||
* This generic CYAML free routine is implemented using recursion, rather
|
||||
* than iteration with a heap-allocated stack. This is because recursion
|
||||
* seems less bad than allocating within the free code, and the stack-cost
|
||||
* of these functions isn't huge. The maximum recursion depth is of course
|
||||
* bound by the schema, however schemas for recursively nesting data structures
|
||||
* are unbound, e.g. for a data tree structure.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "util.h"
|
||||
#include "mem.h"
|
||||
|
||||
/**
|
||||
* Internal function for freeing a CYAML-parsed data structure.
|
||||
*
|
||||
* \param[in] cfg The client's CYAML library config.
|
||||
* \param[in] schema The schema describing how to free `data`.
|
||||
* \param[in] data The data structure to be freed.
|
||||
* \param[in] count If data is of type \ref CYAML_SEQUENCE, this is the
|
||||
* number of entries in the sequence.
|
||||
*/
|
||||
static void cyaml__free_value(
|
||||
const cyaml_config_t *cfg,
|
||||
const cyaml_schema_value_t *schema,
|
||||
uint8_t * data,
|
||||
uint64_t count);
|
||||
|
||||
/**
|
||||
* Internal function for freeing a CYAML-parsed sequence.
|
||||
*
|
||||
* \param[in] cfg The client's CYAML library config.
|
||||
* \param[in] sequence_schema The schema describing how to free `data`.
|
||||
* \param[in] data The data structure to be freed.
|
||||
* \param[in] count The sequence's entry count.
|
||||
*/
|
||||
static void cyaml__free_sequence(
|
||||
const cyaml_config_t *cfg,
|
||||
const cyaml_schema_value_t *sequence_schema,
|
||||
uint8_t * const data,
|
||||
uint64_t count)
|
||||
{
|
||||
const cyaml_schema_value_t *schema = sequence_schema->sequence.entry;
|
||||
uint32_t data_size = schema->data_size;
|
||||
|
||||
cyaml__log(cfg, CYAML_LOG_DEBUG,
|
||||
"Free: Freeing sequence with count: %u\n", count);
|
||||
|
||||
if (schema->flags & CYAML_FLAG_POINTER) {
|
||||
data_size = sizeof(data);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
cyaml__log(cfg, CYAML_LOG_DEBUG,
|
||||
"Free: Freeing sequence entry: %u\n", i);
|
||||
cyaml__free_value(cfg, schema, data + data_size * i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function for freeing a CYAML-parsed mapping.
|
||||
*
|
||||
* \param[in] cfg The client's CYAML library config.
|
||||
* \param[in] mapping_schema The schema describing how to free `data`.
|
||||
* \param[in] data The data structure to be freed.
|
||||
*/
|
||||
static void cyaml__free_mapping(
|
||||
const cyaml_config_t *cfg,
|
||||
const cyaml_schema_value_t *mapping_schema,
|
||||
uint8_t * const data)
|
||||
{
|
||||
const cyaml_schema_field_t *schema = mapping_schema->mapping.fields;
|
||||
|
||||
while (schema->key != NULL) {
|
||||
uint64_t count = 0;
|
||||
cyaml__log(cfg, CYAML_LOG_DEBUG,
|
||||
"Free: Freeing key: %s (at offset: %u)\n",
|
||||
schema->key, (unsigned)schema->data_offset);
|
||||
if (schema->value.type == CYAML_SEQUENCE) {
|
||||
cyaml_err_t err;
|
||||
count = cyaml_data_read(schema->count_size,
|
||||
data + schema->count_offset, &err);
|
||||
if (err != CYAML_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
cyaml__free_value(cfg, &schema->value,
|
||||
data + schema->data_offset, count);
|
||||
schema++;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is documented at the forward declaration above. */
|
||||
static void cyaml__free_value(
|
||||
const cyaml_config_t *cfg,
|
||||
const cyaml_schema_value_t *schema,
|
||||
uint8_t * data,
|
||||
uint64_t count)
|
||||
{
|
||||
if (schema->flags & CYAML_FLAG_POINTER) {
|
||||
data = cyaml_data_read_pointer(data);
|
||||
if (data == NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (schema->type == CYAML_MAPPING) {
|
||||
cyaml__free_mapping(cfg, schema, data);
|
||||
} else if (schema->type == CYAML_SEQUENCE ||
|
||||
schema->type == CYAML_SEQUENCE_FIXED) {
|
||||
if (schema->type == CYAML_SEQUENCE_FIXED) {
|
||||
count = schema->sequence.max;
|
||||
}
|
||||
cyaml__free_sequence(cfg, schema, data, count);
|
||||
}
|
||||
|
||||
if (schema->flags & CYAML_FLAG_POINTER) {
|
||||
cyaml__log(cfg, CYAML_LOG_DEBUG, "Free: Freeing: %p\n", data);
|
||||
cyaml__free(cfg, data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Exported function, documented in include/cyaml/cyaml.h */
|
||||
cyaml_err_t cyaml_free(
|
||||
const cyaml_config_t *config,
|
||||
const cyaml_schema_value_t *schema,
|
||||
cyaml_data_t *data,
|
||||
unsigned seq_count)
|
||||
{
|
||||
if (config == NULL) {
|
||||
return CYAML_ERR_BAD_PARAM_NULL_CONFIG;
|
||||
}
|
||||
if (config->mem_fn == NULL) {
|
||||
return CYAML_ERR_BAD_CONFIG_NULL_MEMFN;
|
||||
}
|
||||
if (schema == NULL) {
|
||||
return CYAML_ERR_BAD_PARAM_NULL_SCHEMA;
|
||||
}
|
||||
cyaml__log(config, CYAML_LOG_DEBUG, "Free: Top level data: %p\n", data);
|
||||
cyaml__free_value(config, schema, (void *)&data, seq_count);
|
||||
return CYAML_OK;
|
||||
}
|
2751
libs/libcyaml/src/load.c
Normal file
2751
libs/libcyaml/src/load.c
Normal file
File diff suppressed because it is too large
Load Diff
35
libs/libcyaml/src/mem.c
Normal file
35
libs/libcyaml/src/mem.c
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2018 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief CYAML memory allocation handling.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mem.h"
|
||||
|
||||
/** Macro to squash unused variable compiler warnings. */
|
||||
#define CYAML_UNUSED(_x) ((void)(_x))
|
||||
|
||||
/* Exported function, documented in include/cyaml/cyaml.h */
|
||||
void * cyaml_mem(
|
||||
void *ctx,
|
||||
void *ptr,
|
||||
size_t size)
|
||||
{
|
||||
CYAML_UNUSED(ctx);
|
||||
|
||||
if (size == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return realloc(ptr, size);
|
||||
}
|
109
libs/libcyaml/src/mem.h
Normal file
109
libs/libcyaml/src/mem.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2018-2020 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief CYAML memory allocation handling.
|
||||
*/
|
||||
|
||||
#ifndef CYAML_MEM_H
|
||||
#define CYAML_MEM_H
|
||||
|
||||
#include "cyaml/cyaml.h"
|
||||
|
||||
/**
|
||||
* Helper for freeing using the client's choice of allocator routine.
|
||||
*
|
||||
* \param[in] config The CYAML client config.
|
||||
* \param[in] ptr Pointer to allocation to free.
|
||||
*/
|
||||
static inline void cyaml__free(
|
||||
const cyaml_config_t *config,
|
||||
void *ptr)
|
||||
{
|
||||
config->mem_fn(config->mem_ctx, ptr, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for new allocations using the client's choice of allocator routine.
|
||||
*
|
||||
* \note On failure, any existing allocation is still owned by the caller, and
|
||||
* they are responsible for freeing it.
|
||||
*
|
||||
* \param[in] config The CYAML client config.
|
||||
* \param[in] ptr The existing allocation or NULL.
|
||||
* \param[in] current_size Size of the current allocation. (Only needed if
|
||||
* `clean != false`).
|
||||
* \param[in] new_size The number of bytes to resize allocation to.
|
||||
* \param[in] clean Only applies if `new_size > current_size`.
|
||||
* If `false`, the new memory is uninitialised,
|
||||
* if `true`, the new memory is initialised to zero.
|
||||
* \return Pointer to allocation on success, or `NULL` on failure.
|
||||
*/
|
||||
static inline void * cyaml__realloc(
|
||||
const cyaml_config_t *config,
|
||||
void *ptr,
|
||||
size_t current_size,
|
||||
size_t new_size,
|
||||
bool clean)
|
||||
{
|
||||
uint8_t *temp = config->mem_fn(config->mem_ctx, ptr, new_size);
|
||||
if (temp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (clean && (new_size > current_size)) {
|
||||
memset(temp + current_size, 0, new_size - current_size);
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for new allocations using the client's choice of allocator routine.
|
||||
*
|
||||
* \param[in] config The CYAML client config.
|
||||
* \param[in] size The number of bytes to allocate.
|
||||
* \param[in] clean If `false`, the memory is uninitialised, if `true`,
|
||||
* the memory is initialised to zero.
|
||||
* \return Pointer to allocation on success, or `NULL` on failure.
|
||||
*/
|
||||
static inline void * cyaml__alloc(
|
||||
const cyaml_config_t *config,
|
||||
size_t size,
|
||||
bool clean)
|
||||
{
|
||||
return cyaml__realloc(config, NULL, 0, size, clean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for string duplication using the client's choice of allocator routine.
|
||||
*
|
||||
* \param[in] config The CYAML client config.
|
||||
* \param[in] str The string to duplicate.
|
||||
* \param[in] len_out If non-NULL, updated to length of string on success.
|
||||
* \return Pointer to new string on success, or `NULL` on failure.
|
||||
*/
|
||||
static inline char * cyaml__strdup(
|
||||
const cyaml_config_t *config,
|
||||
const char *str,
|
||||
size_t *len_out)
|
||||
{
|
||||
size_t len = strlen(str) + 1;
|
||||
char *dup = cyaml__alloc(config, len, false);
|
||||
if (dup == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(dup, str, len);
|
||||
|
||||
if (len_out != NULL) {
|
||||
*len_out = len - 1;
|
||||
}
|
||||
return dup;
|
||||
}
|
||||
|
||||
#endif
|
1516
libs/libcyaml/src/save.c
Normal file
1516
libs/libcyaml/src/save.c
Normal file
File diff suppressed because it is too large
Load Diff
259
libs/libcyaml/src/utf8.c
Normal file
259
libs/libcyaml/src/utf8.c
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2018-2019 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief CYAML functions for handling utf8 text.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "utf8.h"
|
||||
|
||||
/**
|
||||
* Get expected byte-length of UTF8 character.
|
||||
*
|
||||
* Finds the number of bytes expected for the UTF8 sequence starting with
|
||||
* the given byte.
|
||||
*
|
||||
* \param[in] b First byte of UTF8 sequence.
|
||||
* \return the byte width of the character or 0 if invalid.
|
||||
*/
|
||||
static inline unsigned cyaml_utf8_char_len(uint8_t b)
|
||||
{
|
||||
if (!(b & 0x80)) {
|
||||
return 1;
|
||||
} else switch (b >> 3) {
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1a:
|
||||
case 0x1b:
|
||||
return 2;
|
||||
case 0x1c:
|
||||
case 0x1d:
|
||||
return 3;
|
||||
case 0x1e:
|
||||
return 4;
|
||||
}
|
||||
return 0; /* Invalid */
|
||||
}
|
||||
|
||||
/* Exported function, documented in utf8.h. */
|
||||
unsigned cyaml_utf8_get_codepoint(
|
||||
const uint8_t *s,
|
||||
unsigned *len)
|
||||
{
|
||||
unsigned c = 0;
|
||||
bool sf = false;
|
||||
|
||||
if (*len == 1) {
|
||||
return s[0];
|
||||
} else if ((*len > 1) && (*len <= 4)) {
|
||||
/* Compose first byte into codepoint. */
|
||||
c |= (s[0] & ((1u << (7 - *len)) - 1u)) << ((*len - 1) * 6);
|
||||
|
||||
/* Handle continuation bytes. */
|
||||
for (unsigned i = 1; i < *len; i++) {
|
||||
/* Check continuation byte begins with 0b10xxxxxx. */
|
||||
if ((s[i] & 0xc0) != 0x80) {
|
||||
/* Need to shorten length so we don't consume
|
||||
* this byte with this invalid character. */
|
||||
*len -= i;
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
/* Compose continuation byte into codepoint. */
|
||||
c |= (0x3fu & s[i]) << ((*len - i - 1) * 6);
|
||||
}
|
||||
}
|
||||
|
||||
/* Non-shortest forms are forbidden.
|
||||
*
|
||||
* The following table shows the bits available for each
|
||||
* byte sequence length, as well as the bit-delta to the
|
||||
* shorter sequence.
|
||||
*
|
||||
* | Bytes | Bits | Bit delta | Data |
|
||||
* | ----- | ---- | --------- | ----------------------- |
|
||||
* | 1 | 7 | N/A | 0xxxxxxx |
|
||||
* | 2 | 11 | 4 | 110xxxxx + 10xxxxxx x 1 |
|
||||
* | 3 | 16 | 5 | 1110xxxx + 10xxxxxx x 2 |
|
||||
* | 4 | 21 | 5 | 11110xxx + 10xxxxxx x 3 |
|
||||
*
|
||||
* So here we check that the top "bit-delta" bits are not all
|
||||
* clear for the byte length,
|
||||
*/
|
||||
switch (*len) {
|
||||
case 2: sf = (c & (((1 << 4) - 1) << (11 - 4))) != 0; break;
|
||||
case 3: sf = (c & (((1 << 5) - 1) << (16 - 5))) != 0; break;
|
||||
case 4: sf = (c & (((1 << 5) - 1) << (21 - 5))) != 0; break;
|
||||
default: goto invalid;
|
||||
}
|
||||
|
||||
if (!sf) {
|
||||
/* Codepoint representation was not shortest-form. */
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
return c;
|
||||
|
||||
invalid:
|
||||
return 0xfffd; /* REPLACEMENT CHARACTER */
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Unicode codepoint to lower case.
|
||||
*
|
||||
* \note This only handles some of the Unicode blocks.
|
||||
* (Currently the Latin ones.)
|
||||
*
|
||||
* \param[in] c Codepoint to convert to lower-case, if applicable.
|
||||
* \return the lower-cased codepoint.
|
||||
*/
|
||||
static unsigned cyaml_utf8_to_lower(unsigned c)
|
||||
{
|
||||
if (((c >= 0x0041) && (c <= 0x005a)) /* Basic Latin */ ||
|
||||
((c >= 0x00c0) && (c <= 0x00d6)) /* Latin-1 Supplement */ ||
|
||||
((c >= 0x00d8) && (c <= 0x00de)) /* Latin-1 Supplement */ ) {
|
||||
return c + 32u;
|
||||
|
||||
} else if (((c >= 0x0100) && (c <= 0x012f)) /* Latin Extended-A */ ||
|
||||
((c >= 0x0132) && (c <= 0x0137)) /* Latin Extended-A */ ||
|
||||
((c >= 0x014a) && (c <= 0x0177)) /* Latin Extended-A */ ||
|
||||
((c >= 0x0182) && (c <= 0x0185)) /* Latin Extended-B */ ||
|
||||
((c >= 0x01a0) && (c <= 0x01a5)) /* Latin Extended-B */ ||
|
||||
((c >= 0x01de) && (c <= 0x01ef)) /* Latin Extended-B */ ||
|
||||
((c >= 0x01f8) && (c <= 0x021f)) /* Latin Extended-B */ ||
|
||||
((c >= 0x0222) && (c <= 0x0233)) /* Latin Extended-B */ ||
|
||||
((c >= 0x0246) && (c <= 0x024f)) /* Latin Extended-B */ ) {
|
||||
return c & ~0x1u;
|
||||
|
||||
} else if (((c >= 0x0139) && (c <= 0x0148) /* Latin Extended-A */) ||
|
||||
((c >= 0x0179) && (c <= 0x017e) /* Latin Extended-A */) ||
|
||||
((c >= 0x01b3) && (c <= 0x01b6) /* Latin Extended-B */) ||
|
||||
((c >= 0x01cd) && (c <= 0x01dc) /* Latin Extended-B */)) {
|
||||
return (c + 1) & ~0x1u;
|
||||
|
||||
} else switch (c) {
|
||||
case 0x0178: return 0x00ff; /* Latin Extended-A */
|
||||
case 0x0187: return 0x0188; /* Latin Extended-B */
|
||||
case 0x018b: return 0x018c; /* Latin Extended-B */
|
||||
case 0x018e: return 0x01dd; /* Latin Extended-B */
|
||||
case 0x0191: return 0x0192; /* Latin Extended-B */
|
||||
case 0x0198: return 0x0199; /* Latin Extended-B */
|
||||
case 0x01a7: return 0x01a8; /* Latin Extended-B */
|
||||
case 0x01ac: return 0x01ad; /* Latin Extended-B */
|
||||
case 0x01af: return 0x01b0; /* Latin Extended-B */
|
||||
case 0x01b7: return 0x0292; /* Latin Extended-B */
|
||||
case 0x01b8: return 0x01b9; /* Latin Extended-B */
|
||||
case 0x01bc: return 0x01bd; /* Latin Extended-B */
|
||||
case 0x01c4: return 0x01c6; /* Latin Extended-B */
|
||||
case 0x01c5: return 0x01c6; /* Latin Extended-B */
|
||||
case 0x01c7: return 0x01c9; /* Latin Extended-B */
|
||||
case 0x01c8: return 0x01c9; /* Latin Extended-B */
|
||||
case 0x01ca: return 0x01cc; /* Latin Extended-B */
|
||||
case 0x01cb: return 0x01cc; /* Latin Extended-B */
|
||||
case 0x01f1: return 0x01f3; /* Latin Extended-B */
|
||||
case 0x01f2: return 0x01f3; /* Latin Extended-B */
|
||||
case 0x01f4: return 0x01f5; /* Latin Extended-B */
|
||||
case 0x01f7: return 0x01bf; /* Latin Extended-B */
|
||||
case 0x0220: return 0x019e; /* Latin Extended-B */
|
||||
case 0x023b: return 0x023c; /* Latin Extended-B */
|
||||
case 0x023d: return 0x019a; /* Latin Extended-B */
|
||||
case 0x0241: return 0x0242; /* Latin Extended-B */
|
||||
case 0x0243: return 0x0180; /* Latin Extended-B */
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the difference between two codepoints.
|
||||
*
|
||||
* \param a First codepoint.
|
||||
* \param b Second codepoint.
|
||||
* \return the difference.
|
||||
*/
|
||||
static inline int cyaml_utf8_difference(unsigned a, unsigned b)
|
||||
{
|
||||
return (((int)a) - ((int)b));
|
||||
}
|
||||
|
||||
/* Exported function, documented in utf8.h. */
|
||||
int cyaml_utf8_casecmp(
|
||||
const void * const str1,
|
||||
const void * const str2)
|
||||
{
|
||||
const uint8_t *s1 = str1;
|
||||
const uint8_t *s2 = str2;
|
||||
|
||||
while (true) {
|
||||
unsigned len1;
|
||||
unsigned len2;
|
||||
unsigned cmp1;
|
||||
unsigned cmp2;
|
||||
|
||||
/* Check for end of strings. */
|
||||
if ((*s1 == 0) && (*s2 == 0)) {
|
||||
return 0; /* Both strings ended; match. */
|
||||
|
||||
} else if (*s1 == 0) {
|
||||
return 1; /* String 1 has ended. */
|
||||
|
||||
} else if (*s2 == 0) {
|
||||
return -1;/* String 2 has ended. */
|
||||
}
|
||||
|
||||
/* Get byte lengths of these characters. */
|
||||
len1 = cyaml_utf8_char_len(*s1);
|
||||
len2 = cyaml_utf8_char_len(*s2);
|
||||
|
||||
/* Compare values. */
|
||||
if ((len1 == 1) && (len2 == 1)) {
|
||||
/* Common case: Both strings have ASCII values. */
|
||||
if (*s1 != *s2) {
|
||||
/* They're different; need to lower case. */
|
||||
cmp1 = ((*s1 >= 'A') && (*s1 <= 'Z')) ?
|
||||
(*s1 + 32u) : *s1;
|
||||
cmp2 = ((*s2 >= 'A') && (*s2 <= 'Z')) ?
|
||||
(*s2 + 32u) : *s2;
|
||||
if (cmp1 != cmp2) {
|
||||
return cyaml_utf8_difference(
|
||||
cmp1, cmp2);
|
||||
}
|
||||
}
|
||||
} else if ((len1 != 0) && (len2 != 0)) {
|
||||
/* Neither string has invalid sequence;
|
||||
* convert to UCS4 for comparison. */
|
||||
cmp1 = cyaml_utf8_get_codepoint(s1, &len1);
|
||||
cmp2 = cyaml_utf8_get_codepoint(s2, &len2);
|
||||
|
||||
if (cmp1 != cmp2) {
|
||||
/* They're different; need to lower case. */
|
||||
cmp1 = cyaml_utf8_to_lower(cmp1);
|
||||
cmp2 = cyaml_utf8_to_lower(cmp2);
|
||||
if (cmp1 != cmp2) {
|
||||
return cyaml_utf8_difference(
|
||||
cmp1, cmp2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (len1 | len2) {
|
||||
/* One of the strings has invalid sequence. */
|
||||
return cyaml_utf8_difference(len1, len2);
|
||||
} else {
|
||||
/* Both strings have an invalid sequence. */
|
||||
len1 = len2 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance each string by their current character length. */
|
||||
s1 += len1;
|
||||
s2 += len2;
|
||||
}
|
||||
}
|
46
libs/libcyaml/src/utf8.h
Normal file
46
libs/libcyaml/src/utf8.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2018 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief CYAML functions for handling utf8 text.
|
||||
*/
|
||||
|
||||
#ifndef CYAML_UTF8_H
|
||||
#define CYAML_UTF8_H
|
||||
|
||||
/**
|
||||
* Get a codepoint from the input string.
|
||||
*
|
||||
* Caller must provide the expected length given the first input byte.
|
||||
*
|
||||
* If a multi-byte character contains an invalid continuation byte, the
|
||||
* character length will be updated on exit to the number of bytes consumed,
|
||||
* and the replacement character, U+FFFD will be returned.
|
||||
*
|
||||
* \param[in] s String to read first codepoint from.
|
||||
* \param[in,out] len Expected length of first character, updated on exit.
|
||||
* \return The codepoint or `0xfffd` if character is invalid.
|
||||
*/
|
||||
unsigned cyaml_utf8_get_codepoint(
|
||||
const uint8_t *s,
|
||||
unsigned *len);
|
||||
|
||||
/**
|
||||
* Case insensitive comparason.
|
||||
*
|
||||
* \note This has some limitations and only performs case insensitive
|
||||
* comparason over some sectons of unicode.
|
||||
*
|
||||
* \param[in] str1 First string to be compared.
|
||||
* \param[in] str2 Second string to be compared.
|
||||
* \return 0 if and only if strings are equal.
|
||||
*/
|
||||
int cyaml_utf8_casecmp(
|
||||
const void * const str1,
|
||||
const void * const str2);
|
||||
|
||||
#endif
|
122
libs/libcyaml/src/util.c
Normal file
122
libs/libcyaml/src/util.c
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2017-2019 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief Utility functions.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/** Flag that indicates a release in \ref cyaml_version. */
|
||||
#define CYAML_RELEASE_FLAG (1u << 31)
|
||||
|
||||
/** Stringification helper macro. */
|
||||
#define CYAML_STR_HELPER(_x) #_x
|
||||
|
||||
/** Stringification macro. */
|
||||
#define CYAML_STR(_x) CYAML_STR_HELPER(_x)
|
||||
|
||||
/* Version depends on whether we're a development build. */
|
||||
#if VERSION_DEVEL
|
||||
/** Version string is composed from components in Makefile. */
|
||||
#define CYAML_VERSION_STR \
|
||||
CYAML_STR(VERSION_MAJOR) "." \
|
||||
CYAML_STR(VERSION_MINOR) "." \
|
||||
CYAML_STR(VERSION_PATCH) "-DEVEL"
|
||||
|
||||
/* Exported constant, documented in include/cyaml/cyaml.h */
|
||||
const uint32_t cyaml_version =
|
||||
((VERSION_MAJOR << 16) |
|
||||
(VERSION_MINOR << 8) |
|
||||
(VERSION_PATCH << 0));
|
||||
#else
|
||||
/** Version string is composed from components in Makefile. */
|
||||
#define CYAML_VERSION_STR \
|
||||
CYAML_STR(VERSION_MAJOR) "." \
|
||||
CYAML_STR(VERSION_MINOR) "." \
|
||||
CYAML_STR(VERSION_PATCH)
|
||||
|
||||
/* Exported constant, documented in include/cyaml/cyaml.h */
|
||||
const uint32_t cyaml_version =
|
||||
((VERSION_MAJOR << 16) |
|
||||
(VERSION_MINOR << 8) |
|
||||
(VERSION_PATCH << 0) |
|
||||
CYAML_RELEASE_FLAG);
|
||||
#endif
|
||||
|
||||
/* Exported constant, documented in include/cyaml/cyaml.h */
|
||||
const char *cyaml_version_str = CYAML_VERSION_STR;
|
||||
|
||||
/* Exported function, documented in include/cyaml/cyaml.h */
|
||||
void cyaml_log(
|
||||
cyaml_log_t level,
|
||||
void *ctx,
|
||||
const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
static const char * const strings[] = {
|
||||
[CYAML_LOG_DEBUG] = "DEBUG",
|
||||
[CYAML_LOG_INFO] = "INFO",
|
||||
[CYAML_LOG_NOTICE] = "NOTICE",
|
||||
[CYAML_LOG_WARNING] = "WARNING",
|
||||
[CYAML_LOG_ERROR] = "ERROR",
|
||||
};
|
||||
|
||||
CYAML_UNUSED(ctx);
|
||||
|
||||
fprintf(stderr, "libcyaml: %7.7s: ", strings[level]);
|
||||
vfprintf(stderr, fmt, args);
|
||||
}
|
||||
|
||||
/* Exported function, documented in include/cyaml/cyaml.h */
|
||||
const char * cyaml_strerror(
|
||||
cyaml_err_t err)
|
||||
{
|
||||
static const char * const strings[CYAML_ERR__COUNT] = {
|
||||
[CYAML_OK] = "Success",
|
||||
[CYAML_ERR_OOM] = "Memory allocation failed",
|
||||
[CYAML_ERR_ALIAS] = "YAML alias unsupported",
|
||||
[CYAML_ERR_FILE_OPEN] = "Could not open file",
|
||||
[CYAML_ERR_INVALID_KEY] = "Invalid key",
|
||||
[CYAML_ERR_INVALID_VALUE] = "Invalid value",
|
||||
[CYAML_ERR_INVALID_ALIAS] = "No anchor found for alias",
|
||||
[CYAML_ERR_INTERNAL_ERROR] = "Internal error",
|
||||
[CYAML_ERR_UNEXPECTED_EVENT] = "Unexpected event",
|
||||
[CYAML_ERR_STRING_LENGTH_MIN] = "String length too short",
|
||||
[CYAML_ERR_STRING_LENGTH_MAX] = "String length too long",
|
||||
[CYAML_ERR_INVALID_DATA_SIZE] = "Data size must be 0 < X <= 8 bytes",
|
||||
[CYAML_ERR_TOP_LEVEL_NON_PTR] = "Top-level schema value must be pointer",
|
||||
[CYAML_ERR_BAD_TYPE_IN_SCHEMA] = "Schema contains invalid type",
|
||||
[CYAML_ERR_BAD_MIN_MAX_SCHEMA] = "Bad schema: min exceeds max",
|
||||
[CYAML_ERR_BAD_PARAM_SEQ_COUNT] = "Bad parameter: seq_count",
|
||||
[CYAML_ERR_BAD_PARAM_NULL_DATA] = "Bad parameter: NULL data",
|
||||
[CYAML_ERR_BAD_BITVAL_IN_SCHEMA] = "Bit value beyond bitfield size",
|
||||
[CYAML_ERR_SEQUENCE_ENTRIES_MIN] = "Sequence with too few entries",
|
||||
[CYAML_ERR_SEQUENCE_ENTRIES_MAX] = "Sequence with too many entries",
|
||||
[CYAML_ERR_SEQUENCE_FIXED_COUNT] = "Sequence fixed has unequal min max",
|
||||
[CYAML_ERR_SEQUENCE_IN_SEQUENCE] = "Non-fixed sequence in sequence",
|
||||
[CYAML_ERR_MAPPING_FIELD_MISSING] = "Missing required mapping field",
|
||||
[CYAML_ERR_BAD_CONFIG_NULL_MEMFN] = "Bad config: NULL mem function",
|
||||
[CYAML_ERR_BAD_PARAM_NULL_CONFIG] = "Bad parameter: NULL config",
|
||||
[CYAML_ERR_BAD_PARAM_NULL_SCHEMA] = "Bad parameter: NULL schema",
|
||||
[CYAML_ERR_LIBYAML_EMITTER_INIT] = "libyaml emitter init failed",
|
||||
[CYAML_ERR_LIBYAML_PARSER_INIT] = "libyaml parser init failed",
|
||||
[CYAML_ERR_LIBYAML_EVENT_INIT] = "libyaml event init failed",
|
||||
[CYAML_ERR_LIBYAML_EMITTER] = "libyaml emitter error",
|
||||
[CYAML_ERR_LIBYAML_PARSER] = "libyaml parser error",
|
||||
};
|
||||
if ((unsigned)err >= CYAML_ERR__COUNT) {
|
||||
return "Invalid error code";
|
||||
}
|
||||
assert(strings[err] != NULL);
|
||||
return strings[err];
|
||||
}
|
200
libs/libcyaml/src/util.h
Normal file
200
libs/libcyaml/src/util.h
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief CYAML common utility functions.
|
||||
*/
|
||||
|
||||
#ifndef CYAML_UTIL_H
|
||||
#define CYAML_UTIL_H
|
||||
|
||||
#include "cyaml/cyaml.h"
|
||||
#include "utf8.h"
|
||||
|
||||
/** Macro to squash unused variable compiler warnings. */
|
||||
#define CYAML_UNUSED(_x) ((void)(_x))
|
||||
|
||||
/**
|
||||
* Check whether the host is little endian.
|
||||
*
|
||||
* Checks whether least significant bit is in the first byte of a `uint16_t`.
|
||||
*
|
||||
* \return true if host is little endian.
|
||||
*/
|
||||
static inline bool cyaml__host_is_little_endian(void)
|
||||
{
|
||||
const uint16_t test = 1;
|
||||
|
||||
return ((const uint8_t *) &test)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the host is big endian.
|
||||
*
|
||||
* \return true if host is big endian.
|
||||
*/
|
||||
static inline bool cyaml__host_is_big_endian(void)
|
||||
{
|
||||
return !cyaml__host_is_little_endian();
|
||||
}
|
||||
|
||||
/** CYAML bitfield type. */
|
||||
typedef uint32_t cyaml_bitfield_t;
|
||||
|
||||
/** Number of bits in \ref cyaml_bitfield_t. */
|
||||
#define CYAML_BITFIELD_BITS (sizeof(cyaml_bitfield_t) * CHAR_BIT)
|
||||
|
||||
/** CYAML state machine states. */
|
||||
enum cyaml_state_e {
|
||||
CYAML_STATE_START, /**< Initial state. */
|
||||
CYAML_STATE_IN_STREAM, /**< In a stream. */
|
||||
CYAML_STATE_IN_DOC, /**< In a document. */
|
||||
CYAML_STATE_IN_MAP_KEY, /**< In a mapping. */
|
||||
CYAML_STATE_IN_MAP_VALUE, /**< In a mapping. */
|
||||
CYAML_STATE_IN_SEQUENCE, /**< In a sequence. */
|
||||
CYAML_STATE__COUNT, /**< Count of states, **not a valid
|
||||
state itself**. */
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a CYAML state into a human readable string.
|
||||
*
|
||||
* \param[in] state The state to convert.
|
||||
* \return String representing state.
|
||||
*/
|
||||
static inline const char * cyaml__state_to_str(enum cyaml_state_e state)
|
||||
{
|
||||
static const char * const strings[CYAML_STATE__COUNT] = {
|
||||
[CYAML_STATE_START] = "start",
|
||||
[CYAML_STATE_IN_STREAM] = "in stream",
|
||||
[CYAML_STATE_IN_DOC] = "in doc",
|
||||
[CYAML_STATE_IN_MAP_KEY] = "in mapping (key)",
|
||||
[CYAML_STATE_IN_MAP_VALUE] = "in mapping (value)",
|
||||
[CYAML_STATE_IN_SEQUENCE] = "in sequence",
|
||||
};
|
||||
if ((unsigned)state >= CYAML_STATE__COUNT) {
|
||||
return "<invalid>";
|
||||
}
|
||||
return strings[state];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a CYAML type into a human readable string.
|
||||
*
|
||||
* \param[in] type The state to convert.
|
||||
* \return String representing state.
|
||||
*/
|
||||
static inline const char * cyaml__type_to_str(cyaml_type_e type)
|
||||
{
|
||||
static const char * const strings[CYAML__TYPE_COUNT] = {
|
||||
[CYAML_INT] = "INT",
|
||||
[CYAML_UINT] = "UINT",
|
||||
[CYAML_BOOL] = "BOOL",
|
||||
[CYAML_ENUM] = "ENUM",
|
||||
[CYAML_FLAGS] = "FLAGS",
|
||||
[CYAML_FLOAT] = "FLOAT",
|
||||
[CYAML_STRING] = "STRING",
|
||||
[CYAML_MAPPING] = "MAPPING",
|
||||
[CYAML_BITFIELD] = "BITFIELD",
|
||||
[CYAML_SEQUENCE] = "SEQUENCE",
|
||||
[CYAML_SEQUENCE_FIXED] = "SEQUENCE_FIXED",
|
||||
[CYAML_IGNORE] = "IGNORE",
|
||||
};
|
||||
if ((unsigned)type >= CYAML__TYPE_COUNT) {
|
||||
return "<invalid>";
|
||||
}
|
||||
return strings[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Log to client's logging function, if provided.
|
||||
*
|
||||
* \param[in] cfg CYAML client config structure.
|
||||
* \param[in] level Log level of message to log.
|
||||
* \param[in] fmt Format string for message to log.
|
||||
* \param[in] ... Additional arguments used by fmt.
|
||||
*/
|
||||
static inline void cyaml__log(
|
||||
const cyaml_config_t *cfg,
|
||||
cyaml_log_t level,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
if (level >= cfg->log_level && cfg->log_fn != NULL) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
cfg->log_fn(level, cfg->log_ctx, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if comparason should be case sensitive.
|
||||
*
|
||||
* As described in the API, schema flags take priority over config flags.
|
||||
*
|
||||
* \param[in] config Client's CYAML configuration structure.
|
||||
* \param[in] schema The CYAML schema for the value to be compared.
|
||||
* \return Whether to use case-sensitive comparason.
|
||||
*/
|
||||
static inline bool cyaml__is_case_sensitive(
|
||||
const cyaml_config_t *config,
|
||||
const cyaml_schema_value_t *schema)
|
||||
{
|
||||
if (schema->flags & CYAML_FLAG_CASE_INSENSITIVE) {
|
||||
return false;
|
||||
|
||||
} else if (schema->flags & CYAML_FLAG_CASE_SENSITIVE) {
|
||||
return true;
|
||||
|
||||
} else if (config->flags & CYAML_CFG_CASE_INSENSITIVE) {
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two strings.
|
||||
*
|
||||
* Depending on the client's configuration, and the value's schema,
|
||||
* this will do either a case-sensitive or case-insensitive comparason.
|
||||
*
|
||||
* \param[in] config Client's CYAML configuration structure.
|
||||
* \param[in] schema The CYAML schema for the value to be compared.
|
||||
* \param[in] str1 First string to be compared.
|
||||
* \param[in] str2 Second string to be compared.
|
||||
* \return 0 if and only if strings are equal.
|
||||
*/
|
||||
static inline int cyaml__strcmp(
|
||||
const cyaml_config_t *config,
|
||||
const cyaml_schema_value_t *schema,
|
||||
const void * const str1,
|
||||
const void * const str2)
|
||||
{
|
||||
if (cyaml__is_case_sensitive(config, schema)) {
|
||||
return strcmp(str1, str2);
|
||||
}
|
||||
|
||||
return cyaml_utf8_casecmp(str1, str2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check of all the bits of a mask are set in a cyaml value flag word.
|
||||
*
|
||||
* \param[in] flags The value flags to test.
|
||||
* \param[in] mask Mask of the bits to test for in flags.
|
||||
* \return true if all bits of mask are set in flags.
|
||||
*/
|
||||
static inline bool cyaml__flag_check_all(
|
||||
enum cyaml_flag flags,
|
||||
enum cyaml_flag mask)
|
||||
{
|
||||
return ((flags & mask) == mask);
|
||||
}
|
||||
|
||||
#endif
|
19
libs/libcyaml/test/data/basic.yaml
Normal file
19
libs/libcyaml/test/data/basic.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
animals:
|
||||
- kind: cat
|
||||
sounds:
|
||||
- meow
|
||||
- purr
|
||||
- kind: hippo
|
||||
sounds:
|
||||
- wheeze
|
||||
- grunt
|
||||
- roar
|
||||
- kind: snake
|
||||
sounds:
|
||||
- hiss
|
||||
cakes:
|
||||
- salted caramel cake
|
||||
- lemon drizzle cake
|
||||
- victoria sponge
|
||||
- carrot cake
|
||||
- yule log
|
7371
libs/libcyaml/test/units/errs.c
Normal file
7371
libs/libcyaml/test/units/errs.c
Normal file
File diff suppressed because it is too large
Load Diff
448
libs/libcyaml/test/units/file.c
Normal file
448
libs/libcyaml/test/units/file.c
Normal file
@ -0,0 +1,448 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2018-2021 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cyaml/cyaml.h>
|
||||
|
||||
#include "ttest.h"
|
||||
#include "test.h"
|
||||
|
||||
/**
|
||||
* Unit test context data.
|
||||
*/
|
||||
typedef struct test_data {
|
||||
cyaml_data_t **data;
|
||||
unsigned *seq_count;
|
||||
const struct cyaml_config *config;
|
||||
const struct cyaml_schema_value *schema;
|
||||
} test_data_t;
|
||||
|
||||
/**
|
||||
* Common clean up function to free data loaded by tests.
|
||||
*
|
||||
* \param[in] data The unit test context data.
|
||||
*/
|
||||
static void cyaml_cleanup(void *data)
|
||||
{
|
||||
struct test_data *td = data;
|
||||
unsigned seq_count = 0;
|
||||
|
||||
if (td->seq_count != NULL) {
|
||||
seq_count = *(td->seq_count);
|
||||
}
|
||||
|
||||
if (td->data != NULL) {
|
||||
cyaml_free(td->config, td->schema, *(td->data), seq_count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading a non-existent file.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_file_load_bad_path(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
struct target_struct {
|
||||
char *cakes;
|
||||
} *data_tgt = NULL;
|
||||
static const struct cyaml_schema_field mapping_schema[] = {
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value top_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct target_struct, mapping_schema),
|
||||
};
|
||||
test_data_t td = {
|
||||
.data = (cyaml_data_t **) &data_tgt,
|
||||
.config = config,
|
||||
.schema = &top_schema,
|
||||
};
|
||||
cyaml_err_t err;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = cyaml_load_file("/cyaml/path/shouldn't/exist.yaml",
|
||||
config, &top_schema, (cyaml_data_t **) &data_tgt, NULL);
|
||||
if (err != CYAML_ERR_FILE_OPEN) {
|
||||
return ttest_fail(&tc, cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading a non-existent file.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_file_save_bad_path(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
struct target_struct {
|
||||
char *cakes;
|
||||
} *data = NULL;
|
||||
static const struct cyaml_schema_field mapping_schema[] = {
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value top_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct target_struct, mapping_schema),
|
||||
};
|
||||
test_data_t td = {
|
||||
.data = NULL,
|
||||
.config = config,
|
||||
.schema = &top_schema,
|
||||
};
|
||||
cyaml_err_t err;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = cyaml_save_file("/cyaml/path/shouldn't/exist.yaml",
|
||||
config, &top_schema, data, 0);
|
||||
if (err != CYAML_ERR_FILE_OPEN) {
|
||||
return ttest_fail(&tc, cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading the basic YAML file.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_file_load_basic(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
struct animal {
|
||||
char *kind;
|
||||
char **sounds;
|
||||
unsigned sounds_count;
|
||||
};
|
||||
struct target_struct {
|
||||
struct animal *animals;
|
||||
unsigned animals_count;
|
||||
char **cakes;
|
||||
unsigned cakes_count;
|
||||
} *data_tgt = NULL;
|
||||
static const struct cyaml_schema_value sounds_entry_schema = {
|
||||
CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
|
||||
};
|
||||
static const struct cyaml_schema_field animal_mapping_schema[] = {
|
||||
CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER,
|
||||
struct animal, kind, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER,
|
||||
struct animal, sounds,
|
||||
&sounds_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value animals_entry_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
|
||||
struct animal, animal_mapping_schema),
|
||||
};
|
||||
static const struct cyaml_schema_value cakes_entry_schema = {
|
||||
CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
|
||||
};
|
||||
static const struct cyaml_schema_field mapping_schema[] = {
|
||||
CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER,
|
||||
struct target_struct, animals,
|
||||
&animals_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER,
|
||||
struct target_struct, cakes,
|
||||
&cakes_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value top_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct target_struct, mapping_schema),
|
||||
};
|
||||
test_data_t td = {
|
||||
.data = (cyaml_data_t **) &data_tgt,
|
||||
.config = config,
|
||||
.schema = &top_schema,
|
||||
};
|
||||
cyaml_err_t err;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = cyaml_load_file("test/data/basic.yaml", config, &top_schema,
|
||||
(cyaml_data_t **) &data_tgt, NULL);
|
||||
if (err != CYAML_OK) {
|
||||
return ttest_fail(&tc, cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading and then saving the basic YAML file.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_file_load_save_basic(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
struct animal {
|
||||
char *kind;
|
||||
char **sounds;
|
||||
unsigned sounds_count;
|
||||
};
|
||||
struct target_struct {
|
||||
struct animal *animals;
|
||||
unsigned animals_count;
|
||||
char **cakes;
|
||||
unsigned cakes_count;
|
||||
} *data_tgt = NULL;
|
||||
static const struct cyaml_schema_value sounds_entry_schema = {
|
||||
CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
|
||||
};
|
||||
static const struct cyaml_schema_field animal_mapping_schema[] = {
|
||||
CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER,
|
||||
struct animal, kind, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER,
|
||||
struct animal, sounds,
|
||||
&sounds_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value animals_entry_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
|
||||
struct animal, animal_mapping_schema),
|
||||
};
|
||||
static const struct cyaml_schema_value cakes_entry_schema = {
|
||||
CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
|
||||
};
|
||||
static const struct cyaml_schema_field mapping_schema[] = {
|
||||
CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER,
|
||||
struct target_struct, animals,
|
||||
&animals_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER,
|
||||
struct target_struct, cakes,
|
||||
&cakes_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value top_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct target_struct, mapping_schema),
|
||||
};
|
||||
test_data_t td = {
|
||||
.data = (cyaml_data_t **) &data_tgt,
|
||||
.config = config,
|
||||
.schema = &top_schema,
|
||||
};
|
||||
cyaml_err_t err;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = cyaml_load_file("test/data/basic.yaml", config, &top_schema,
|
||||
(cyaml_data_t **) &data_tgt, NULL);
|
||||
if (err != CYAML_OK) {
|
||||
return ttest_fail(&tc, cyaml_strerror(err));
|
||||
}
|
||||
|
||||
err = cyaml_save_file("build/load_save.yaml", config, &top_schema,
|
||||
data_tgt, 0);
|
||||
if (err != CYAML_OK) {
|
||||
return ttest_fail(&tc, cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test loading the basic YAML file, with a mismatching schema.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_file_load_basic_invalid(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
struct animal {
|
||||
char *kind;
|
||||
int *sounds;
|
||||
unsigned sounds_count;
|
||||
};
|
||||
struct target_struct {
|
||||
struct animal *animals;
|
||||
unsigned animals_count;
|
||||
char **cakes;
|
||||
unsigned cakes_count;
|
||||
} *data_tgt = NULL;
|
||||
static const struct cyaml_schema_value sounds_entry_schema = {
|
||||
/* The data has a string, but we're expecting int here. */
|
||||
CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int),
|
||||
};
|
||||
static const struct cyaml_schema_field animal_mapping_schema[] = {
|
||||
CYAML_FIELD_STRING_PTR("kind", CYAML_FLAG_POINTER,
|
||||
struct animal, kind, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_SEQUENCE("sounds", CYAML_FLAG_POINTER,
|
||||
struct animal, sounds,
|
||||
&sounds_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value animals_entry_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
|
||||
struct animal, animal_mapping_schema),
|
||||
};
|
||||
static const struct cyaml_schema_value cakes_entry_schema = {
|
||||
CYAML_VALUE_STRING(CYAML_FLAG_POINTER, char, 0, CYAML_UNLIMITED),
|
||||
};
|
||||
static const struct cyaml_schema_field mapping_schema[] = {
|
||||
CYAML_FIELD_SEQUENCE("animals", CYAML_FLAG_POINTER,
|
||||
struct target_struct, animals,
|
||||
&animals_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_SEQUENCE("cakes", CYAML_FLAG_POINTER,
|
||||
struct target_struct, cakes,
|
||||
&cakes_entry_schema, 0, CYAML_UNLIMITED),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value top_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct target_struct, mapping_schema),
|
||||
};
|
||||
test_data_t td = {
|
||||
.data = (cyaml_data_t **) &data_tgt,
|
||||
.config = config,
|
||||
.schema = &top_schema,
|
||||
};
|
||||
cyaml_err_t err;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = cyaml_load_file("test/data/basic.yaml", config, &top_schema,
|
||||
(cyaml_data_t **) &data_tgt, NULL);
|
||||
if (err != CYAML_ERR_INVALID_VALUE) {
|
||||
return ttest_fail(&tc, cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test saving to a file when an erro occurs.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_file_save_basic_invalid(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
static const struct target_struct {
|
||||
int value;
|
||||
} data = {
|
||||
.value = 9,
|
||||
};
|
||||
static const struct cyaml_schema_field mapping_schema[] = {
|
||||
{
|
||||
.key = "key",
|
||||
.value = {
|
||||
.type = CYAML_INT,
|
||||
.flags = CYAML_FLAG_DEFAULT,
|
||||
.data_size = 0,
|
||||
},
|
||||
.data_offset = offsetof(struct target_struct, value),
|
||||
},
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value top_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct target_struct, mapping_schema),
|
||||
};
|
||||
test_data_t td = {
|
||||
.config = config,
|
||||
};
|
||||
cyaml_err_t err;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, cyaml_cleanup, &td, &tc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
err = cyaml_save_file("build/save.yaml", config, &top_schema, &data, 0);
|
||||
if (err != CYAML_ERR_INVALID_DATA_SIZE) {
|
||||
return ttest_fail(&tc, cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the YAML file tests.
|
||||
*
|
||||
* \param[in] rc The ttest report context.
|
||||
* \param[in] log_level CYAML log level.
|
||||
* \param[in] log_fn CYAML logging function, or NULL.
|
||||
* \return true iff all unit tests pass, otherwise false.
|
||||
*/
|
||||
bool file_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn)
|
||||
{
|
||||
bool pass = true;
|
||||
cyaml_config_t config = {
|
||||
.log_fn = log_fn,
|
||||
.mem_fn = cyaml_mem,
|
||||
.log_level = log_level,
|
||||
.flags = CYAML_CFG_DEFAULT,
|
||||
};
|
||||
|
||||
ttest_heading(rc, "File loading tests");
|
||||
|
||||
pass &= test_file_load_basic(rc, &config);
|
||||
pass &= test_file_load_save_basic(rc, &config);
|
||||
|
||||
/* Since we expect loads of error logging for these tests,
|
||||
* suppress log output if required log level is greater
|
||||
* than \ref CYAML_LOG_INFO.
|
||||
*/
|
||||
if (log_level > CYAML_LOG_INFO) {
|
||||
config.log_fn = NULL;
|
||||
}
|
||||
|
||||
pass &= test_file_load_bad_path(rc, &config);
|
||||
pass &= test_file_save_bad_path(rc, &config);
|
||||
pass &= test_file_load_basic_invalid(rc, &config);
|
||||
pass &= test_file_save_basic_invalid(rc, &config);
|
||||
|
||||
return pass;
|
||||
}
|
161
libs/libcyaml/test/units/free.c
Normal file
161
libs/libcyaml/test/units/free.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cyaml/cyaml.h>
|
||||
|
||||
#include "ttest.h"
|
||||
#include "test.h"
|
||||
|
||||
/** Macro to squash unused variable compiler warnings. */
|
||||
#define UNUSED(_x) ((void)(_x))
|
||||
|
||||
/**
|
||||
* Test cyaml_free with NULL data.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_free_null_data(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
cyaml_err_t err;
|
||||
struct target_struct {
|
||||
int test_value_int;
|
||||
};
|
||||
static const struct cyaml_schema_field mapping_schema[] = {
|
||||
CYAML_FIELD_INT("test_int", CYAML_FLAG_DEFAULT,
|
||||
struct target_struct, test_value_int),
|
||||
CYAML_FIELD_END
|
||||
};
|
||||
static const struct cyaml_schema_value top_schema = {
|
||||
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
|
||||
struct target_struct, mapping_schema),
|
||||
};
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
err = cyaml_free(config, &top_schema, NULL, 0);
|
||||
if (err != CYAML_OK) {
|
||||
return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cyaml_free with NULL config.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_free_null_config(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
cyaml_err_t err;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
UNUSED(config);
|
||||
|
||||
err = cyaml_free(NULL, NULL, NULL, 0);
|
||||
if (err != CYAML_ERR_BAD_PARAM_NULL_CONFIG) {
|
||||
return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cyaml_free with NULL memory allocation function.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_free_null_mem_fn(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
cyaml_err_t err;
|
||||
cyaml_config_t cfg = *config;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
cfg.mem_fn = NULL;
|
||||
|
||||
err = cyaml_free(&cfg, NULL, NULL, 0);
|
||||
if (err != CYAML_ERR_BAD_CONFIG_NULL_MEMFN) {
|
||||
return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cyaml_free with NULL schema.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_free_null_schema(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
cyaml_err_t err;
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
err = cyaml_free(config, NULL, NULL, 0);
|
||||
if (err != CYAML_ERR_BAD_PARAM_NULL_SCHEMA) {
|
||||
return ttest_fail(&tc, "Free failed: %s", cyaml_strerror(err));
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the CYAML freeing unit tests.
|
||||
*
|
||||
* \param[in] rc The ttest report context.
|
||||
* \param[in] log_level CYAML log level.
|
||||
* \param[in] log_fn CYAML logging function, or NULL.
|
||||
* \return true iff all unit tests pass, otherwise false.
|
||||
*/
|
||||
bool free_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn)
|
||||
{
|
||||
bool pass = true;
|
||||
cyaml_config_t config = {
|
||||
.log_fn = log_fn,
|
||||
.mem_fn = cyaml_mem,
|
||||
.log_level = log_level,
|
||||
.flags = CYAML_CFG_DEFAULT,
|
||||
};
|
||||
|
||||
ttest_heading(rc, "Free tests");
|
||||
|
||||
pass &= test_free_null_data(rc, &config);
|
||||
pass &= test_free_null_mem_fn(rc, &config);
|
||||
pass &= test_free_null_config(rc, &config);
|
||||
pass &= test_free_null_schema(rc, &config);
|
||||
|
||||
return pass;
|
||||
}
|
7725
libs/libcyaml/test/units/load.c
Normal file
7725
libs/libcyaml/test/units/load.c
Normal file
File diff suppressed because it is too large
Load Diff
4530
libs/libcyaml/test/units/save.c
Normal file
4530
libs/libcyaml/test/units/save.c
Normal file
File diff suppressed because it is too large
Load Diff
85
libs/libcyaml/test/units/test.c
Normal file
85
libs/libcyaml/test/units/test.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cyaml/cyaml.h>
|
||||
|
||||
#include "ttest.h"
|
||||
#include "test.h"
|
||||
|
||||
/**
|
||||
* Print program usage
|
||||
*
|
||||
* \param[in] prog_name Name of program.
|
||||
*/
|
||||
static void usage(const char *prog_name)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-q|-v|-d]\n", prog_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point from OS.
|
||||
*
|
||||
* \param[in] argc Argument count.
|
||||
* \param[in] argv Vector of string arguments.
|
||||
* \return Program return code.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool pass = true;
|
||||
bool quiet = false;
|
||||
ttest_report_ctx_t rc;
|
||||
const char *test_list = NULL;
|
||||
cyaml_log_fn_t log_fn = cyaml_log;
|
||||
cyaml_log_t log_level = CYAML_LOG_ERROR;
|
||||
enum {
|
||||
ARG_PROG_NAME,
|
||||
ARG_VERBOSE,
|
||||
ARG_TEST_LIST,
|
||||
ARG__COUNT,
|
||||
};
|
||||
|
||||
if (argc > ARG__COUNT) {
|
||||
usage(argv[ARG_PROG_NAME]);
|
||||
return EXIT_FAILURE;
|
||||
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if ((strcmp(argv[i], "-q") == 0) ||
|
||||
(strcmp(argv[i], "--quiet") == 0)) {
|
||||
quiet = true;
|
||||
log_fn = NULL;
|
||||
} else if ((strcmp(argv[i], "-v") == 0) ||
|
||||
(strcmp(argv[i], "--verbose") == 0)) {
|
||||
log_level = CYAML_LOG_INFO;
|
||||
} else if ((strcmp(argv[i], "-d") == 0) ||
|
||||
(strcmp(argv[i], "--debug") == 0)) {
|
||||
log_level = CYAML_LOG_DEBUG;
|
||||
} else {
|
||||
test_list = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
rc = ttest_init(test_list, quiet);
|
||||
|
||||
pass &= utf8_tests(&rc, log_level, log_fn);
|
||||
pass &= util_tests(&rc, log_level, log_fn);
|
||||
pass &= free_tests(&rc, log_level, log_fn);
|
||||
pass &= load_tests(&rc, log_level, log_fn);
|
||||
pass &= errs_tests(&rc, log_level, log_fn);
|
||||
pass &= file_tests(&rc, log_level, log_fn);
|
||||
pass &= save_tests(&rc, log_level, log_fn);
|
||||
|
||||
ttest_report(&rc);
|
||||
|
||||
return (pass) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
52
libs/libcyaml/test/units/test.h
Normal file
52
libs/libcyaml/test/units/test.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2021 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#ifndef TEST_H
|
||||
#define TEST_H
|
||||
|
||||
/** In load.c */
|
||||
extern bool load_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn);
|
||||
|
||||
/** In file.c */
|
||||
extern bool file_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn);
|
||||
|
||||
/** In free.c */
|
||||
extern bool free_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn);
|
||||
|
||||
/** In utf8.c */
|
||||
extern bool utf8_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn);
|
||||
|
||||
/** In util.c */
|
||||
extern bool util_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn);
|
||||
|
||||
/** In errs.c */
|
||||
extern bool errs_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn);
|
||||
|
||||
/** In save.c */
|
||||
extern bool save_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn);
|
||||
|
||||
#endif
|
291
libs/libcyaml/test/units/ttest.h
Normal file
291
libs/libcyaml/test/units/ttest.h
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2013-2019 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#ifndef TTEST_H
|
||||
#define TTEST_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Test cleanup client callback.
|
||||
*
|
||||
* The caller passes this callback function to ttest_start, and it is called
|
||||
* in whichever tlsa-test function finishes the test (ttest_pass, ttest_fail,
|
||||
* or ttest_todo).
|
||||
*
|
||||
* \param[in] Client data to clean up.
|
||||
*/
|
||||
typedef void (*ttest_cleanup_fn)(void *data);
|
||||
|
||||
/**
|
||||
* Internal tlsa-test report context.
|
||||
*
|
||||
* Clients should not touch this directly, it is only exposed in this
|
||||
* header because tlsa-test is arranged to be header-only, for convenience
|
||||
* of utilisation.
|
||||
*/
|
||||
typedef struct ttest_report_ctx {
|
||||
/** Space/comma separated list of tests to run. */
|
||||
const char *test_list;
|
||||
size_t test_list_len;
|
||||
|
||||
bool quiet; /**< Whether to print only the report summary. */
|
||||
unsigned tests; /**< Number of tests started. */
|
||||
unsigned todo; /**< Number of tests marked as unimplemented. */
|
||||
unsigned passed; /**< Number of tests passed. */
|
||||
} ttest_report_ctx_t;
|
||||
|
||||
/**
|
||||
* Internal tlsa-test test context.
|
||||
*
|
||||
* Clients should not touch this directly, it is only exposed in this
|
||||
* header because tlsa-test is arranged to be header-only, for convenience
|
||||
* of utilisation.
|
||||
*/
|
||||
typedef struct ttest_ctx {
|
||||
ttest_report_ctx_t *report; /**< The tlsa-test report context. */
|
||||
const char *name; /**< The unit test name. */
|
||||
ttest_cleanup_fn cleanup; /**< Client's unit test cleanup function. */
|
||||
void *cleanup_data; /**< Client's unit test cleanup context. */
|
||||
} ttest_ctx_t;
|
||||
|
||||
/**
|
||||
* Initialise a tlsa-test report context.
|
||||
*
|
||||
* \param[in] test_list Space/comma separated list of tests to run.
|
||||
* \param[in] quiet Whether report should be a minimal summary.
|
||||
* \return initialised tlsa-test report context.
|
||||
*/
|
||||
static inline ttest_report_ctx_t ttest_init(
|
||||
const char *test_list,
|
||||
bool quiet)
|
||||
{
|
||||
ttest_report_ctx_t rc = {
|
||||
.test_list = test_list,
|
||||
.test_list_len = 0,
|
||||
.quiet = quiet,
|
||||
};
|
||||
|
||||
if (test_list != NULL) {
|
||||
rc.test_list_len = strlen(test_list);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether test of given name should be run.
|
||||
*
|
||||
* \param[in] report The tlsa-test report context.
|
||||
* \param[in] name The name of the test to consider.
|
||||
* \return true if test should be run, false otherwise.
|
||||
*/
|
||||
static inline bool ttest__run_test(
|
||||
ttest_report_ctx_t *report,
|
||||
const char *name)
|
||||
{
|
||||
size_t len;
|
||||
size_t pos = 0;
|
||||
size_t name_len;
|
||||
|
||||
if (report->test_list == NULL || report->test_list_len == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
name_len = strlen(name);
|
||||
while (pos < report->test_list_len) {
|
||||
/* Skip commas and spaces. */
|
||||
pos += strspn(report->test_list + pos, ", ");
|
||||
len = strcspn(report->test_list + pos, ", ");
|
||||
if (len == name_len) {
|
||||
if (memcmp(report->test_list + pos, name, len) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a unit test.
|
||||
*
|
||||
* The when complete, the test must be competed by calling either ttest_pass,
|
||||
* ttest_fail, or ttest_todo.
|
||||
*
|
||||
* \param[in] report The tlsa-test report context.
|
||||
* \param[in] name Name for this unit test.
|
||||
* \param[in] cleanup Cleanup function to call on test completion.
|
||||
* \param[in] cleanup_data Pointer to client cleanup context.
|
||||
* \param[out] test_ctx_out Returns the unit test context on success.
|
||||
* The test context must be passed to the test
|
||||
* completion function.
|
||||
* \return true if the test should be started, false otherwise.
|
||||
*/
|
||||
static inline bool ttest_start(
|
||||
ttest_report_ctx_t *report,
|
||||
const char *name,
|
||||
ttest_cleanup_fn cleanup,
|
||||
void *cleanup_data,
|
||||
ttest_ctx_t *test_ctx_out)
|
||||
{
|
||||
ttest_ctx_t tc = {
|
||||
.name = name,
|
||||
.report = report,
|
||||
.cleanup = cleanup,
|
||||
.cleanup_data = cleanup_data,
|
||||
};
|
||||
|
||||
if (!ttest__run_test(report, name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
report->tests++;
|
||||
|
||||
*test_ctx_out = tc;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass a unit test.
|
||||
*
|
||||
* This function competes a unit test. It should be called when a test passes.
|
||||
* This function always returns true.
|
||||
*
|
||||
* \param[in] tc Unit test context, returned by ttest_start.
|
||||
* \return true.
|
||||
*/
|
||||
static inline bool ttest_pass(
|
||||
const ttest_ctx_t *tc)
|
||||
{
|
||||
assert(tc != NULL);
|
||||
assert(tc->report != NULL);
|
||||
|
||||
tc->report->passed++;
|
||||
|
||||
if (tc->cleanup != NULL) {
|
||||
tc->cleanup(tc->cleanup_data);
|
||||
}
|
||||
|
||||
if (tc->report->quiet == false) {
|
||||
fprintf(stderr, " PASS: %s\n", tc->name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail a unit test.
|
||||
*
|
||||
* This function competes a unit test. It should be called when a test fails.
|
||||
* This function always returns false. Prints the test result.
|
||||
*
|
||||
* \param[in] tc Unit test context, returned by ttest_start.
|
||||
* \param[in] reason Format string to explain reason for test failure.
|
||||
* \param[in] ... Additional arguments for formatted rendering.
|
||||
* \return false.
|
||||
*/
|
||||
static inline bool ttest_fail(
|
||||
const ttest_ctx_t *tc,
|
||||
const char *reason, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
assert(tc != NULL);
|
||||
assert(tc->report != NULL);
|
||||
|
||||
fprintf(stderr, " FAIL: %s (", tc->name);
|
||||
va_start(args, reason);
|
||||
vfprintf(stderr, reason, args);
|
||||
va_end(args);
|
||||
fprintf(stderr, ")\n");
|
||||
|
||||
/* Cleanup after printing result, in case `reason` refers to cleaned up
|
||||
* memory. */
|
||||
if (tc->cleanup != NULL) {
|
||||
tc->cleanup(tc->cleanup_data);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a unit test as unimplemented.
|
||||
*
|
||||
* This function competes a unit test. Should be called on unimplemented tests.
|
||||
* This function always returns true.
|
||||
*
|
||||
* \param[in] tc Unit test context, returned by ttest_start.
|
||||
* \return true.
|
||||
*/
|
||||
static inline bool ttest_todo(
|
||||
const ttest_ctx_t *tc)
|
||||
{
|
||||
assert(tc != NULL);
|
||||
assert(tc->report != NULL);
|
||||
|
||||
tc->report->todo++;
|
||||
|
||||
if (tc->cleanup != NULL) {
|
||||
tc->cleanup(tc->cleanup_data);
|
||||
}
|
||||
|
||||
if (tc->report->quiet == false) {
|
||||
fprintf(stderr, " TODO: %s\n", tc->name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a visual divider in the test output.
|
||||
*/
|
||||
static inline void ttest_divider(void)
|
||||
{
|
||||
fprintf(stderr, "========================================"
|
||||
"========================================\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a test heading.
|
||||
*
|
||||
* \param[in] tr The tlsa-test report context.
|
||||
* \param[in] heading The heading to print.
|
||||
*/
|
||||
static inline void ttest_heading(
|
||||
const ttest_report_ctx_t *tr,
|
||||
const char *heading)
|
||||
{
|
||||
if (tr->test_list == NULL || tr->test_list_len == 0) {
|
||||
if (!tr->quiet) {
|
||||
ttest_divider();
|
||||
fprintf(stderr, "TEST: %s\n", heading);
|
||||
ttest_divider();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the test report summary.
|
||||
*
|
||||
* \param[in] tr The tlsa-test report context.
|
||||
*/
|
||||
static inline void ttest_report(
|
||||
const ttest_report_ctx_t *tr)
|
||||
{
|
||||
ttest_divider();
|
||||
if (tr->todo > 0) {
|
||||
fprintf(stderr, "TODO: %u test%s unimplemented.\n",
|
||||
tr->todo, (tr->todo > 1) ? "s" : "");
|
||||
}
|
||||
fprintf(stderr, "%s: %u of %u tests passed.\n",
|
||||
(tr->passed == tr->tests - tr->todo) ? "PASS" : "FAIL",
|
||||
tr->passed, tr->tests - tr->todo);
|
||||
ttest_divider();
|
||||
}
|
||||
|
||||
#endif
|
280
libs/libcyaml/test/units/utf8.c
Normal file
280
libs/libcyaml/test/units/utf8.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2018-2021 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cyaml/cyaml.h>
|
||||
|
||||
#include "../../src/utf8.h"
|
||||
|
||||
#include "ttest.h"
|
||||
#include "test.h"
|
||||
|
||||
/** Helper macro to squash unused variable warnings. */
|
||||
#define UNUSED(_x) ((void)(_x))
|
||||
|
||||
/** Helper macro to get the length of string string literals. */
|
||||
#define SLEN(_s) (CYAML_ARRAY_LEN(_s) - 1)
|
||||
|
||||
/**
|
||||
* Test utf-8 decoding.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_utf8_get_codepoint(
|
||||
ttest_report_ctx_t *report)
|
||||
{
|
||||
static const struct tests {
|
||||
unsigned c;
|
||||
const char *s;
|
||||
unsigned l;
|
||||
} t[] = {
|
||||
{ 0xfffd, "\ufffd", SLEN("\ufffd") },
|
||||
{ 0xfffd, "\xC1\x9C", SLEN("\xC1\x9C") },
|
||||
{ 0x1f638, u8"😸", SLEN(u8"😸") },
|
||||
{ 0xfffd, u8"😸", 0 },
|
||||
{ 0xfffd, u8"😸", 5 },
|
||||
};
|
||||
bool pass = true;
|
||||
|
||||
for (unsigned i = 0; i < CYAML_ARRAY_LEN(t); i++) {
|
||||
unsigned l;
|
||||
unsigned c;
|
||||
ttest_ctx_t tc;
|
||||
char name[sizeof(__func__) + 32];
|
||||
sprintf(name, "%s_%u", __func__, i);
|
||||
|
||||
if (!ttest_start(report, name, NULL, NULL, &tc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
l = t[i].l;
|
||||
c = cyaml_utf8_get_codepoint((uint8_t *)t[i].s, &l);
|
||||
if (c != t[i].c) {
|
||||
pass &= ttest_fail(&tc, "Incorrect codepoint for %s "
|
||||
"(expecting %4.4x, got %4.4x)",
|
||||
t[i].s, t[i].c, c);
|
||||
continue;
|
||||
}
|
||||
|
||||
pass &= ttest_pass(&tc);
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test comparing the same strings.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_utf8_strcmp_same(
|
||||
ttest_report_ctx_t *report)
|
||||
{
|
||||
const char *strings[] = {
|
||||
"Simple",
|
||||
"test",
|
||||
"This is a LONGER string, if you see what I mean.",
|
||||
"29087 lsdkfj </,.{}'#\"|@>",
|
||||
u8"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß",
|
||||
u8"àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
|
||||
u8"¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿",
|
||||
"\xc3\0",
|
||||
u8"αβγδε",
|
||||
u8"¯\\_(ツ)_/¯",
|
||||
"\xfa",
|
||||
u8"😸",
|
||||
};
|
||||
bool pass = true;
|
||||
|
||||
for (unsigned i = 0; i < CYAML_ARRAY_LEN(strings); i++) {
|
||||
ttest_ctx_t tc;
|
||||
char name[sizeof(__func__) + 32];
|
||||
sprintf(name, "%s_%u", __func__, i);
|
||||
|
||||
if (!ttest_start(report, name, NULL, NULL, &tc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cyaml_utf8_casecmp(strings[i], strings[i]) != 0) {
|
||||
pass &= ttest_fail(&tc, "Failed to match: %s",
|
||||
strings[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
pass &= ttest_pass(&tc);
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test comparing strings that match.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_utf8_strcmp_matches(
|
||||
ttest_report_ctx_t *report)
|
||||
{
|
||||
static const struct string_pairs {
|
||||
const char *a;
|
||||
const char *b;
|
||||
} pairs[] = {
|
||||
{ "", "" },
|
||||
{ "This is a TEST", "this is A test" },
|
||||
{ u8"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ", u8"àáâãäåæçèéêëìíîïðñòóôõö" },
|
||||
{ u8"ĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞ", u8"āăąćĉċčďđēĕėęěĝğ" },
|
||||
{ u8"ijĵķĸĺļľ", u8"IJĴĶĸĹĻĽ" },
|
||||
{ u8"ŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶ", u8"ŋōŏőœŕŗřśŝşšţťŧũūŭůűųŵŷ" },
|
||||
{ u8"űųŵŷŸźżž", u8"űųŵŷÿŹŻŽ" },
|
||||
{ u8"ƂƄ ơƣƥ", u8"ƃƅ ƠƢƤ" },
|
||||
{ u8"ǞǠǢǤǦǨǪǬǮ", u8"ǟǡǣǥǧǩǫǭǯ" },
|
||||
{ u8"ǸǺǼǾȀȂȄȆȈȊȌȎȐȒȔȖȘȚȜȞ", u8"ǹǻǽǿȁȃȅȇȉȋȍȏȑȓȕȗșțȝȟ" },
|
||||
{ u8"ȢȤȦȨȪȬȮȰȲ", u8"ȣȥȧȩȫȭȯȱȳ" },
|
||||
{ u8"ɇɉɋɍɏ", u8"ɆɈɊɌɎ" },
|
||||
{ u8"ƯźżžƳƵ", u8"ưŹŻŽƴƶ" },
|
||||
{ u8"ǍǏǑǓǕǗǙǛ", u8"ǎǐǒǔǖǘǚǜ" },
|
||||
{ u8"\u0178", u8"\u00ff" },
|
||||
{ u8"\u0187", u8"\u0188" },
|
||||
{ u8"\u018b", u8"\u018c" },
|
||||
{ u8"\u018e", u8"\u01dd" },
|
||||
{ u8"\u0191", u8"\u0192" },
|
||||
{ u8"\u0198", u8"\u0199" },
|
||||
{ u8"\u01a7", u8"\u01a8" },
|
||||
{ u8"\u01ac", u8"\u01ad" },
|
||||
{ u8"\u01af", u8"\u01b0" },
|
||||
{ u8"\u01b7", u8"\u0292" },
|
||||
{ u8"\u01b8", u8"\u01b9" },
|
||||
{ u8"\u01bc", u8"\u01bd" },
|
||||
{ u8"\u01c4", u8"\u01c6" },
|
||||
{ u8"\u01c5", u8"\u01c6" },
|
||||
{ u8"\u01c7", u8"\u01c9" },
|
||||
{ u8"\u01c8", u8"\u01c9" },
|
||||
{ u8"\u01ca", u8"\u01cc" },
|
||||
{ u8"\u01cb", u8"\u01cc" },
|
||||
{ u8"\u01f1", u8"\u01f3" },
|
||||
{ u8"\u01f2", u8"\u01f3" },
|
||||
{ u8"\u01f4", u8"\u01f5" },
|
||||
{ u8"\u01f7", u8"\u01bf" },
|
||||
{ u8"\u0220", u8"\u019e" },
|
||||
{ u8"\u023b", u8"\u023c" },
|
||||
{ u8"\u023d", u8"\u019a" },
|
||||
{ u8"\u0241", u8"\u0242" },
|
||||
{ u8"\u0243", u8"\u0180" },
|
||||
{ "\xF0\x9F\x98\xB8", "\xF0\x9F\x98\xB8" },
|
||||
{ "\xF0\x00\x98\xB8", "\xF0\x00\x98\xB8" },
|
||||
{ "\xF0\x9F\x00\xB8", "\xF0\x9F\x00\xB8" },
|
||||
{ "\xF0\x9F\x98\x00", "\xF0\x9F\x98\x00" },
|
||||
{ "\xE2\x9F\x9A", "\xE2\x9F\x9A" },
|
||||
{ "\xE2\x00\x9A", "\xE2\x00\x9A" },
|
||||
{ "\xE2\x9F\x00", "\xE2\x9F\x00" },
|
||||
{ "A\xc2""C", "A\xc2""C" },
|
||||
{ "A\xc2""C", u8"A\ufffdC" },
|
||||
{ u8"A\ufffdC", "A\xc2""C" },
|
||||
};
|
||||
bool pass = true;
|
||||
|
||||
for (unsigned i = 0; i < CYAML_ARRAY_LEN(pairs); i++) {
|
||||
ttest_ctx_t tc;
|
||||
char name[sizeof(__func__) + 32];
|
||||
sprintf(name, "%s_%u", __func__, i);
|
||||
|
||||
if (!ttest_start(report, name, NULL, NULL, &tc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cyaml_utf8_casecmp(pairs[i].a, pairs[i].b) != 0) {
|
||||
pass &= ttest_fail(&tc, "Failed to match strings: "
|
||||
"%s and %s", pairs[i].a, pairs[i].b);
|
||||
continue;
|
||||
}
|
||||
|
||||
pass &= ttest_pass(&tc);
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test comparing strings that match.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_utf8_strcmp_mismatches(
|
||||
ttest_report_ctx_t *report)
|
||||
{
|
||||
static const struct string_pairs {
|
||||
const char *a;
|
||||
const char *b;
|
||||
} pairs[] = {
|
||||
{ "Invalid", "\xfa" },
|
||||
{ "Cat", u8"😸" },
|
||||
{ "cat", u8"😸" },
|
||||
{ "1 cat", u8"😸" },
|
||||
{ "[cat]", u8"😸" },
|
||||
{ "Ü cat", u8"😸" },
|
||||
{ "Ü cat", u8"😸" },
|
||||
{ "\\", "\xC1\x9C" },
|
||||
};
|
||||
bool pass = true;
|
||||
|
||||
for (unsigned i = 0; i < CYAML_ARRAY_LEN(pairs); i++) {
|
||||
ttest_ctx_t tc;
|
||||
char name[sizeof(__func__) + 32];
|
||||
sprintf(name, "%s_%u", __func__, i);
|
||||
|
||||
if (!ttest_start(report, name, NULL, NULL, &tc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cyaml_utf8_casecmp(pairs[i].a, pairs[i].b) == 0) {
|
||||
pass &= ttest_fail(&tc, "Failed to detect mismatch: "
|
||||
"%s and %s", pairs[i].a, pairs[i].b);
|
||||
continue;
|
||||
}
|
||||
|
||||
pass &= ttest_pass(&tc);
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the CYAML util unit tests.
|
||||
*
|
||||
* \param[in] rc The ttest report context.
|
||||
* \param[in] log_level CYAML log level.
|
||||
* \param[in] log_fn CYAML logging function, or NULL.
|
||||
* \return true iff all unit tests pass, otherwise false.
|
||||
*/
|
||||
bool utf8_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn)
|
||||
{
|
||||
bool pass = true;
|
||||
|
||||
UNUSED(log_level);
|
||||
UNUSED(log_fn);
|
||||
|
||||
ttest_heading(rc, "UTF-8 tests: Codepoint composition");
|
||||
|
||||
pass &= test_utf8_get_codepoint(rc);
|
||||
|
||||
ttest_heading(rc, "UTF-8 tests: String comparison");
|
||||
|
||||
pass &= test_utf8_strcmp_same(rc);
|
||||
pass &= test_utf8_strcmp_matches(rc);
|
||||
pass &= test_utf8_strcmp_mismatches(rc);
|
||||
|
||||
return pass;
|
||||
}
|
300
libs/libcyaml/test/units/util.c
Normal file
300
libs/libcyaml/test/units/util.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (C) 2018-2021 Michael Drake <tlsa@netsurf-browser.org>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cyaml/cyaml.h>
|
||||
|
||||
#include "../../src/mem.h"
|
||||
#include "../../src/util.h"
|
||||
|
||||
#include "ttest.h"
|
||||
#include "test.h"
|
||||
|
||||
/**
|
||||
* Test cyaml memory functions.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_util_memory_funcs(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
ttest_ctx_t tc;
|
||||
unsigned char *mem, *tmp;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
/* Create test allocation */
|
||||
mem = cyaml__alloc(config, 0xff, true);
|
||||
if (mem == NULL) {
|
||||
return ttest_fail(&tc, "Memory allocation failed.");
|
||||
}
|
||||
|
||||
/* Check allocation was zeroed. */
|
||||
for (unsigned i = 0; i < 0x7f; i++) {
|
||||
if (mem[i] != 0) {
|
||||
return ttest_fail(&tc, "Allocation not cleaned.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Set our own known values */
|
||||
for (unsigned i = 0; i < 0xff; i++) {
|
||||
mem[i] = 0xff;
|
||||
}
|
||||
|
||||
/* Shrink allocation */
|
||||
tmp = cyaml__realloc(config, mem, 0xff, 0x7f, true);
|
||||
if (tmp == NULL) {
|
||||
return ttest_fail(&tc, "Allocation shrink failed.");
|
||||
}
|
||||
mem = tmp;
|
||||
|
||||
/* Check for our own values. */
|
||||
for (unsigned i = 0; i < 0x7f; i++) {
|
||||
if (mem[i] != 0xff) {
|
||||
return ttest_fail(&tc, "Data trampled by realloc.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Free test allocation. */
|
||||
cyaml__free(config, mem);
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cyaml memory functions.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_util_strdup(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
ttest_ctx_t tc;
|
||||
const char *orig = "Hello!";
|
||||
char *copy;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
/* Create test allocation */
|
||||
copy = cyaml__strdup(config, orig, NULL);
|
||||
if (copy == NULL) {
|
||||
return ttest_fail(&tc, "Memory allocation failed.");
|
||||
}
|
||||
|
||||
/* Confirm string duplication. */
|
||||
if (strcmp(orig, copy) != 0) {
|
||||
return ttest_fail(&tc, "String not copied correctly.");
|
||||
}
|
||||
|
||||
/* Free test allocation. */
|
||||
cyaml__free(config, copy);
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cyaml memory functions.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \param[in] config The CYAML config to use for the test.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_util_strdup_len(
|
||||
ttest_report_ctx_t *report,
|
||||
const cyaml_config_t *config)
|
||||
{
|
||||
ttest_ctx_t tc;
|
||||
const char *orig = "Hello!";
|
||||
char *copy;
|
||||
size_t len;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
/* Create test allocation */
|
||||
copy = cyaml__strdup(config, orig, &len);
|
||||
if (copy == NULL) {
|
||||
return ttest_fail(&tc, "Memory allocation failed.");
|
||||
}
|
||||
|
||||
/* Confirm string duplication. */
|
||||
if (strcmp(orig, copy) != 0) {
|
||||
return ttest_fail(&tc, "String not copied correctly.");
|
||||
}
|
||||
|
||||
/* Confirm string length. */
|
||||
if (strlen(orig) != len) {
|
||||
return ttest_fail(&tc, "Bad string length.");
|
||||
}
|
||||
|
||||
/* Free test allocation. */
|
||||
cyaml__free(config, copy);
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invalid state machine state.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_util_state_invalid(
|
||||
ttest_report_ctx_t *report)
|
||||
{
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
if (strcmp(cyaml__state_to_str(CYAML_STATE__COUNT),
|
||||
"<invalid>") != 0) {
|
||||
return ttest_fail(&tc, "Wrong string for invalid state.");
|
||||
}
|
||||
|
||||
if (strcmp(cyaml__state_to_str(CYAML_STATE__COUNT + 1),
|
||||
"<invalid>") != 0) {
|
||||
return ttest_fail(&tc, "Wrong string for invalid state.");
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test CYAML_OK error code.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_util_err_success_zero(
|
||||
ttest_report_ctx_t *report)
|
||||
{
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
if (CYAML_OK != 0) {
|
||||
return ttest_fail(&tc, "CYAML_OK value not zero");
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test valid cyaml_strerror strings.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_util_err_strings_valid(
|
||||
ttest_report_ctx_t *report)
|
||||
{
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
if (cyaml_strerror(CYAML_OK) == NULL) {
|
||||
return ttest_fail(&tc, "CYAML_OK string is NULL");
|
||||
}
|
||||
|
||||
if (strcmp(cyaml_strerror(CYAML_OK), "Success") != 0) {
|
||||
return ttest_fail(&tc, "CYAML_OK string not 'Success'");
|
||||
}
|
||||
|
||||
for (unsigned i = 1; i <= CYAML_ERR__COUNT; i++) {
|
||||
if (cyaml_strerror(i) == NULL) {
|
||||
return ttest_fail(&tc, "Error code %u string is NULL",
|
||||
i);
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < i; j++) {
|
||||
if (strcmp(cyaml_strerror(j), cyaml_strerror(i)) == 0) {
|
||||
return ttest_fail(&tc, "Two error codes "
|
||||
"have same string (%u and %u)",
|
||||
i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invalid cyaml_strerror strings.
|
||||
*
|
||||
* \param[in] report The test report context.
|
||||
* \return true if test passes, false otherwise.
|
||||
*/
|
||||
static bool test_util_err_strings_invalid(
|
||||
ttest_report_ctx_t *report)
|
||||
{
|
||||
ttest_ctx_t tc;
|
||||
|
||||
if (!ttest_start(report, __func__, NULL, NULL, &tc)) return true;
|
||||
|
||||
if (strcmp(cyaml_strerror(CYAML_ERR__COUNT),
|
||||
"Invalid error code") != 0) {
|
||||
return ttest_fail(&tc, "CYAML_ERR__COUNT string not "
|
||||
"'Invalid error code'");
|
||||
}
|
||||
|
||||
if (strcmp(cyaml_strerror(CYAML_ERR__COUNT + 1),
|
||||
"Invalid error code") != 0) {
|
||||
return ttest_fail(&tc, "CYAML_ERR__COUNT + 1 string not "
|
||||
"'Invalid error code'");
|
||||
}
|
||||
|
||||
if (strcmp(cyaml_strerror((cyaml_err_t)-1), "Invalid error code") != 0) {
|
||||
return ttest_fail(&tc, "-1 string not 'Invalid error code'");
|
||||
}
|
||||
|
||||
return ttest_pass(&tc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the CYAML util unit tests.
|
||||
*
|
||||
* \param[in] rc The ttest report context.
|
||||
* \param[in] log_level CYAML log level.
|
||||
* \param[in] log_fn CYAML logging function, or NULL.
|
||||
* \return true iff all unit tests pass, otherwise false.
|
||||
*/
|
||||
bool util_tests(
|
||||
ttest_report_ctx_t *rc,
|
||||
cyaml_log_t log_level,
|
||||
cyaml_log_fn_t log_fn)
|
||||
{
|
||||
bool pass = true;
|
||||
cyaml_config_t config = {
|
||||
.log_fn = log_fn,
|
||||
.mem_fn = cyaml_mem,
|
||||
.log_level = log_level,
|
||||
.flags = CYAML_CFG_DEFAULT,
|
||||
};
|
||||
|
||||
ttest_heading(rc, "Memory utility functions");
|
||||
|
||||
pass &= test_util_strdup(rc, &config);
|
||||
pass &= test_util_strdup_len(rc, &config);
|
||||
pass &= test_util_memory_funcs(rc, &config);
|
||||
|
||||
ttest_heading(rc, "Error code tests");
|
||||
|
||||
pass &= test_util_state_invalid(rc);
|
||||
pass &= test_util_err_success_zero(rc);
|
||||
pass &= test_util_err_strings_valid(rc);
|
||||
pass &= test_util_err_strings_invalid(rc);
|
||||
|
||||
return pass;
|
||||
}
|
1
objs/libcyaml.so
Symbolic link
1
objs/libcyaml.so
Symbolic link
@ -0,0 +1 @@
|
||||
/home/joe/Development/Ennix/objs/libcyaml.so.1
|
BIN
objs/libcyaml.so.1
Executable file
BIN
objs/libcyaml.so.1
Executable file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user