diff options
Diffstat (limited to 'src/writer.c')
-rw-r--r-- | src/writer.c | 267 |
1 files changed, 132 insertions, 135 deletions
diff --git a/src/writer.c b/src/writer.c index e4ef5651..aa7fd980 100644 --- a/src/writer.c +++ b/src/writer.c @@ -1,4 +1,4 @@ -// Copyright 2011-2023 David Robillard <d@drobilla.net> +// Copyright 2011-2025 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #include "attributes.h" @@ -10,7 +10,7 @@ #include "uri_utils.h" #include "warnings.h" -#include "serd/serd.h" +#include <serd/serd.h> #include <assert.h> #include <errno.h> @@ -37,20 +37,12 @@ typedef enum { typedef struct { ContextType type; + bool comma_indented; SerdNode graph; SerdNode subject; SerdNode predicate; - bool predicates; - bool comma_indented; } WriteContext; -static const WriteContext WRITE_CONTEXT_NULL = {CTX_NAMED, - {0, 0, 0, 0, SERD_NOTHING}, - {0, 0, 0, 0, SERD_NOTHING}, - {0, 0, 0, 0, SERD_NOTHING}, - 0U, - 0U}; - typedef enum { SEP_NONE, ///< Sentinel after "nothing" SEP_NEWLINE, ///< Sentinel after a line end @@ -143,7 +135,7 @@ write_node(SerdWriter* writer, SerdStatementFlags flags); SERD_NODISCARD static bool -supports_abbrev(const SerdWriter* writer) +supports_abbrev(const SerdWriter* const writer) { return writer->syntax == SERD_TURTLE || writer->syntax == SERD_TRIG; } @@ -162,7 +154,7 @@ free_context(WriteContext* const ctx) SERD_LOG_FUNC(3, 4) static SerdStatus -w_err(SerdWriter* writer, SerdStatus st, const char* fmt, ...) +w_err(SerdWriter* const writer, const SerdStatus st, const char* const fmt, ...) { /* TODO: This results in errors with no file information, which is not helpful when re-serializing a file (particularly for "undefined @@ -179,7 +171,7 @@ w_err(SerdWriter* writer, SerdStatus st, const char* fmt, ...) } static void -copy_node(SerdNode* dst, const SerdNode* src) +copy_node(SerdNode* const dst, const SerdNode* const src) { const size_t new_size = src->n_bytes + 1U; uint8_t* const new_buf = (uint8_t*)realloc((char*)dst->buf, new_size); @@ -205,12 +197,12 @@ push_context(SerdWriter* const writer, *(WriteContext*)top = writer->context; // Update the current context - const WriteContext current = {type, graph, subject, predicate, 0U, 0U}; + const WriteContext current = {type, false, graph, subject, predicate}; writer->context = current; } static void -pop_context(SerdWriter* writer) +pop_context(SerdWriter* const writer) { // Replace the current context with the top of the stack free_context(&writer->context); @@ -223,7 +215,7 @@ pop_context(SerdWriter* writer) } SERD_NODISCARD static size_t -sink(const void* buf, size_t len, SerdWriter* writer) +sink(const void* const buf, const size_t len, SerdWriter* const writer) { const size_t written = serd_byte_sink_write(buf, len, &writer->byte_sink); if (written != len) { @@ -238,8 +230,8 @@ sink(const void* buf, size_t len, SerdWriter* writer) return written; } -SERD_NODISCARD static inline SerdStatus -esink(const void* buf, size_t len, SerdWriter* writer) +SERD_NODISCARD static SerdStatus +esink(const void* const buf, const size_t len, SerdWriter* const writer) { return sink(buf, len, writer) == len ? SERD_SUCCESS : SERD_ERR_BAD_WRITE; } @@ -247,10 +239,10 @@ esink(const void* buf, size_t len, SerdWriter* writer) // Write a single character, as an escape for single byte characters // (Caller prints any single byte characters that don't need escaping) static size_t -write_character(SerdWriter* writer, - const uint8_t* utf8, - size_t* size, - SerdStatus* st) +write_character(SerdWriter* const writer, + const uint8_t* const utf8, + uint8_t* const size, + SerdStatus* const st) { char escape[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const uint32_t c = parse_utf8_char(utf8, size); @@ -288,10 +280,10 @@ uri_must_escape(const uint8_t c) } static size_t -write_uri(SerdWriter* writer, - const uint8_t* utf8, - size_t n_bytes, - SerdStatus* st) +write_uri(SerdWriter* const writer, + const uint8_t* const utf8, + const size_t n_bytes, + SerdStatus* const st) { size_t len = 0; for (size_t i = 0; i < n_bytes;) { @@ -315,14 +307,14 @@ write_uri(SerdWriter* writer, } // Write UTF-8 character - size_t size = 0; + uint8_t size = 0U; len += write_character(writer, utf8 + i, &size, st); i += size; if (*st && (writer->style & SERD_STYLE_STRICT)) { break; } - if (size == 0) { + if (!size) { // Corrupt input, write percent-encoded bytes and scan to next start char escape[4] = {0, 0, 0, 0}; for (; i < n_bytes && (utf8[i] & 0x80); ++i) { @@ -336,7 +328,9 @@ write_uri(SerdWriter* writer, } SERD_NODISCARD static SerdStatus -ewrite_uri(SerdWriter* writer, const uint8_t* utf8, size_t n_bytes) +ewrite_uri(SerdWriter* const writer, + const uint8_t* const utf8, + const size_t n_bytes) { SerdStatus st = SERD_SUCCESS; write_uri(writer, utf8, n_bytes, &st); @@ -347,7 +341,7 @@ ewrite_uri(SerdWriter* writer, const uint8_t* utf8, size_t n_bytes) } SERD_NODISCARD static SerdStatus -write_uri_from_node(SerdWriter* writer, const SerdNode* node) +write_uri_from_node(SerdWriter* const writer, const SerdNode* const node) { return ewrite_uri(writer, node->buf, node->n_bytes); } @@ -369,7 +363,9 @@ lname_must_escape(const uint8_t c) } SERD_NODISCARD static SerdStatus -write_lname(SerdWriter* writer, const uint8_t* utf8, size_t n_bytes) +write_lname(SerdWriter* const writer, + const uint8_t* const utf8, + const size_t n_bytes) { SerdStatus st = SERD_SUCCESS; for (size_t i = 0; i < n_bytes; ++i) { @@ -395,10 +391,10 @@ write_lname(SerdWriter* writer, const uint8_t* utf8, size_t n_bytes) } SERD_NODISCARD static SerdStatus -write_text(SerdWriter* writer, - TextContext ctx, - const uint8_t* utf8, - size_t n_bytes) +write_text(SerdWriter* const writer, + const TextContext ctx, + const uint8_t* const utf8, + const size_t n_bytes) { size_t n_consecutive_quotes = 0; SerdStatus st = SERD_SUCCESS; @@ -484,19 +480,19 @@ write_text(SerdWriter* writer, } // Write UTF-8 character - size_t size = 0; + uint8_t size = 0U; write_character(writer, utf8 + i - 1, &size, &st); if (st && (writer->style & SERD_STYLE_STRICT)) { return st; } - if (size == 0) { + if (size > 0U) { + i += size - 1U; + } else { // Corrupt input, write replacement character and scan to the next start st = esink(replacement_char, sizeof(replacement_char), writer); - for (; i < n_bytes && (utf8[i] & 0x80); ++i) { + for (; i < n_bytes && (utf8[i] & 0x80U); ++i) { } - } else { - i += size - 1; } } @@ -509,7 +505,7 @@ typedef struct { } UriSinkContext; SERD_NODISCARD static size_t -uri_sink(const void* buf, size_t len, void* stream) +uri_sink(const void* const buf, const size_t len, void* const stream) { UriSinkContext* const context = (UriSinkContext*)stream; SerdWriter* const writer = context->writer; @@ -518,7 +514,7 @@ uri_sink(const void* buf, size_t len, void* stream) } SERD_NODISCARD static SerdStatus -write_newline(SerdWriter* writer) +write_newline(SerdWriter* const writer) { SerdStatus st = SERD_SUCCESS; @@ -531,7 +527,7 @@ write_newline(SerdWriter* writer) } SERD_NODISCARD static SerdStatus -write_sep(SerdWriter* writer, const Sep sep) +write_sep(SerdWriter* const writer, const Sep sep) { SerdStatus st = SERD_SUCCESS; const SepRule* const rule = &rules[sep]; @@ -546,10 +542,13 @@ write_sep(SerdWriter* writer, const Sep sep) : 0); } - // If this is the first comma, bump the increment for the following object + // Adjust indentation for object comma if necessary if (sep == SEP_END_O && !writer->context.comma_indented) { ++writer->indent; writer->context.comma_indented = true; + } else if (sep == SEP_END_P && writer->context.comma_indented) { + --writer->indent; + writer->context.comma_indented = false; } // Write newline or space before separator if necessary @@ -575,7 +574,6 @@ write_sep(SerdWriter* writer, const Sep sep) // Reset context and write a blank line after ends of subjects if (sep == SEP_END_S) { writer->indent = writer->context.graph.type ? 1 : 0; - writer->context.predicates = false; writer->context.comma_indented = false; TRY(st, esink("\n", 1, writer)); } @@ -585,7 +583,7 @@ write_sep(SerdWriter* writer, const Sep sep) } static void -free_anon_stack(SerdWriter* writer) +free_anon_stack(SerdWriter* const writer) { while (!serd_stack_is_empty(&writer->anon_stack)) { pop_context(writer); @@ -593,7 +591,7 @@ free_anon_stack(SerdWriter* writer) } static SerdStatus -reset_context(SerdWriter* writer, const unsigned flags) +reset_context(SerdWriter* const writer, const unsigned flags) { free_anon_stack(writer); @@ -608,7 +606,6 @@ reset_context(SerdWriter* writer, const unsigned flags) writer->context.type = CTX_NAMED; writer->context.subject.type = SERD_NOTHING; writer->context.predicate.type = SERD_NOTHING; - writer->context.predicates = false; writer->context.comma_indented = false; return SERD_SUCCESS; } @@ -639,11 +636,11 @@ get_xsd_name(const SerdEnv* const env, const SerdNode* const datatype) } SERD_NODISCARD static SerdStatus -write_literal(SerdWriter* writer, - const SerdNode* node, - const SerdNode* datatype, - const SerdNode* lang, - SerdStatementFlags flags) +write_literal(SerdWriter* const writer, + const SerdNode* const node, + const SerdNode* const datatype, + const SerdNode* const lang, + const SerdStatementFlags flags) { SerdStatus st = SERD_SUCCESS; @@ -679,7 +676,7 @@ write_literal(SerdWriter* writer, // Return true iff `buf` is a valid prefixed name prefix or suffix static bool -is_name(const uint8_t* buf, const size_t len) +is_name(const uint8_t* const buf, const size_t len) { // TODO: This is more strict than it should be for (size_t i = 0; i < len; ++i) { @@ -692,9 +689,9 @@ is_name(const uint8_t* buf, const size_t len) } SERD_NODISCARD static SerdStatus -write_uri_node(SerdWriter* const writer, - const SerdNode* node, - const Field field) +write_uri_node(SerdWriter* const writer, + const SerdNode* const node, + const Field field) { SerdStatus st = SERD_SUCCESS; SerdNode prefix = SERD_NODE_NULL; @@ -741,8 +738,8 @@ write_uri_node(SerdWriter* const writer, serd_uri_parse(node->buf, &uri); SERD_RESTORE_WARNINGS serd_uri_resolve(&uri, &in_base_uri, &abs_uri); - bool rooted = uri_is_under(&writer->base_uri, &writer->root_uri); - SerdURI* root = rooted ? &writer->root_uri : &writer->base_uri; + const bool rooted = uri_is_under(&writer->base_uri, &writer->root_uri); + const SerdURI* root = rooted ? &writer->root_uri : &writer->base_uri; UriSinkContext ctx = {writer, SERD_SUCCESS}; if (!uri_is_under(&abs_uri, root) || writer->syntax == SERD_NTRIPLES || writer->syntax == SERD_NQUADS) { @@ -789,7 +786,7 @@ write_curie(SerdWriter* const writer, const SerdNode* const node) SERD_NODISCARD static SerdStatus write_blank(SerdWriter* const writer, - const SerdNode* node, + const SerdNode* const node, const Field field, const SerdStatementFlags flags) { @@ -828,12 +825,12 @@ write_blank(SerdWriter* const writer, } SERD_NODISCARD static SerdStatus -write_node(SerdWriter* writer, - const SerdNode* node, - const SerdNode* datatype, - const SerdNode* lang, - Field field, - SerdStatementFlags flags) +write_node(SerdWriter* const writer, + const SerdNode* const node, + const SerdNode* const datatype, + const SerdNode* const lang, + const Field field, + const SerdStatementFlags flags) { return (node->type == SERD_LITERAL) ? write_literal(writer, node, datatype, lang, flags) @@ -844,13 +841,15 @@ write_node(SerdWriter* writer, } static bool -is_resource(const SerdNode* node) +is_resource(const SerdNode* const node) { return node->buf && node->type > SERD_LITERAL; } SERD_NODISCARD static SerdStatus -write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred) +write_pred(SerdWriter* const writer, + const SerdStatementFlags flags, + const SerdNode* const pred) { SerdStatus st = SERD_SUCCESS; @@ -858,18 +857,17 @@ write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred) TRY(st, write_sep(writer, SEP_P_O)); copy_node(&writer->context.predicate, pred); - writer->context.predicates = true; writer->context.comma_indented = false; return st; } SERD_NODISCARD static SerdStatus -write_list_next(SerdWriter* writer, - SerdStatementFlags flags, - const SerdNode* predicate, - const SerdNode* object, - const SerdNode* datatype, - const SerdNode* lang) +write_list_next(SerdWriter* const writer, + const SerdStatementFlags flags, + const SerdNode* const predicate, + const SerdNode* const object, + const SerdNode* const datatype, + const SerdNode* const lang) { SerdStatus st = SERD_SUCCESS; @@ -888,7 +886,7 @@ write_list_next(SerdWriter* writer, } SERD_NODISCARD static SerdStatus -terminate_context(SerdWriter* writer) +terminate_context(SerdWriter* const writer) { SerdStatus st = SERD_SUCCESS; @@ -904,14 +902,14 @@ terminate_context(SerdWriter* writer) } SerdStatus -serd_writer_write_statement(SerdWriter* writer, - SerdStatementFlags flags, - const SerdNode* graph, - const SerdNode* subject, - const SerdNode* predicate, - const SerdNode* object, - const SerdNode* datatype, - const SerdNode* lang) +serd_writer_write_statement(SerdWriter* const writer, + SerdStatementFlags flags, + const SerdNode* const graph, + const SerdNode* const subject, + const SerdNode* const predicate, + const SerdNode* const object, + const SerdNode* const datatype, + const SerdNode* const lang) { assert(writer); assert(subject); @@ -920,10 +918,6 @@ serd_writer_write_statement(SerdWriter* writer, SerdStatus st = SERD_SUCCESS; - if (!is_resource(subject) || !is_resource(predicate) || !object->buf) { - return SERD_ERR_BAD_ARG; - } - if ((flags & SERD_LIST_O_BEGIN) && !strcmp((const char*)object->buf, NS_RDF "nil")) { /* Tolerate LIST_O_BEGIN for "()" objects, even though it doesn't make @@ -932,6 +926,17 @@ serd_writer_write_statement(SerdWriter* writer, flags &= (SerdStatementFlags)~SERD_LIST_O_BEGIN; } + // Refuse to write incoherent statements + if (!is_resource(subject) || !is_resource(predicate) || + object->type == SERD_NOTHING || !object->buf || + (datatype && datatype->buf && lang && lang->buf) || + ((flags & SERD_ANON_S_BEGIN) && (flags & SERD_LIST_S_BEGIN)) || + ((flags & SERD_EMPTY_S) && (flags & SERD_LIST_S_BEGIN)) || + ((flags & SERD_ANON_O_BEGIN) && (flags & SERD_LIST_O_BEGIN)) || + ((flags & SERD_EMPTY_O) && (flags & SERD_LIST_O_BEGIN))) { + return SERD_ERR_BAD_ARG; + } + // Simple case: write a line of NTriples or NQuads if (writer->syntax == SERD_NTRIPLES || writer->syntax == SERD_NQUADS) { TRY(st, write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags)); @@ -947,23 +952,21 @@ serd_writer_write_statement(SerdWriter* writer, return SERD_SUCCESS; } - SERD_DISABLE_NULL_WARNINGS - // Separate graphs if necessary - if ((graph && !serd_node_equals(graph, &writer->context.graph)) || - (!graph && writer->context.graph.type)) { + const SerdNode* const out_graph = writer->syntax == SERD_TRIG ? graph : NULL; + if ((out_graph && !serd_node_equals(out_graph, &writer->context.graph)) || + (!out_graph && writer->context.graph.type)) { TRY(st, terminate_context(writer)); reset_context(writer, RESET_GRAPH | RESET_INDENT); TRY(st, write_newline(writer)); - if (graph) { - TRY(st, write_node(writer, graph, datatype, lang, FIELD_GRAPH, flags)); + if (out_graph) { + TRY(st, + write_node(writer, out_graph, datatype, lang, FIELD_GRAPH, flags)); TRY(st, write_sep(writer, SEP_GRAPH_BEGIN)); - copy_node(&writer->context.graph, graph); + copy_node(&writer->context.graph, out_graph); } } - SERD_RESTORE_WARNINGS - if ((flags & SERD_LIST_CONT)) { // Continue a list if (!strcmp((const char*)predicate->buf, NS_RDF "first") && @@ -997,11 +1000,6 @@ serd_writer_write_statement(SerdWriter* writer, } else { // Elide S (write P and O) - if (writer->context.comma_indented) { - --writer->indent; - writer->context.comma_indented = false; - } - const bool first = !writer->context.predicate.type; TRY(st, write_sep(writer, first ? SEP_S_P : SEP_END_P)); TRY(st, write_pred(writer, flags, predicate)); @@ -1046,7 +1044,7 @@ serd_writer_write_statement(SerdWriter* writer, const bool is_list = (flags & SERD_LIST_S_BEGIN); push_context(writer, is_list ? CTX_LIST : CTX_BLANK, - serd_node_copy(graph), + serd_node_copy(out_graph), serd_node_copy(subject), is_list ? SERD_NODE_NULL : serd_node_copy(predicate)); } @@ -1055,7 +1053,7 @@ serd_writer_write_statement(SerdWriter* writer, // Push context for anonymous or list object if necessary push_context(writer, (flags & SERD_LIST_O_BEGIN) ? CTX_LIST : CTX_BLANK, - serd_node_copy(graph), + serd_node_copy(out_graph), serd_node_copy(object), SERD_NODE_NULL); } @@ -1064,7 +1062,7 @@ serd_writer_write_statement(SerdWriter* writer, } SerdStatus -serd_writer_end_anon(SerdWriter* writer, const SerdNode* node) +serd_writer_end_anon(SerdWriter* const writer, const SerdNode* const node) { assert(writer); @@ -1083,19 +1081,16 @@ serd_writer_end_anon(SerdWriter* writer, const SerdNode* node) TRY(st, write_sep(writer, SEP_ANON_END)); pop_context(writer); - SERD_DISABLE_NULL_WARNINGS - if (node && serd_node_equals(node, &writer->context.subject)) { // Now-finished anonymous node is the new subject with no other context writer->context.predicate.type = SERD_NOTHING; } - SERD_RESTORE_WARNINGS return st; } SerdStatus -serd_writer_finish(SerdWriter* writer) +serd_writer_finish(SerdWriter* const writer) { assert(writer); @@ -1107,18 +1102,17 @@ serd_writer_finish(SerdWriter* writer) } SerdWriter* -serd_writer_new(SerdSyntax syntax, - SerdStyle style, - SerdEnv* env, - const SerdURI* base_uri, - SerdSink ssink, - void* stream) +serd_writer_new(const SerdSyntax syntax, + const SerdStyle style, + SerdEnv* const env, + const SerdURI* const base_uri, + SerdSink ssink, + void* const stream) { assert(env); assert(ssink); - const WriteContext context = WRITE_CONTEXT_NULL; - SerdWriter* writer = (SerdWriter*)calloc(1, sizeof(SerdWriter)); + SerdWriter* writer = (SerdWriter*)calloc(1, sizeof(SerdWriter)); writer->syntax = syntax; writer->style = style; @@ -1127,7 +1121,6 @@ serd_writer_new(SerdSyntax syntax, writer->root_uri = SERD_URI_NULL; writer->base_uri = base_uri ? *base_uri : SERD_URI_NULL; writer->anon_stack = serd_stack_new(SERD_PAGE_SIZE); - writer->context = context; writer->byte_sink = serd_byte_sink_new( ssink, stream, (style & SERD_STYLE_BULK) ? SERD_PAGE_SIZE : 1); @@ -1135,9 +1128,9 @@ serd_writer_new(SerdSyntax syntax, } void -serd_writer_set_error_sink(SerdWriter* writer, - SerdErrorSink error_sink, - void* error_handle) +serd_writer_set_error_sink(SerdWriter* const writer, + const SerdErrorSink error_sink, + void* const error_handle) { assert(writer); assert(error_sink); @@ -1146,7 +1139,8 @@ serd_writer_set_error_sink(SerdWriter* writer, } void -serd_writer_chop_blank_prefix(SerdWriter* writer, const uint8_t* prefix) +serd_writer_chop_blank_prefix(SerdWriter* const writer, + const uint8_t* const prefix) { assert(writer); @@ -1163,7 +1157,7 @@ serd_writer_chop_blank_prefix(SerdWriter* writer, const uint8_t* prefix) } SerdStatus -serd_writer_set_base_uri(SerdWriter* writer, const SerdNode* uri) +serd_writer_set_base_uri(SerdWriter* const writer, const SerdNode* const uri) { assert(writer); @@ -1185,7 +1179,7 @@ serd_writer_set_base_uri(SerdWriter* writer, const SerdNode* uri) } SerdStatus -serd_writer_set_root_uri(SerdWriter* writer, const SerdNode* uri) +serd_writer_set_root_uri(SerdWriter* const writer, const SerdNode* const uri) { assert(writer); @@ -1205,9 +1199,9 @@ serd_writer_set_root_uri(SerdWriter* writer, const SerdNode* uri) } SerdStatus -serd_writer_set_prefix(SerdWriter* writer, - const SerdNode* name, - const SerdNode* uri) +serd_writer_set_prefix(SerdWriter* const writer, + const SerdNode* const name, + const SerdNode* const uri) { assert(writer); assert(name); @@ -1218,7 +1212,12 @@ serd_writer_set_prefix(SerdWriter* writer, TRY(st, serd_env_set_prefix(writer->env, name, uri)); if (writer->syntax == SERD_TURTLE || writer->syntax == SERD_TRIG) { + const bool had_subject = writer->context.subject.type; TRY(st, terminate_context(writer)); + if (had_subject) { + TRY(st, esink("\n", 1, writer)); + } + TRY(st, esink("@prefix ", 8, writer)); TRY(st, esink(name->buf, name->n_bytes, writer)); TRY(st, esink(": <", 3, writer)); @@ -1231,15 +1230,13 @@ serd_writer_set_prefix(SerdWriter* writer, } void -serd_writer_free(SerdWriter* writer) +serd_writer_free(SerdWriter* const writer) { if (!writer) { return; } - SERD_DISABLE_NULL_WARNINGS serd_writer_finish(writer); - SERD_RESTORE_WARNINGS free_context(&writer->context); free_anon_stack(writer); serd_stack_free(&writer->anon_stack); @@ -1250,14 +1247,14 @@ serd_writer_free(SerdWriter* writer) } SerdEnv* -serd_writer_get_env(SerdWriter* writer) +serd_writer_get_env(SerdWriter* const writer) { assert(writer); return writer->env; } size_t -serd_file_sink(const void* buf, size_t len, void* stream) +serd_file_sink(const void* const buf, const size_t len, void* const stream) { assert(buf); assert(stream); @@ -1265,7 +1262,7 @@ serd_file_sink(const void* buf, size_t len, void* stream) } size_t -serd_chunk_sink(const void* buf, size_t len, void* stream) +serd_chunk_sink(const void* const buf, const size_t len, void* const stream) { assert(buf); assert(stream); @@ -1281,7 +1278,7 @@ serd_chunk_sink(const void* buf, size_t len, void* stream) } uint8_t* -serd_chunk_sink_finish(SerdChunk* stream) +serd_chunk_sink_finish(SerdChunk* const stream) { assert(stream); serd_chunk_sink("", 1, stream); |