aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-06-15 09:26:17 -0400
committerDavid Robillard <d@drobilla.net>2021-03-07 15:32:24 -0500
commitee254d3e92900986526078bb9cbef1642a51dac1 (patch)
tree5e3197d7abbebde216024efec5b7189559ac6b4e
parent9a8e06aa5bdc62ed589bd8ed5789bd059cec0700 (diff)
downloadserd-ee254d3e92900986526078bb9cbef1642a51dac1.tar.gz
serd-ee254d3e92900986526078bb9cbef1642a51dac1.tar.bz2
serd-ee254d3e92900986526078bb9cbef1642a51dac1.zip
Bring read/write interface closer to C standard
-rw-r--r--NEWS1
-rw-r--r--include/serd/serd.h65
-rw-r--r--src/byte_sink.h18
-rw-r--r--src/byte_source.c2
-rw-r--r--src/byte_source.h4
-rw-r--r--src/node.c16
-rw-r--r--src/reader.c4
-rw-r--r--src/serdi.c6
-rw-r--r--src/uri.c31
-rw-r--r--src/writer.c28
-rw-r--r--test/test_reader_writer.c15
11 files changed, 97 insertions, 93 deletions
diff --git a/NEWS b/NEWS
index 021e46d1..f204ae93 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,7 @@
serd (1.0.1) unstable;
* Add SerdBuffer for mutable buffers to keep SerdChunk const-correct
+ * Bring read/write interface closer to C standard
* Make nodes opaque
* Remove serd_uri_to_path()
* Remove useless character counting from API
diff --git a/include/serd/serd.h b/include/serd/serd.h
index 000a8ef8..fcc8fb47 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -363,17 +363,29 @@ typedef int (*SerdStreamErrorFunc)(void* SERD_NONNULL stream);
@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);
+typedef size_t (*SerdReadFunc)(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);
+/**
+ Sink function for raw string output.
+
+ 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);
/**
@}
@@ -475,9 +487,9 @@ serd_uri_is_within(SerdURIView r, SerdURIView base);
*/
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);
/**
@}
@@ -884,7 +896,7 @@ serd_reader_read_file(SerdReader* SERD_NONNULL reader,
SERD_API
SerdStatus
serd_reader_start_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,
@@ -944,11 +956,11 @@ serd_reader_free(SerdReader* SERD_NULLABLE reader);
/// 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
@@ -961,19 +973,7 @@ 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);
-
-/**
- A convenience sink function for writing to a string
+ 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
@@ -983,7 +983,8 @@ serd_file_sink(const void* SERD_NONNULL buf,
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/src/byte_sink.h b/src/byte_sink.h
index d5222843..576f9c2e 100644
--- a/src/byte_sink.h
+++ b/src/byte_sink.h
@@ -26,15 +26,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;
@@ -50,7 +50,7 @@ static inline void
serd_byte_sink_flush(SerdByteSink* bsink)
{
if (bsink->block_size > 1 && bsink->size > 0) {
- bsink->sink(bsink->buf, bsink->size, bsink->stream);
+ bsink->sink(bsink->buf, 1, bsink->size, bsink->stream);
bsink->size = 0;
}
}
@@ -71,7 +71,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;
@@ -87,7 +87,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 594dd785..04836984 100644
--- a/src/byte_source.c
+++ b/src/byte_source.c
@@ -48,7 +48,7 @@ serd_byte_source_page(SerdByteSource* source)
SerdStatus
serd_byte_source_open_source(SerdByteSource* source,
- SerdSource read_func,
+ SerdReadFunc read_func,
SerdStreamErrorFunc error_func,
void* stream,
const char* name,
diff --git a/src/byte_source.h b/src/byte_source.h
index 7a42feaa..f0df9bf3 100644
--- a/src/byte_source.h
+++ b/src/byte_source.h
@@ -32,7 +32,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
@@ -55,7 +55,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,
diff --git a/src/node.c b/src/node.c
index 4728c39e..522a939a 100644
--- a/src/node.c
+++ b/src/node.c
@@ -246,12 +246,12 @@ serd_uri_string_length(const SerdURIView* uri)
}
static size_t
-string_sink(const void* buf, size_t len, void* stream)
+string_sink(const void* buf, size_t size, size_t nmemb, void* stream)
{
char** ptr = (char**)stream;
- memcpy(*ptr, buf, len);
- *ptr += len;
- return len;
+ memcpy(*ptr, buf, size * nmemb);
+ *ptr += size * nmemb;
+ return nmemb;
}
SerdNode*
@@ -369,16 +369,16 @@ serd_new_file_uri(const SerdStringView path, const SerdStringView hostname)
SerdBuffer buffer = {uri, uri_len};
for (size_t i = 0; i < path.len; ++i) {
if (is_windows && path.buf[i] == '\\') {
- serd_buffer_sink("/", 1, &buffer);
+ serd_buffer_sink("/", 1, 1, &buffer);
} else if (path.buf[i] == '%') {
- serd_buffer_sink("%%", 2, &buffer);
+ serd_buffer_sink("%%", 1, 2, &buffer);
} else if (is_uri_path_char(path.buf[i])) {
- serd_buffer_sink(path.buf + i, 1, &buffer);
+ serd_buffer_sink(path.buf + i, 1, 1, &buffer);
} 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.buf[i]);
- serd_buffer_sink(escape_str, 3, &buffer);
+ serd_buffer_sink(escape_str, 1, 3, &buffer);
}
}
serd_buffer_sink_finish(&buffer);
diff --git a/src/reader.c b/src/reader.c
index 194c295b..0c4ff5e2 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -275,7 +275,7 @@ serd_reader_read_file(SerdReader* reader, const char* uri)
}
SerdStatus st = serd_reader_start_stream(reader,
- (SerdSource)fread,
+ (SerdReadFunc)fread,
(SerdStreamErrorFunc)ferror,
fd,
path,
@@ -312,7 +312,7 @@ skip_bom(SerdReader* me)
SerdStatus
serd_reader_start_stream(SerdReader* reader,
- SerdSource read_func,
+ SerdReadFunc read_func,
SerdStreamErrorFunc error_func,
void* stream,
const char* name,
diff --git a/src/serdi.c b/src/serdi.c
index 0e59c5ee..62d1e44a 100644
--- a/src/serdi.c
+++ b/src/serdi.c
@@ -336,8 +336,8 @@ main(int argc, char** argv)
FILE* const out_fd = stdout;
SerdEnv* const env = serd_env_new(serd_node_string_view(base));
- 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,
@@ -365,7 +365,7 @@ main(int argc, char** argv)
st = serd_reader_start_string(reader, input);
} else {
st = serd_reader_start_stream(reader,
- bulk_read ? (SerdSource)fread
+ bulk_read ? (SerdReadFunc)fread
: serd_file_read_byte,
(SerdStreamErrorFunc)ferror,
in_fd,
diff --git a/src/uri.c b/src/uri.c
index 88e1bd1e..780f7e72 100644
--- a/src/uri.c
+++ b/src/uri.c
@@ -57,18 +57,18 @@ serd_parse_file_uri(const char* uri, char** 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 char code[3] = {*(s + 1), *(s + 2), 0};
const char c = (char)strtoul((const char*)code, NULL, 16);
- 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);
}
}
@@ -415,48 +415,47 @@ 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* stream)
+serd_write_uri(const SerdURIView uri, SerdWriteFunc sink, void* stream)
{
size_t len = 0;
if (uri.scheme.buf) {
- len += sink(uri.scheme.buf, uri.scheme.len, stream);
- len += sink(":", 1, stream);
+ len += sink(uri.scheme.buf, 1, uri.scheme.len, stream);
+ len += sink(":", 1, 1, stream);
}
if (uri.authority.buf) {
- len += sink("//", 2, stream);
- len += sink(uri.authority.buf, uri.authority.len, stream);
+ len += sink("//", 1, 2, stream);
+ len += sink(uri.authority.buf, 1, uri.authority.len, stream);
if (uri.authority.len > 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.buf) {
- len += sink(uri.path_prefix.buf, uri.path_prefix.len, stream);
+ len += sink(uri.path_prefix.buf, 1, uri.path_prefix.len, stream);
} else if (uri.path_prefix.len) {
for (size_t i = 0; i < uri.path_prefix.len; ++i) {
- len += sink("../", 3, stream);
+ len += sink("../", 1, 3, stream);
}
}
if (uri.path.buf) {
- len += sink(uri.path.buf, uri.path.len, stream);
+ len += sink(uri.path.buf, 1, uri.path.len, stream);
}
if (uri.query.buf) {
- len += sink("?", 1, stream);
- len += sink(uri.query.buf, uri.query.len, stream);
+ len += sink("?", 1, 1, stream);
+ len += sink(uri.query.buf, 1, uri.query.len, stream);
}
if (uri.fragment.buf) {
// Note that uri.fragment.buf includes the leading `#'
- len += sink(uri.fragment.buf, uri.fragment.len, stream);
+ len += sink(uri.fragment.buf, 1, uri.fragment.len, stream);
}
-
return len;
}
diff --git a/src/writer.c b/src/writer.c
index 75734c0c..c70c5f1d 100644
--- a/src/writer.c
+++ b/src/writer.c
@@ -434,9 +434,11 @@ write_text(SerdWriter* writer,
}
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)
{
- return write_uri((SerdWriter*)stream, (const char*)buf, len);
+ (void)size;
+ assert(size == 1);
+ return write_uri((SerdWriter*)stream, (const char*)buf, nmemb);
}
static void
@@ -956,7 +958,7 @@ SerdWriter*
serd_writer_new(SerdSyntax syntax,
SerdWriterFlags flags,
SerdEnv* env,
- SerdSink ssink,
+ SerdWriteFunc ssink,
void* stream)
{
const WriteContext context = WRITE_CONTEXT_NULL;
@@ -1088,25 +1090,21 @@ serd_writer_env(SerdWriter* writer)
}
size_t
-serd_file_sink(const void* buf, size_t len, void* stream)
+serd_buffer_sink(const void* buf, size_t size, size_t nmemb, void* stream)
{
- return fwrite(buf, 1, len, (FILE*)stream);
-}
+ assert(size == 1);
+ (void)size;
-size_t
-serd_buffer_sink(const void* buf, size_t len, void* stream)
-{
SerdBuffer* buffer = (SerdBuffer*)stream;
-
- buffer->buf = (char*)realloc(buffer->buf, buffer->len + len);
- memcpy((uint8_t*)buffer->buf + buffer->len, buf, len);
- buffer->len += len;
- return len;
+ buffer->buf = (char*)realloc(buffer->buf, buffer->len + nmemb);
+ memcpy((uint8_t*)buffer->buf + buffer->len, buf, nmemb);
+ buffer->len += nmemb;
+ return nmemb;
}
char*
serd_buffer_sink_finish(SerdBuffer* 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 48bb508e..91e95806 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -89,7 +89,7 @@ test_read_chunks(void)
assert(f);
SerdStatus st = serd_reader_start_stream(
- reader, (SerdSource)fread, (SerdStreamErrorFunc)ferror, f, NULL, 1);
+ reader, (SerdReadFunc)fread, (SerdStreamErrorFunc)ferror, f, NULL, 1);
assert(st == SERD_SUCCESS);
// Write two statement separated by null characters
@@ -163,7 +163,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");
@@ -292,8 +293,12 @@ test_reader(const char* path)
fflush(temp);
fseek(temp, 0L, SEEK_SET);
- serd_reader_start_stream(
- reader, (SerdSource)fread, (SerdStreamErrorFunc)ferror, temp, NULL, 4096);
+ serd_reader_start_stream(reader,
+ (SerdReadFunc)fread,
+ (SerdStreamErrorFunc)ferror,
+ temp,
+ NULL,
+ 4096);
assert(serd_reader_read_chunk(reader) == SERD_SUCCESS);
assert(serd_reader_read_chunk(reader) == SERD_FAILURE);
@@ -307,7 +312,7 @@ test_reader(const char* path)
{
size_t n_reads = 0;
serd_reader_start_stream(reader,
- (SerdSource)eof_test_read,
+ (SerdReadFunc)eof_test_read,
(SerdStreamErrorFunc)eof_test_error,
&n_reads,
NULL,