[RFC v2 3/4] libcamera: Implement YamlEmitter
Jacopo Mondi
jacopo.mondi at ideasonboard.com
Thu Oct 17 14:52:18 CEST 2024
Signed-off-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
---
include/libcamera/internal/meson.build | 1 +
include/libcamera/internal/yaml_emitter.h | 162 ++++++++++
src/libcamera/meson.build | 1 +
src/libcamera/yaml_emitter.cpp | 362 ++++++++++++++++++++++
4 files changed, 526 insertions(+)
create mode 100644 include/libcamera/internal/yaml_emitter.h
create mode 100644 src/libcamera/yaml_emitter.cpp
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index 1c5eef9cab80..7533b075fde2 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -41,6 +41,7 @@ libcamera_internal_headers = files([
'v4l2_pixelformat.h',
'v4l2_subdevice.h',
'v4l2_videodevice.h',
+ 'yaml_emitter.h',
'yaml_parser.h',
])
diff --git a/include/libcamera/internal/yaml_emitter.h b/include/libcamera/internal/yaml_emitter.h
new file mode 100644
index 000000000000..e4a0e3b440a5
--- /dev/null
+++ b/include/libcamera/internal/yaml_emitter.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board Oy
+ *
+ * libcamera YAML emitter helper
+ */
+
+#pragma once
+
+#include <memory>
+#include <string_view>
+
+#include <libcamera/base/class.h>
+#include <libcamera/base/file.h>
+#include <libcamera/orientation.h>
+
+#include <yaml.h>
+
+namespace libcamera {
+
+class YamlDict;
+class YamlEvent;
+class YamlList;
+class YamlRoot;
+class YamlScalar;
+
+class YamlEmitter final
+{
+public:
+ ~YamlEmitter();
+
+ static std::unique_ptr<YamlRoot> root(std::string_view path);
+
+ int emit();
+ yaml_event_t *event() { return &event_; }
+
+private:
+ LIBCAMERA_DISABLE_COPY(YamlEmitter)
+
+ class Emitter
+ {
+ public:
+ ~Emitter();
+
+ void init(File *file);
+
+ int emit(yaml_event_t *event);
+
+ private:
+ void logError();
+
+ yaml_emitter_t emitter_;
+ };
+
+ YamlEmitter() = default;
+
+ void init();
+
+ std::unique_ptr<File> file_;
+ yaml_event_t event_;
+ Emitter emitter_;
+};
+
+class YamlOutput
+{
+public:
+ virtual ~YamlOutput() {};
+
+ YamlOutput(YamlOutput &&other)
+ {
+ emitter_ = other.emitter_;
+ other.emitter_ = nullptr;
+ }
+
+ YamlScalar scalar();
+ std::unique_ptr<YamlDict> dict();
+ std::unique_ptr<YamlList> list();
+
+protected:
+ YamlOutput(YamlEmitter *emitter)
+ : emitter_(emitter)
+ {
+ }
+
+ int emitScalar(std::string_view scalar);
+ int emitMappingStart();
+ int emitMappingEnd();
+ int emitSequenceStart();
+ int emitSequenceEnd();
+
+ YamlEmitter *emitter_;
+ yaml_event_t event_;
+};
+
+class YamlRoot : public YamlOutput
+{
+public:
+ ~YamlRoot();
+
+ std::unique_ptr<YamlList> list();
+ std::unique_ptr<YamlDict> dict();
+ void scalar(std::string_view scalar);
+
+private:
+ friend class YamlEmitter;
+
+ YamlRoot(YamlEmitter *emitter)
+ : YamlOutput(emitter)
+ {
+ emitterRoot_ = std::unique_ptr<YamlEmitter>(emitter);
+ }
+
+ std::unique_ptr<YamlEmitter> emitterRoot_;
+};
+
+class YamlScalar : public YamlOutput
+{
+public:
+ ~YamlScalar() = default;
+
+ void operator=(std::string_view scalar);
+
+private:
+ friend class YamlOutput;
+
+ YamlScalar(YamlEmitter *emitter);
+};
+
+class YamlList : public YamlOutput
+{
+public:
+ YamlList(YamlList &&other) = default;
+ ~YamlList();
+
+ std::unique_ptr<YamlList> list();
+ std::unique_ptr<YamlDict> dict();
+ void scalar(std::string_view scalar);
+
+private:
+ friend class YamlOutput;
+
+ YamlList(YamlEmitter *emitter);
+};
+
+class YamlDict : public YamlOutput
+{
+public:
+ YamlDict(YamlDict &&other) = default;
+ ~YamlDict();
+
+ std::unique_ptr<YamlList> list(std::string_view key);
+ std::unique_ptr<YamlDict> dict(std::string_view key);
+
+ YamlScalar operator[](std::string_view key);
+
+private:
+ friend class YamlOutput;
+
+ YamlDict(YamlEmitter *emitter);
+};
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index aa9ab0291854..5b8af4103085 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -51,6 +51,7 @@ libcamera_internal_sources = files([
'v4l2_pixelformat.cpp',
'v4l2_subdevice.cpp',
'v4l2_videodevice.cpp',
+ 'yaml_emitter.cpp',
'yaml_parser.cpp',
])
diff --git a/src/libcamera/yaml_emitter.cpp b/src/libcamera/yaml_emitter.cpp
new file mode 100644
index 000000000000..f2aaa1c1c1a6
--- /dev/null
+++ b/src/libcamera/yaml_emitter.cpp
@@ -0,0 +1,362 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2024, Ideas On Board Oy
+ *
+ * libcamera YAML emitter helper
+ */
+
+#include "libcamera/internal/yaml_emitter.h"
+
+#include <libcamera/base/log.h>
+
+/**
+ * \file yaml_emitter.h
+ * \brief A YAML emitter helper
+ *
+ * The YAML Emitter helpers allows users to emit output in YAML format.
+ *
+ * To emit YAML users of this classes should create a root node with
+ *
+ * \code
+ std::string filePath("...");
+ auto root = YamlEmitter::root(filePath);
+ \endcode
+ *
+ * A YamlRoot implements RAII-style handling of YAML sequence and document
+ * events handling. Creating a YamlRoot emits the STREAM_START and DOC_START
+ * events. Once a YamlRoot gets deleted the DOC_SEND and STREAM_END events
+ * gets automatically deleted.
+ *
+ * Once a root node has been created it is possible to populate it with
+ * scalars, list or dictionaries.
+ *
+ * YamlList and YamlDict can only be created wrapped in unique_ptr<> instances,
+ * to implement a RAII-style handling of YAML of lists and dictionaries.
+ * Creating a YamlList and a YamlDict emits the YAML sequence and mapping start
+ * events. Once an instance gets deleted, the sequence and mapping gets
+ * automatically emitted.
+ *
+ * A YamlScalar is a simpler object that can be assigned to different types
+ * to emit them as strings.
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(YamlEmitter)
+
+namespace {
+
+int yamlWrite(void *data, unsigned char *buffer, size_t size)
+{
+ File *file = static_cast<File *>(data);
+
+ Span<unsigned char> buf{ buffer, size };
+ ssize_t ret = file->write(buf);
+ if (ret < 0) {
+ ret = errno;
+ LOG(YamlEmitter, Error) << "Write error : " << strerror(ret);
+ return 0;
+ }
+
+ return 1;
+}
+
+} /* namespace */
+
+/**
+ * \class YamlEmitter
+ *
+ * Yaml Emitter entry point. Allows to create a YamlRoot object that users
+ * can populate.
+ */
+
+YamlEmitter::~YamlEmitter()
+{
+ yaml_event_delete(&event_);
+}
+
+/**
+ * \brief Create a YamlRoot that applications can start populating with YamlOutput
+ * \param[in] path The YAML output file path
+ * \return A unique pointer to a YamlRoot
+ */
+std::unique_ptr<YamlRoot> YamlEmitter::root(std::string_view path)
+{
+ YamlEmitter *emitter = new YamlEmitter();
+
+ std::string filePath(path);
+ emitter->file_ = std::make_unique<File>(filePath);
+ emitter->file_->open(File::OpenModeFlag::WriteOnly);
+
+ emitter->init();
+
+ YamlRoot *root = new YamlRoot(emitter);
+ return std::unique_ptr<YamlRoot>(root);
+}
+
+/**
+ * \brief Emit a yaml event
+ */
+int YamlEmitter::emit()
+{
+ return emitter_.emit(&event_);
+}
+
+void YamlEmitter::init()
+{
+ emitter_.init(file_.get());
+
+ yaml_stream_start_event_initialize(&event_, YAML_UTF8_ENCODING);
+ emitter_.emit(&event_);
+
+ yaml_document_start_event_initialize(&event_, NULL, NULL, NULL, 0);
+ emitter_.emit(&event_);
+}
+
+/**
+ * \class YamlEmitter::Emitter
+ *
+ * yaml_emitter_t wrapper. Initialize the yaml_emitter_t on creation allows
+ * YamlOutput classes to emit events.
+ */
+
+YamlEmitter::Emitter::~Emitter()
+{
+ yaml_emitter_delete(&emitter_);
+}
+
+void YamlEmitter::Emitter::init(File *file)
+{
+ yaml_emitter_initialize(&emitter_);
+ yaml_emitter_set_output(&emitter_, yamlWrite, file);
+}
+
+void YamlEmitter::Emitter::logError()
+{
+ switch (emitter_.error) {
+ case YAML_MEMORY_ERROR:
+ LOG(YamlEmitter, Error)
+ << "Memory error: Not enough memory for emitting";
+ break;
+
+ case YAML_WRITER_ERROR:
+ LOG(YamlEmitter, Error)
+ << "Writer error: " << emitter_.problem;
+ break;
+
+ case YAML_EMITTER_ERROR:
+ LOG(YamlEmitter, Error)
+ << "Emitter error: " << emitter_.problem;
+ break;
+
+ default:
+ LOG(YamlEmitter, Error) << "Internal problem";
+ break;
+ }
+}
+
+int YamlEmitter::Emitter::emit(yaml_event_t *event)
+{
+ int ret = yaml_emitter_emit(&emitter_, event);
+ if (!ret) {
+ logError();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * \class YamlOutput
+ *
+ * The YamlOutput base class. From this class YamlScalar, YamlList and YamlDict
+ * are derived.
+ *
+ * The YamlOutput class provides functions to create a scalar, a list or a
+ * dictionary.
+ *
+ * The class cannot be instantiated directly by applications.
+ */
+
+YamlScalar YamlOutput::scalar()
+{
+ return YamlScalar(emitter_);
+}
+
+std::unique_ptr<YamlDict> YamlOutput::dict()
+{
+ YamlDict dict(emitter_);
+
+ return std::make_unique<YamlDict>(std::move(dict));
+}
+
+std::unique_ptr<YamlList> YamlOutput::list()
+{
+ YamlList list(emitter_);
+
+ return std::make_unique<YamlList>(std::move(list));
+}
+
+int YamlOutput::emitScalar(std::string_view scalar)
+{
+ const unsigned char *value = reinterpret_cast<const unsigned char *>
+ (scalar.data());
+ yaml_scalar_event_initialize(emitter_->event(), NULL, NULL, value,
+ scalar.length(), true, false,
+ YAML_PLAIN_SCALAR_STYLE);
+ return emitter_->emit();
+}
+
+int YamlOutput::emitMappingStart()
+{
+ yaml_mapping_start_event_initialize(emitter_->event(), NULL, NULL,
+ true, YAML_BLOCK_MAPPING_STYLE);
+ return emitter_->emit();
+}
+
+int YamlOutput::emitMappingEnd()
+{
+ yaml_mapping_end_event_initialize(emitter_->event());
+ return emitter_->emit();
+}
+
+int YamlOutput::emitSequenceStart()
+{
+ yaml_sequence_start_event_initialize(emitter_->event(), NULL, NULL, true,
+ YAML_BLOCK_SEQUENCE_STYLE);
+ return emitter_->emit();
+}
+
+int YamlOutput::emitSequenceEnd()
+{
+ yaml_sequence_end_event_initialize(emitter_->event());
+ return emitter_->emit();
+}
+
+/**
+ * \class YamlRoot
+ *
+ * Yaml root node. A root node can be populated with a scalar, a list or a dict.
+ */
+
+YamlRoot::~YamlRoot()
+{
+ yaml_document_end_event_initialize(emitter_->event(), 0);
+ emitterRoot_->emit();
+
+ yaml_stream_end_event_initialize(emitter_->event());
+ emitterRoot_->emit();
+}
+
+std::unique_ptr<YamlDict> YamlRoot::dict()
+{
+ emitMappingStart();
+
+ return YamlOutput::dict();
+}
+
+std::unique_ptr<YamlList> YamlRoot::list()
+{
+ emitSequenceStart();
+
+ return YamlOutput::list();
+}
+
+/**
+ * \class YamlScalar
+ *
+ * A YamlScalar can be assigned to an std::string_view and other libcamera
+ * types to emit them as YAML scalars.
+ */
+
+YamlScalar::YamlScalar(YamlEmitter *emitter)
+ : YamlOutput(emitter)
+{
+}
+
+void YamlScalar::operator=(std::string_view scalar)
+{
+ emitScalar(scalar);
+}
+
+/**
+ * \class YamlList
+ *
+ * A YamlList can be populated with scalar and allows to create other lists
+ * and dictionaries.
+ */
+
+YamlList::YamlList(YamlEmitter *emitter)
+ : YamlOutput(emitter)
+{
+}
+
+YamlList::~YamlList()
+{
+ if (emitter_)
+ emitSequenceEnd();
+}
+
+void YamlList::scalar(std::string_view scalar)
+{
+ emitScalar(scalar);
+}
+
+std::unique_ptr<YamlList> YamlList::list()
+{
+ emitSequenceStart();
+
+ return YamlOutput::list();
+}
+
+std::unique_ptr<YamlDict> YamlList::dict()
+{
+ emitMappingStart();
+
+ return YamlOutput::dict();
+}
+
+/**
+ * \class YamlDict
+ *
+ * A YamlDict derives an unordered_map that maps strings to YAML scalar.
+ *
+ * A YamlDict can be populated with scalars using operator[] and allows to
+ * create other lists and dictionaries.
+ */
+
+YamlDict::YamlDict(YamlEmitter *emitter)
+ : YamlOutput(emitter)
+{
+}
+
+YamlDict::~YamlDict()
+{
+ if (emitter_)
+ emitMappingEnd();
+}
+
+std::unique_ptr<YamlList> YamlDict::list(std::string_view key)
+{
+ emitScalar(key);
+ emitSequenceStart();
+
+ return YamlOutput::list();
+}
+
+std::unique_ptr<YamlDict> YamlDict::dict(std::string_view key)
+{
+ emitScalar(key);
+ emitMappingStart();
+
+ return YamlOutput::dict();
+}
+
+YamlScalar YamlDict::operator[](std::string_view key)
+{
+ emitScalar(key);
+
+ return YamlOutput::scalar();
+}
+
+} /* namespace libcamera */
--
2.47.0
More information about the libcamera-devel
mailing list