diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | include/serd/buffer.h | 7 | ||||
-rw-r--r-- | include/serd/reader.h | 4 | ||||
-rw-r--r-- | include/serd/stream.h | 38 | ||||
-rw-r--r-- | include/serd/uri.h | 6 | ||||
-rw-r--r-- | include/serd/writer.h | 22 | ||||
-rw-r--r-- | src/byte_sink.h | 18 | ||||
-rw-r--r-- | src/byte_source.c | 2 | ||||
-rw-r--r-- | src/byte_source.h | 4 | ||||
-rw-r--r-- | src/node.c | 21 | ||||
-rw-r--r-- | src/reader.c | 8 | ||||
-rw-r--r-- | src/serdi.c | 5 | ||||
-rw-r--r-- | src/uri.c | 33 | ||||
-rw-r--r-- | src/writer.c | 32 | ||||
-rw-r--r-- | test/test_reader_writer.c | 21 | ||||
-rw-r--r-- | test/test_writer.c | 13 |
16 files changed, 130 insertions, 105 deletions
@@ -1,6 +1,7 @@ serd (1.1.1) unstable; urgency=medium * Add SerdBuffer for mutable buffers to keep SerdChunk const-correct + * Bring read/write interface closer to C standard * Make nodes opaque * Remove SERD_DISABLE_DEPRECATED and SERD_DEPRECATED_BY * Remove serd_uri_to_path() diff --git a/include/serd/buffer.h b/include/serd/buffer.h index 90b51a48..f1bfa897 100644 --- a/include/serd/buffer.h +++ b/include/serd/buffer.h @@ -28,14 +28,15 @@ typedef struct { /** A convenience sink function for writing to a string. - This function can be used as a #SerdSink to write to a SerdBuffer which is - resized as necessary with realloc(). The `stream` parameter must point to + This function can be used as a #SerdWriteFunc to write to a SerdBuffer which + is resized as necessary with realloc(). The `stream` parameter must point to an initialized #SerdBuffer. When the write is finished, the string should be retrieved with serd_buffer_sink_finish(). */ SERD_API size_t serd_buffer_sink(const void* SERD_NONNULL buf, - size_t len, + size_t size, + size_t nmemb, void* SERD_NONNULL stream); /** diff --git a/include/serd/reader.h b/include/serd/reader.h index e4bd9b2f..63c2c5ac 100644 --- a/include/serd/reader.h +++ b/include/serd/reader.h @@ -113,7 +113,7 @@ serd_reader_start_stream(SerdReader* SERD_NONNULL reader, */ SERD_API SerdStatus serd_reader_start_source_stream(SerdReader* SERD_NONNULL reader, - SerdSource SERD_NONNULL read_func, + SerdReadFunc SERD_NONNULL read_func, SerdStreamErrorFunc SERD_NONNULL error_func, void* SERD_NONNULL stream, const char* SERD_NULLABLE name, @@ -143,7 +143,7 @@ serd_reader_read_file_handle(SerdReader* SERD_NONNULL reader, /// Read a user-specified byte source SERD_API SerdStatus serd_reader_read_source(SerdReader* SERD_NONNULL reader, - SerdSource SERD_NONNULL source, + SerdReadFunc SERD_NONNULL source, SerdStreamErrorFunc SERD_NONNULL error, void* SERD_NONNULL stream, const char* SERD_NULLABLE name, diff --git a/include/serd/stream.h b/include/serd/stream.h index 8b023add..992db552 100644 --- a/include/serd/stream.h +++ b/include/serd/stream.h @@ -32,26 +32,38 @@ SERD_BEGIN_DECLS typedef int (*SerdStreamErrorFunc)(void* SERD_NONNULL stream); /** - Source function for raw string input. + Function for reading input bytes from a stream. - Identical semantics to `fread`, but may set errno for more informative error - reporting than supported by SerdStreamErrorFunc. + This has identical semantics to `fread`, but may set `errno` for more + informative error reporting than supported by #SerdStreamErrorFunc. @param buf Output buffer. @param size Size of a single element of data in bytes (always 1). @param nmemb Number of elements to read. @param stream Stream to read from (FILE* for fread). - @return Number of elements (bytes) read. + @return Number of elements (bytes) read, which is short on error. */ -typedef size_t (*SerdSource)(void* SERD_NONNULL buf, - size_t size, - size_t nmemb, - void* SERD_NONNULL stream); - -/// Sink function for raw string output -typedef size_t (*SerdSink)(const void* SERD_NONNULL buf, - size_t len, - void* SERD_NONNULL stream); +typedef size_t (*SerdReadFunc)(void* SERD_NONNULL buf, + size_t size, + size_t nmemb, + void* SERD_NONNULL stream); + +/** + Function for writing output bytes to a stream. + + This has identical semantics to `fwrite`, but may set `errno` for more + informative error reporting than supported by #SerdStreamErrorFunc. + + @param buf Input buffer. + @param size Size of a single element of data in bytes (always 1). + @param nmemb Number of elements to read. + @param stream Stream to write to (FILE* for fread). + @return Number of elements (bytes) written, which is short on error. +*/ +typedef size_t (*SerdWriteFunc)(const void* SERD_NONNULL buf, + size_t size, + size_t nmemb, + void* SERD_NONNULL stream); /** @} diff --git a/include/serd/uri.h b/include/serd/uri.h index 7b5529dc..93409075 100644 --- a/include/serd/uri.h +++ b/include/serd/uri.h @@ -133,9 +133,9 @@ serd_uri_is_within(SerdURIView r, SerdURIView base); @return The number of bytes written. */ SERD_API size_t -serd_write_uri(SerdURIView uri, - SerdSink SERD_NONNULL sink, - void* SERD_NONNULL stream); +serd_write_uri(SerdURIView uri, + SerdWriteFunc SERD_NONNULL sink, + void* SERD_NONNULL stream); /** @} diff --git a/include/serd/writer.h b/include/serd/writer.h index 976ed5b7..94747309 100644 --- a/include/serd/writer.h +++ b/include/serd/writer.h @@ -13,7 +13,6 @@ #include "serd/stream.h" #include "serd/syntax.h" -#include <stddef.h> #include <stdint.h> SERD_BEGIN_DECLS @@ -48,11 +47,11 @@ typedef uint32_t SerdWriterFlags; /// Create a new RDF writer SERD_API SerdWriter* SERD_ALLOCATED -serd_writer_new(SerdSyntax syntax, - SerdWriterFlags flags, - SerdEnv* SERD_NONNULL env, - SerdSink SERD_NONNULL ssink, - void* SERD_NULLABLE stream); +serd_writer_new(SerdSyntax syntax, + SerdWriterFlags flags, + SerdEnv* SERD_NONNULL env, + SerdWriteFunc SERD_NONNULL ssink, + void* SERD_NULLABLE stream); /// Free `writer` SERD_API void @@ -63,17 +62,6 @@ SERD_PURE_API SerdEnv* SERD_NONNULL serd_writer_env(SerdWriter* SERD_NONNULL writer); /** - A convenience sink function for writing to a FILE*. - - This function can be used as a #SerdSink when writing to a FILE*. The - `stream` parameter must be a FILE* opened for writing. -*/ -SERD_API size_t -serd_file_sink(const void* SERD_NONNULL buf, - size_t len, - void* SERD_NONNULL stream); - -/** Set a function to be called when errors occur during writing. The `error_func` will be called with `handle` as its first argument. If diff --git a/src/byte_sink.h b/src/byte_sink.h index 0f3d35f5..d033f227 100644 --- a/src/byte_sink.h +++ b/src/byte_sink.h @@ -13,15 +13,15 @@ #include <string.h> typedef struct SerdByteSinkImpl { - SerdSink sink; - void* stream; - char* buf; - size_t size; - size_t block_size; + SerdWriteFunc sink; + void* stream; + char* buf; + size_t size; + size_t block_size; } SerdByteSink; static inline SerdByteSink -serd_byte_sink_new(SerdSink sink, void* stream, size_t block_size) +serd_byte_sink_new(SerdWriteFunc sink, void* stream, size_t block_size) { SerdByteSink bsink; bsink.sink = sink; @@ -38,7 +38,7 @@ serd_byte_sink_flush(SerdByteSink* bsink) { if (bsink->block_size > 1 && bsink->size > 0) { const size_t size = bsink->size; - const size_t n_out = bsink->sink(bsink->buf, size, bsink->stream); + const size_t n_out = bsink->sink(bsink->buf, 1, bsink->size, bsink->stream); bsink->size = 0; return (n_out != size) ? SERD_BAD_WRITE : SERD_SUCCESS; @@ -63,7 +63,7 @@ serd_byte_sink_write(const void* buf, size_t len, SerdByteSink* bsink) } if (bsink->block_size == 1) { - return bsink->sink(buf, len, bsink->stream); + return bsink->sink(buf, 1, len, bsink->stream); } const size_t orig_len = len; @@ -79,7 +79,7 @@ serd_byte_sink_write(const void* buf, size_t len, SerdByteSink* bsink) // Flush page if buffer is full if (bsink->size == bsink->block_size) { - bsink->sink(bsink->buf, bsink->block_size, bsink->stream); + bsink->sink(bsink->buf, 1, bsink->block_size, bsink->stream); bsink->size = 0; } } diff --git a/src/byte_source.c b/src/byte_source.c index 54b0ad7e..3a2f10b6 100644 --- a/src/byte_source.c +++ b/src/byte_source.c @@ -33,7 +33,7 @@ serd_byte_source_page(SerdByteSource* const source) SerdStatus serd_byte_source_open_source(SerdByteSource* const source, - const SerdSource read_func, + const SerdReadFunc read_func, const SerdStreamErrorFunc error_func, void* const stream, const char* const name, diff --git a/src/byte_source.h b/src/byte_source.h index e8ddb98a..5290dca2 100644 --- a/src/byte_source.h +++ b/src/byte_source.h @@ -21,7 +21,7 @@ typedef struct { } Cursor; typedef struct { - SerdSource read_func; ///< Read function (e.g. fread) + SerdReadFunc read_func; ///< Read function (e.g. fread) SerdStreamErrorFunc error_func; ///< Error function (e.g. ferror) void* stream; ///< Stream (e.g. FILE) size_t page_size; ///< Number of bytes to read at a time @@ -44,7 +44,7 @@ serd_byte_source_open_string(SerdByteSource* source, const char* utf8); SerdStatus serd_byte_source_open_source(SerdByteSource* source, - SerdSource read_func, + SerdReadFunc read_func, SerdStreamErrorFunc error_func, void* stream, const char* name, @@ -56,7 +56,7 @@ static const SerdNodeFlags meta_mask = (SERD_HAS_DATATYPE | SERD_HAS_LANGUAGE); static SerdNode* serd_new_from_uri(SerdURIView uri, SerdURIView base); -SERD_PURE_FUNC static size_t +static size_t serd_uri_string_length(const SerdURIView* const uri) { size_t len = uri->path_prefix.length; @@ -76,12 +76,15 @@ serd_uri_string_length(const SerdURIView* const uri) } static size_t -string_sink(const void* const buf, const size_t len, void* const stream) +string_sink(const void* const buf, + const size_t size, + const size_t nmemb, + void* const stream) { char** ptr = (char**)stream; - memcpy(*ptr, buf, len); - *ptr += len; - return len; + memcpy(*ptr, buf, size * nmemb); + *ptr += size * nmemb; + return nmemb; } SERD_PURE_FUNC static size_t @@ -428,18 +431,18 @@ serd_new_file_uri(const SerdStringView path, const SerdStringView hostname) SerdBuffer buffer = {uri, uri_len}; for (size_t i = 0; i < path.length; ++i) { if (path.data[i] == '%') { - serd_buffer_sink("%%", 2, &buffer); + serd_buffer_sink("%%", 1, 2, &buffer); } else if (is_uri_path_char(path.data[i])) { - serd_buffer_sink(path.data + i, 1, &buffer); + serd_buffer_sink(path.data + i, 1, 1, &buffer); #ifdef _WIN32 } else if (path.data[i] == '\\') { - serd_buffer_sink("/", 1, &buffer); + serd_buffer_sink("/", 1, 1, &buffer); #endif } else { char escape_str[10] = {'%', 0, 0, 0, 0, 0, 0, 0, 0, 0}; snprintf( escape_str + 1, sizeof(escape_str) - 1, "%X", (unsigned)path.data[i]); - serd_buffer_sink(escape_str, 3, &buffer); + serd_buffer_sink(escape_str, 1, 3, &buffer); } } diff --git a/src/reader.c b/src/reader.c index 02738cd2..c7919ca6 100644 --- a/src/reader.c +++ b/src/reader.c @@ -303,7 +303,7 @@ serd_reader_start_stream(SerdReader* const reader, const bool bulk) { return serd_reader_start_source_stream(reader, - bulk ? (SerdSource)fread + bulk ? (SerdReadFunc)fread : serd_file_read_byte, (SerdStreamErrorFunc)ferror, file, @@ -313,7 +313,7 @@ serd_reader_start_stream(SerdReader* const reader, SerdStatus serd_reader_start_source_stream(SerdReader* const reader, - const SerdSource read_func, + const SerdReadFunc read_func, const SerdStreamErrorFunc error_func, void* const stream, const char* const name, @@ -369,7 +369,7 @@ serd_reader_read_file_handle(SerdReader* const reader, const char* const name) { return serd_reader_read_source(reader, - (SerdSource)fread, + (SerdReadFunc)fread, (SerdStreamErrorFunc)ferror, file, name, @@ -378,7 +378,7 @@ serd_reader_read_file_handle(SerdReader* const reader, SerdStatus serd_reader_read_source(SerdReader* const reader, - const SerdSource source, + const SerdReadFunc source, const SerdStreamErrorFunc error, void* const stream, const char* const name, diff --git a/src/serdi.c b/src/serdi.c index cdd3a7b1..e3b41204 100644 --- a/src/serdi.c +++ b/src/serdi.c @@ -11,6 +11,7 @@ #include "serd/reader.h" #include "serd/sink.h" #include "serd/status.h" +#include "serd/stream.h" #include "serd/string_view.h" #include "serd/syntax.h" #include "serd/uri.h" @@ -343,8 +344,8 @@ main(int argc, char** argv) SerdEnv* const env = serd_env_new(base ? serd_node_string_view(base) : serd_empty_string()); - SerdWriter* const writer = - serd_writer_new(output_syntax, writer_flags, env, serd_file_sink, out_fd); + SerdWriter* writer = serd_writer_new( + output_syntax, writer_flags, env, (SerdWriteFunc)fwrite, out_fd); SerdReader* const reader = serd_reader_new(input_syntax, @@ -48,19 +48,19 @@ serd_parse_file_uri(const char* const uri, char** const hostname) for (const char* s = path; *s; ++s) { if (*s == '%') { if (*(s + 1) == '%') { - serd_buffer_sink("%", 1, &buffer); + serd_buffer_sink("%", 1, 1, &buffer); ++s; } else if (is_hexdig(*(s + 1)) && is_hexdig(*(s + 2))) { const uint8_t hi = hex_digit_value((const uint8_t)s[1]); const uint8_t lo = hex_digit_value((const uint8_t)s[2]); const char c = (char)((hi << 4U) | lo); - serd_buffer_sink(&c, 1, &buffer); + serd_buffer_sink(&c, 1, 1, &buffer); s += 2; } else { s += 2; // Junk escape, ignore } } else { - serd_buffer_sink(s, 1, &buffer); + serd_buffer_sink(s, 1, 1, &buffer); } } @@ -398,48 +398,49 @@ serd_uri_is_within(const SerdURIView uri, const SerdURIView base) /// See http://tools.ietf.org/html/rfc3986#section-5.3 size_t -serd_write_uri(const SerdURIView uri, SerdSink sink, void* const stream) +serd_write_uri(const SerdURIView uri, + const SerdWriteFunc sink, + void* const stream) { size_t len = 0; if (uri.scheme.data) { - len += sink(uri.scheme.data, uri.scheme.length, stream); - len += sink(":", 1, stream); + len += sink(uri.scheme.data, 1, uri.scheme.length, stream); + len += sink(":", 1, 1, stream); } if (uri.authority.data) { - len += sink("//", 2, stream); - len += sink(uri.authority.data, uri.authority.length, stream); + len += sink("//", 1, 2, stream); + len += sink(uri.authority.data, 1, uri.authority.length, stream); if (uri.authority.length > 0 && uri_path_len(&uri) > 0 && uri_path_at(&uri, 0) != '/') { // Special case: ensure path begins with a slash // https://tools.ietf.org/html/rfc3986#section-3.2 - len += sink("/", 1, stream); + len += sink("/", 1, 1, stream); } } if (uri.path_prefix.data) { - len += sink(uri.path_prefix.data, uri.path_prefix.length, stream); + len += sink(uri.path_prefix.data, 1, uri.path_prefix.length, stream); } else if (uri.path_prefix.length) { for (size_t i = 0; i < uri.path_prefix.length; ++i) { - len += sink("../", 3, stream); + len += sink("../", 1, 3, stream); } } if (uri.path.data) { - len += sink(uri.path.data, uri.path.length, stream); + len += sink(uri.path.data, 1, uri.path.length, stream); } if (uri.query.data) { - len += sink("?", 1, stream); - len += sink(uri.query.data, uri.query.length, stream); + len += sink("?", 1, 1, stream); + len += sink(uri.query.data, 1, uri.query.length, stream); } if (uri.fragment.data) { // Note that uri.fragment.data includes the leading '#' - len += sink(uri.fragment.data, uri.fragment.length, stream); + len += sink(uri.fragment.data, 1, uri.fragment.length, stream); } - return len; } diff --git a/src/writer.c b/src/writer.c index cea75aec..01beb315 100644 --- a/src/writer.c +++ b/src/writer.c @@ -23,6 +23,7 @@ #include "serd/uri.h" #include "serd/writer.h" +#include <assert.h> #include <errno.h> #include <stdarg.h> #include <stdbool.h> @@ -553,12 +554,15 @@ typedef struct { } UriSinkContext; SERD_NODISCARD static size_t -uri_sink(const void* buf, size_t len, void* stream) +uri_sink(const void* buf, size_t size, size_t nmemb, void* stream) { + (void)size; + assert(size == 1); + UriSinkContext* const context = (UriSinkContext*)stream; SerdWriter* const writer = context->writer; - return write_uri(writer, (const char*)buf, len, &context->status); + return write_uri(writer, (const char*)buf, nmemb, &context->status); } SERD_NODISCARD static SerdStatus @@ -1126,7 +1130,7 @@ SerdWriter* serd_writer_new(SerdSyntax syntax, SerdWriterFlags flags, SerdEnv* env, - SerdSink ssink, + SerdWriteFunc ssink, void* stream) { const WriteContext context = WRITE_CONTEXT_NULL; @@ -1262,27 +1266,27 @@ serd_writer_env(SerdWriter* writer) } size_t -serd_file_sink(const void* buf, size_t len, void* stream) +serd_buffer_sink(const void* const buf, + const size_t size, + const size_t nmemb, + void* const stream) { - return fwrite(buf, 1, len, (FILE*)stream); -} + assert(size == 1); + (void)size; -size_t -serd_buffer_sink(const void* const buf, const size_t len, void* const stream) -{ SerdBuffer* buffer = (SerdBuffer*)stream; - char* new_buf = (char*)realloc((char*)buffer->buf, buffer->len + len); + char* new_buf = (char*)realloc(buffer->buf, buffer->len + nmemb); if (new_buf) { - memcpy(new_buf + buffer->len, buf, len); + memcpy(new_buf + buffer->len, buf, nmemb); buffer->buf = new_buf; - buffer->len += len; + buffer->len += nmemb; } - return len; + return nmemb; } char* serd_buffer_sink_finish(SerdBuffer* const stream) { - serd_buffer_sink("", 1, stream); + serd_buffer_sink("", 1, 1, stream); return (char*)stream->buf; } diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c index 9183af5c..3b656c2e 100644 --- a/test/test_reader_writer.c +++ b/test/test_reader_writer.c @@ -374,21 +374,27 @@ test_read_string(void) } static size_t -faulty_sink(const void* const buf, const size_t len, void* const stream) +faulty_sink(const void* const buf, + const size_t size, + const size_t nmemb, + void* const stream) { (void)buf; - (void)len; + (void)size; + (void)nmemb; + + assert(size == 1); ErrorContext* const ctx = (ErrorContext*)stream; - const size_t new_n_written = ctx->n_written + len; + const size_t new_n_written = ctx->n_written + nmemb; if (new_n_written >= ctx->error_offset) { errno = EINVAL; return 0U; } - ctx->n_written += len; + ctx->n_written += nmemb; errno = 0; - return len; + return nmemb; } static SerdStatus @@ -448,7 +454,8 @@ test_writer(const char* const path) SerdEnv* env = serd_env_new(serd_empty_string()); assert(fd); - SerdWriter* writer = serd_writer_new(SERD_TURTLE, 0, env, serd_file_sink, fd); + SerdWriter* writer = + serd_writer_new(SERD_TURTLE, 0, env, (SerdWriteFunc)fwrite, fd); assert(writer); serd_writer_chop_blank_prefix(writer, "tmp"); @@ -595,7 +602,7 @@ test_reader(const char* path) { size_t n_reads = 0; serd_reader_start_source_stream(reader, - (SerdSource)eof_test_read, + (SerdReadFunc)eof_test_read, (SerdStreamErrorFunc)eof_test_error, &n_reads, NULL, diff --git a/test/test_writer.c b/test/test_writer.c index 76d046d7..0c78495e 100644 --- a/test/test_writer.c +++ b/test/test_writer.c @@ -77,12 +77,15 @@ test_write_long_literal(void) } static size_t -null_sink(const void* const buf, const size_t len, void* const stream) +null_sink(const void* const buf, + const size_t size, + const size_t nmemb, + void* const stream) { (void)buf; (void)stream; - return len; + return size * nmemb; } static void @@ -167,9 +170,13 @@ test_strict_write(void) // Produce a write error without setting errno static size_t -error_sink(const void* const buf, const size_t len, void* const stream) +error_sink(const void* const buf, + const size_t size, + const size_t len, + void* const stream) { (void)buf; + (void)size; (void)len; (void)stream; return 0U; |