>From 8ac66216adada0dd45c3a29caaf7a4e63bb2d626 Mon Sep 17 00:00:00 2001 From: Alessio Vanni Date: Thu, 14 May 2020 16:03:10 +0200 Subject: [PATCH] Improved BIO API Also small changes to GNUNET_Buffer to account for BIO's new features. --- src/include/gnunet_bio_lib.h | 550 +++++++++++++--- src/include/gnunet_buffer_lib.h | 13 + src/util/bio.c | 1068 +++++++++++++++++++++++++++---- src/util/buffer.c | 23 +- 4 files changed, 1422 insertions(+), 232 deletions(-) diff --git a/src/include/gnunet_bio_lib.h b/src/include/gnunet_bio_lib.h index 2f715ec97..40a554b98 100644 --- a/src/include/gnunet_bio_lib.h +++ b/src/include/gnunet_bio_lib.h @@ -42,6 +42,8 @@ extern "C" #endif #endif +/****************************** READING API *******************************/ + /** * Handle for buffered reading. */ @@ -55,11 +57,22 @@ struct GNUNET_BIO_ReadHandle; * @return IO handle on success, NULL on error */ struct GNUNET_BIO_ReadHandle * -GNUNET_BIO_read_open (const char *fn); +GNUNET_BIO_read_open_file (const char *fn); + + +/** + * Create a handle from an existing allocated buffer. + * + * @param buffer the buffer to use as source + * @param size the total size in bytes of the buffer + * @return IO handle on sucess, NULL on error + */ +struct GNUNET_BIO_ReadHandle * +GNUNET_BIO_read_open_buffer (void *buffer, size_t size); /** - * Close an open file. Reports if any errors reading + * Close an open handle. Reports if any errors reading * from the file were encountered. * * @param h file handle @@ -71,51 +84,40 @@ GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg); /** - * Read the contents of a binary file into a buffer. + * Read some contents into a buffer. * - * @param h handle to an open file + * @param h the IO handle to read from * @param what describes what is being read (for error message creation) * @param result the buffer to write the result to * @param len the number of bytes to read * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ int -GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, const char *what, - void *result, size_t len); +GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *result, + size_t len); /** - * Read the contents of a binary file into a buffer. + * Read 0-terminated string. * - * @param h handle to an open file - * @param file name of the source file - * @param line line number in the source file - * @param result the buffer to write the result to - * @param len the number of bytes to read - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure - */ -int -GNUNET_BIO_read_fn (struct GNUNET_BIO_ReadHandle *h, - const char *file, int line, - void *result, size_t len); - -/** - * Read 0-terminated string from a file. - * - * @param h handle to an open file + * @param h the IO handle to read from * @param what describes what is being read (for error message creation) - * @param result the buffer to store a pointer to the (allocated) string to + * @param result where to store the pointer to the (allocated) string * (note that *result could be set to NULL as well) * @param max_length maximum allowed length for the string * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ int -GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, const char *what, - char **result, size_t max_length); +GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, + const char *what, + char **result, + size_t max_length); /** - * Read metadata container from a file. + * Read a metadata container. * * @param h handle to an open file * @param what describes what is being read (for error message creation) @@ -123,79 +125,65 @@ GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, const char *what, * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ int -GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h, const char *what, +GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h, + const char *what, struct GNUNET_CONTAINER_MetaData **result); /** * Read a float. * - * @param h hande to open file + * @param h the IO handle to read from + * @param what describes what is being read (for error message creation) * @param f address of float to read */ -#define GNUNET_BIO_read_float(h, f) (GNUNET_BIO_read_fn (h, __FILE__, __LINE__, \ - f, sizeof(float))) +#define GNUNET_BIO_read_float(h, what, f) \ + (GNUNET_BIO_read (h, what, f, sizeof(float))) /** * Read a double. * - * @param h hande to open file + * @param h the IO handle to read from + * @param what describes what is being read (for error message creation) * @param f address of double to read */ -#define GNUNET_BIO_read_double(h, f) (GNUNET_BIO_read_fn (h, __FILE__, __LINE__, \ - f, sizeof(double))) +#define GNUNET_BIO_read_double(h, what, f) \ + (GNUNET_BIO_read (h, what, f, sizeof(double))) + /** * Read an (u)int32_t. * - * @param h hande to open file - * @param file name of the source file - * @param line line number in the code - * @param i address of 32-bit integer to read + * @param h the IO handle to read from + * @param what describes what is being read (for error message creation) + * @param i where to store the data * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_read_int32__ (struct GNUNET_BIO_ReadHandle *h, const char *file, - int line, int32_t *i); +GNUNET_BIO_read_int32 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + int32_t *i); -/** - * Read an (u)int32_t. - * - * @param h hande to open file - * @param i address of 32-bit integer to read - */ -#define GNUNET_BIO_read_int32(h, i) GNUNET_BIO_read_int32__ (h, __FILE__, \ - __LINE__, \ - (int32_t *) i) - /** * Read an (u)int64_t. * - * @param h hande to open file - * @param file name of the source file - * @param line line number in the code - * @param i address of 64-bit integer to read + * @param h the IO handle to read from + * @param what describes what is being read (for error message creation) + * @param i where to store the data * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_read_int64__ (struct GNUNET_BIO_ReadHandle *h, const char *file, - int line, int64_t *i); +GNUNET_BIO_read_int64 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + int64_t *i); -/** - * Read an (u)int64_t. - * - * @param h hande to open file - * @param i address of 64-bit integer to read - */ -#define GNUNET_BIO_read_int64(h, i) GNUNET_BIO_read_int64__ (h, __FILE__, \ - __LINE__, \ - (int64_t *) i) +/****************************** WRITING API *******************************/ /** * Handle for buffered writing. @@ -205,108 +193,478 @@ struct GNUNET_BIO_WriteHandle; /** * Open a file for writing. * - * @param fn file name to be opened + * @param fn name of the file to be opened * @return IO handle on success, NULL on error */ struct GNUNET_BIO_WriteHandle * -GNUNET_BIO_write_open (const char *fn); +GNUNET_BIO_write_open_file (const char *fn); /** - * Close an open file for writing. + * Create a handle backed by an in-memory buffer. * - * @param h file handle - * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + * @return IO handle on success, NULL on error + */ +struct GNUNET_BIO_WriteHandle * +GNUNET_BIO_write_open_buffer (void); + + +/** + * Force a file-based buffered writer to flush its buffer. + * If the handle does not use a file, this function returs #GNUNET_OK + * without doing anything. + * + * @param h the IO handle + * @return #GNUNET_OK upon success. Upon failure #GNUNET_SYSERR is returned + * and the file is closed */ int -GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h); +GNUNET_BIO_flush (struct GNUNET_BIO_WriteHandle *h); /** - * Write a buffer to a file. + * Get the IO handle's contents. + * If the handle doesn't use an in-memory buffer, this function returns + * #GNUNET_SYSERR. * - * @param h handle to open file - * @param buffer the data to write - * @param n number of bytes to write - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @param h the IO handle + * @param emsg set to the (allocated) error message + * if the handle has an error message the return value is #GNUNET_SYSERR + * @param contents where to store the pointer to the handle's contents + * @param size where to store the size of @e contents + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int -GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, const void *buffer, - size_t n); +GNUNET_BIO_get_buffer_contents (struct GNUNET_BIO_WriteHandle *h, + char **emsg, + void **contents, + size_t *size); + + +/** + * Close an IO handle. + * If the handle was using a file, the file will be closed. + * + * @param h file handle + * @param emsg set to the (allocated) error message + * if the handle has an error message, the return value is #GNUNET_SYSERR + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h, char **emsg); /** - * Force a buffered writer to flush its buffer + * Write a buffer to a handle. * - * @param h the writer handle - * @return #GNUNET_OK upon success. Upon failure #GNUNET_SYSERR is returned and - * the file is closed + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param buffer the data to write + * @param n number of bytes to write + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_flush (struct GNUNET_BIO_WriteHandle *h); +GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const void *buffer, + size_t n); /** - * Write a string to a file. + * Write a 0-terminated string. * - * @param h handle to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param s string to write (can be NULL) * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h, const char *s); +GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const char *s); /** - * Write metadata container to a file. + * Write a metadata container. * - * @param h handle to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param m metadata to write * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h, + const char *what, const struct GNUNET_CONTAINER_MetaData *m); /** * Write a float. * - * @param h hande to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param f float to write (must be a variable) */ -#define GNUNET_BIO_write_float(h, f) GNUNET_BIO_write (h, &f, sizeof(float)) +#define GNUNET_BIO_write_float(h, what, f) \ + GNUNET_BIO_write (h, what, &f, sizeof(float)) /** * Write a double. * - * @param h hande to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param f double to write (must be a variable) */ -#define GNUNET_BIO_write_double(h, f) GNUNET_BIO_write (h, &f, sizeof(double)) +#define GNUNET_BIO_write_double(h, what, f) \ + GNUNET_BIO_write (h, what, &f, sizeof(double)) /** * Write an (u)int32_t. * - * @param h hande to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param i 32-bit integer to write * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, int32_t i); +GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int32_t i); /** * Write an (u)int64_t. * - * @param h hande to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param i 64-bit integer to write * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, int64_t i); +GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int64_t i); + + +/****************************** READ SPEC API ***************************/ + + +/** + * Function used to deserialize data read from @a h and store it into @a + * target. + * + * @param cls closure (can be NULL) + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param target where to store the data + * @param target_size how many bytes can be written in @a target + * can be 0 if the size is unknown or is not fixed + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +typedef int +(*GNUNET_BIO_ReadHandler)(void *cls, + struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *target, + size_t target_size); + + +/** + * Structure specifying a reading operation on an IO handle. + */ +struct GNUNET_BIO_ReadSpec +{ + /** + * Function performing data deserialization. + */ + GNUNET_BIO_ReadHandler rh; + + /** + * Closure for @e rh. Can be NULL. + */ + void *cls; + + /** + * What is being read (for error message creation) + */ + const char *what; + + /** + * Destination buffer. Can also be a pointer to a pointer, especially for + * dynamically allocated structures. + */ + void *target; + + /** + * Size of @e target. Can be 0 if unknown or not fixed. + */ + size_t size; +}; + + +/** + * End of specifications marker. + */ +#define GNUNET_BIO_read_spec_end() \ + { NULL, NULL, NULL, NULL, 0 } + + +/** + * Create the specification to read a certain amount of bytes. + * + * @param what describes what is being read (for error message creation) + * @param result the buffer to write the result to + * @param len the number of bytes to read + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_object (const char *what, + void *result, + size_t size); + + +/** + * Create the specification to read a 0-terminated string. + * + * @param what describes what is being read (for error message creation) + * @param result where to store the pointer to the (allocated) string + * (note that *result could be set to NULL as well) + * @param max_length maximum allowed length for the string + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_string (const char *what, + char **result, + size_t max_length); + + +/** + * Create the specification to read a metadata container. + * + * @param what describes what is being read (for error message creation) + * @param result the buffer to store a pointer to the (allocated) metadata + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_meta_data (const char *what, + struct GNUNET_CONTAINER_MetaData **result); + + +/** + * Create the specification to read an (u)int32_t. + * + * @param what describes what is being read (for error message creation) + * @param i where to store the data + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_int32 (const char *what, + int32_t *i); + + +/** + * Create the specification to read an (u)int64_t. + * + * @param what describes what is being read (for error message creation) + * @param i where to store the data + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_int64 (const char *what, + int64_t *i); + + +/** + * Create the specification to read a float. + * + * @param what describes what is being read (for error message creation) + * @param f address of float to read + */ +#define GNUNET_BIO_read_spec_float(what, f) \ + GNUNET_BIO_read_spec_object (what, f, sizeof (float)) + + +/** + * Create the specification to read a double. + * + * @param what describes what is being read (for error message creation) + * @param f address of double to read + */ +#define GNUNET_BIO_read_spec_double(what, f) \ + GNUNET_BIO_read_spec_object (what, f, sizeof (double)) + + +/** + * Execute the read specifications in order. + * + * @param h the IO handle to read from + * @param rs array of read specs + * the last element must be #GNUNET_BIO_read_spec_end + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +GNUNET_BIO_read_spec_commit (struct GNUNET_BIO_ReadHandle *h, + struct GNUNET_BIO_ReadSpec *rs); + + +/******************************* WRITE SPEC API *****************************/ + + +/** + * Function used to serialize data from a buffer and write it to @a h. + * + * @param cls closure (can be NULL) + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the data to write + * @param source_size how many bytes should be written + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +typedef int +(*GNUNET_BIO_WriteHandler) (void *cls, + struct GNUNET_BIO_WriteHandle *h, + const char *what, + void *source, + size_t source_size); + + +/** + * Structure specifying a writing operation on an IO handle. + */ +struct GNUNET_BIO_WriteSpec +{ + /** + * Function performing data serialization. + */ + GNUNET_BIO_WriteHandler wh; + + /** + * Closure for @e rh. Can be NULL. + */ + void *cls; + + /** + * What is being read (for error message creation) + */ + const char *what; + + /** + * Source buffer. The data in this buffer will be written to the handle. + */ + void *source; + + /** + * Size of @e source. If it's smaller than the real size of @e source, only + * this many bytes will be written. + */ + size_t source_size; +}; + + +/** + * End of specifications marker. + */ +#define GNUNET_BIO_write_spec_end() \ + { NULL, NULL, NULL, NULL, 0 } + + +/** + * Create the specification to read some bytes. + * + * @param what describes what is being written (for error message creation) + * @param source the data to write + * @param size how many bytes should be written + * @return the write spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_object (const char *what, + void *source, + size_t size); + + +/** + * Create the specification to write a 0-terminated string. + * + * @param what describes what is being read (for error message creation) + * @param s string to write (can be NULL) + * @return the read spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_string (const char *what, + const char *s); + + +/** + * Create the specification to write a metadata container. + * + * @param what what is being written (for error message creation) + * @param m metadata to write + * @return the write spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_meta_data (const char *what, + const struct GNUNET_CONTAINER_MetaData *m); + + +/** + * Create the specification to write an (u)int32_t. + * + * @param what describes what is being written (for error message creation) + * @param i pointer to a 32-bit integer + * @return the write spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_int32 (const char *what, + int32_t *i); + + +/** + * Create the specification to write an (u)int64_t. + * + * @param what describes what is being written (for error message creation) + * @param i pointer to a 64-bit integer + * @return the write spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_int64 (const char *what, + int64_t *i); + + +/** + * Create the specification to write a float. + * + * @param what describes what is being written (for error message creation) + * @param f pointer to a float + * @return the write spec + */ +#define GNUNET_BIO_write_spec_float(what, f) \ + GNUNET_BIO_write_spec_object (what, f, sizeof (float)) + + +/** + * Create the specification to write an double. + * + * @param what describes what is being written (for error message creation) + * @param f pointer to a double + * @return the write spec + */ +#define GNUNET_BIO_write_spec_double(what, f) \ + GNUNET_BIO_write_spec_object (what, f, sizeof (double)) + + +/** + * Execute the write specifications in order. + * + * @param h the IO handle to write to + * @param ws array of write specs + * the last element must be #GNUNET_BIO_write_spec_end + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +GNUNET_BIO_write_spec_commit (struct GNUNET_BIO_WriteHandle *h, + struct GNUNET_BIO_WriteSpec *ws); #if 0 /* keep Emacsens' auto-indent happy */ diff --git a/src/include/gnunet_buffer_lib.h b/src/include/gnunet_buffer_lib.h index c0ae06d77..e23536ab2 100644 --- a/src/include/gnunet_buffer_lib.h +++ b/src/include/gnunet_buffer_lib.h @@ -164,6 +164,19 @@ char * GNUNET_buffer_reap_str (struct GNUNET_Buffer *buf); +/** + * Clear the buffer and return its contents. + * The caller is responsible to eventually #GNUNET_free + * the returned data. + * + * @param buf the buffer to reap the contents from + * @param size where to store the size of the returned data + * @returns the data contained in the string + */ +void * +GNUNET_buffer_reap (struct GNUNET_Buffer *buf, size_t *size); + + /** * Free the backing memory of the given buffer. * Does not free the memory of the buffer control structure, diff --git a/src/util/bio.c b/src/util/bio.c index e05258f73..8580d97c4 100644 --- a/src/util/bio.c +++ b/src/util/bio.c @@ -47,13 +47,38 @@ #define MAX_META_DATA (1024 * 1024) +/** + * Enum used internally to know how buffering is handled. + * + * The idea is that by using an enum, BIO can be extended to support other + * kinds of "backend" for buffering (or just formatted I/O.) + */ +enum IOType +{ + /** + * The handle uses a file to read/write data. + */ + IO_FILE = 0, + + /** + * The data is stored entirely in memory. + */ + IO_BUFFER, +}; + + /** * Handle for buffered reading. */ struct GNUNET_BIO_ReadHandle { /** - * Underlying file abstraction. + * The "backend" type. + */ + enum IOType type; + + /** + * Handle to a file on disk, if @e type is #IO_FILE. */ struct GNUNET_DISK_FileHandle *fd; @@ -63,12 +88,12 @@ struct GNUNET_BIO_ReadHandle char *emsg; /** - * I/O buffer. Allocated at the end of the struct, do not free! + * I/O buffer. Do @b not free! */ char *buffer; /** - * Number of bytes available in read @e buffer. + * Number of bytes available in @e buffer. */ size_t have; @@ -91,7 +116,7 @@ struct GNUNET_BIO_ReadHandle * @return IO handle on success, NULL on error */ struct GNUNET_BIO_ReadHandle * -GNUNET_BIO_read_open (const char *fn) +GNUNET_BIO_read_open_file (const char *fn) { struct GNUNET_DISK_FileHandle *fd; struct GNUNET_BIO_ReadHandle *h; @@ -100,6 +125,7 @@ GNUNET_BIO_read_open (const char *fn) if (NULL == fd) return NULL; h = GNUNET_malloc (sizeof(struct GNUNET_BIO_ReadHandle) + BIO_BUFFER_SIZE); + h->type = IO_FILE; h->buffer = (char *) &h[1]; h->size = BIO_BUFFER_SIZE; h->fd = fd; @@ -108,11 +134,32 @@ GNUNET_BIO_read_open (const char *fn) /** - * Close an open file. Reports if any errors reading + * Create a handle from an existing allocated buffer. + * + * @param buffer the buffer to use as source + * @param size the total size in bytes of the buffer + * @return IO handle on sucess, NULL on error + */ +struct GNUNET_BIO_ReadHandle * +GNUNET_BIO_read_open_buffer (void *buffer, size_t size) +{ + struct GNUNET_BIO_ReadHandle *h; + + h = GNUNET_new (struct GNUNET_BIO_ReadHandle); + h->type = IO_BUFFER; + h->buffer = buffer; + h->size = size; + return h; +} + + +/** + * Close an open handle. Reports if any errors reading * from the file were encountered. * * @param h file handle - * @param emsg set to the error message + * @param emsg set to the (allocated) error message + * if the handle has an error message, the return value is #GNUNET_SYSERR * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int @@ -121,60 +168,63 @@ GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg) int err; err = (NULL == h->emsg) ? GNUNET_OK : GNUNET_SYSERR; - if (emsg != NULL) + if (NULL != emsg) *emsg = h->emsg; else GNUNET_free_non_null (h->emsg); - GNUNET_DISK_file_close (h->fd); + switch (h->type) + { + case IO_FILE: + GNUNET_DISK_file_close (h->fd); + break; + case IO_BUFFER: + break; + default: + break; + } GNUNET_free (h); return err; } /** - * Read the contents of a binary file into a buffer. + * Function used internally to read the contents of a file into a buffer. * - * @param h handle to an open file + * @param h the IO handle to read from * @param what describes what is being read (for error message creation) - * @param result the buffer to write the result to + * @param result the buffer to write the data to * @param len the number of bytes to read - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ -int -GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, - const char *what, - void *result, - size_t len) +static int +read_from_file (struct GNUNET_BIO_ReadHandle *h, + const char *what, + char *result, + size_t len) { - char *dst = result; + size_t pos = 0; size_t min; - size_t pos; ssize_t ret; - if (NULL != h->emsg) - return GNUNET_SYSERR; - pos = 0; do { - /* first, use buffer */ min = h->have - h->pos; - if (min > 0) + if (0 < min) { - if (min > len - pos) + if (len - pos < min) min = len - pos; - GNUNET_memcpy (&dst[pos], &h->buffer[h->pos], min); + GNUNET_memcpy (&result[pos], &h->buffer[h->pos], min); h->pos += min; pos += min; } - if (pos == len) - return GNUNET_OK; /* done! */ + if (len == pos) + return GNUNET_OK; GNUNET_assert (((off_t) h->have) == h->pos); - /* fill buffer */ ret = GNUNET_DISK_file_read (h->fd, h->buffer, h->size); if (-1 == ret) { GNUNET_asprintf (&h->emsg, - _ ("Error reading `%s': %s"), + _ ("Error reading `%s' from file: %s"), what, strerror (errno)); return GNUNET_SYSERR; @@ -182,7 +232,7 @@ GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, if (0 == ret) { GNUNET_asprintf (&h->emsg, - _ ("Error reading `%s': %s"), + _ ("Error reading `%s' from file: %s"), what, _ ("End of file")); return GNUNET_SYSERR; @@ -190,41 +240,84 @@ GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, h->pos = 0; h->have = ret; } - while (pos < len); /* should always be true */ + while (pos < len); return GNUNET_OK; } /** - * Read the contents of a binary file into a buffer. + * Function used internally to read the content of a buffer into a buffer. * - * @param h handle to an open file - * @param file name of the source file - * @param line line number in the source file + * @param h the IO handle to read from + * @param what describes what is being read (for error message creation) + * @param result the buffer to write the result to + * @param len the number of bytes to read + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +read_from_buffer (struct GNUNET_BIO_ReadHandle *h, + const char *what, + char *result, + size_t len) +{ + if (h->size < len || h->size - h->pos < len) + { + GNUNET_asprintf (&h->emsg, + _ ("Error while reading `%s' from buffer: %s"), + what, + _ ("Not enough data left")); + return GNUNET_SYSERR; + } + GNUNET_memcpy (result, h->buffer + h->pos, len); + h->pos += len; + return GNUNET_OK; +} + + +/** + * Read some contents into a buffer. + * + * @param h the IO handle to read from + * @param what describes what is being read (for error message creation) * @param result the buffer to write the result to * @param len the number of bytes to read * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ int -GNUNET_BIO_read_fn (struct GNUNET_BIO_ReadHandle *h, - const char *file, - int line, - void *result, - size_t len) +GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *result, + size_t len) { - char what[PATH_MAX + 1024]; + char *dst = result; + + if (NULL != h->emsg) + return GNUNET_SYSERR; - GNUNET_snprintf (what, sizeof(what), "%s:%d", file, line); - return GNUNET_BIO_read (h, what, result, len); + if (0 == len) + return GNUNET_OK; + + switch (h->type) + { + case IO_FILE: + return read_from_file (h, what, dst, len); + case IO_BUFFER: + return read_from_buffer (h, what, dst, len); + default: + GNUNET_asprintf (&h->emsg, + _ ("Invalid handle type while reading `%s'"), + what); + return GNUNET_SYSERR; + } } /** - * Read 0-terminated string from a file. + * Read 0-terminated string. * - * @param h handle to an open file + * @param h the IO handle to read from * @param what describes what is being read (for error message creation) - * @param result the buffer to store a pointer to the (allocated) string to + * @param result where to store the pointer to the (allocated) string * (note that *result could be set to NULL as well) * @param max_length maximum allowed length for the string * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure @@ -238,10 +331,21 @@ GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, char *buf; uint32_t big; - if (GNUNET_OK != GNUNET_BIO_read_int32 (h, &big)) + if (GNUNET_OK != GNUNET_BIO_read_int32 (h, + _ ("string length"), + (int32_t *) &big)) { - GNUNET_free_non_null (h->emsg); - GNUNET_asprintf (&h->emsg, _ ("Error reading length of string `%s'"), what); + char *tmp = h->emsg; + if (NULL != tmp) + GNUNET_asprintf (&h->emsg, + _ ("%s (while reading `%s')"), + tmp, + what); + else + GNUNET_asprintf (&h->emsg, + _ ("Error reading length of string `%s'"), + what); + GNUNET_free_non_null (tmp); return GNUNET_SYSERR; } if (0 == big) @@ -274,7 +378,7 @@ GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, /** - * Read metadata container from a file. + * Read a metadata container. * * @param h handle to an open file * @param what describes what is being read (for error message creation) @@ -290,20 +394,23 @@ GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h, char *buf; struct GNUNET_CONTAINER_MetaData *meta; - if (GNUNET_OK != GNUNET_BIO_read_int32 (h, (int32_t *) &size)) + if (GNUNET_OK != GNUNET_BIO_read_int32 (h, + _ ("metadata length"), + (int32_t *) &size)) return GNUNET_SYSERR; - if (size == 0) + if (0 == size) { *result = NULL; return GNUNET_OK; } - if (size > MAX_META_DATA) + if (MAX_META_DATA < size) { - GNUNET_asprintf (&h->emsg, - _ ("Serialized metadata `%s' larger than allowed (%u>%u)"), - what, - size, - MAX_META_DATA); + GNUNET_asprintf ( + &h->emsg, + _ ("Serialized metadata `%s' larger than allowed (%u > %u)"), + what, + size, + MAX_META_DATA); return GNUNET_SYSERR; } buf = GNUNET_malloc (size); @@ -316,7 +423,7 @@ GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h, if (NULL == meta) { GNUNET_free (buf); - GNUNET_asprintf (&h->emsg, _ ("Metadata `%s' failed to deserialize"), what); + GNUNET_asprintf (&h->emsg, _ ("Failed to deserialize metadata `%s'"), what); return GNUNET_SYSERR; } GNUNET_free (buf); @@ -328,21 +435,19 @@ GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h, /** * Read an (u)int32_t. * - * @param h hande to open file - * @param file name of the source file - * @param line line number in the source file - * @param i address of 32-bit integer to read + * @param h the IO handle to read from + * @param what describes what is being read (for error message creation) + * @param i where to store the data * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_read_int32__ (struct GNUNET_BIO_ReadHandle *h, - const char *file, - int line, - int32_t *i) +GNUNET_BIO_read_int32 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + int32_t *i) { int32_t big; - if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof(int32_t))) + if (GNUNET_OK != GNUNET_BIO_read (h, what, &big, sizeof(int32_t))) return GNUNET_SYSERR; *i = ntohl (big); return GNUNET_OK; @@ -352,21 +457,19 @@ GNUNET_BIO_read_int32__ (struct GNUNET_BIO_ReadHandle *h, /** * Read an (u)int64_t. * - * @param h hande to open file - * @param file name of the source file - * @param line line number in the source file - * @param i address of 64-bit integer to read + * @param h the IO handle to read from + * @param what describes what is being read (for error message creation) + * @param i where to store the data * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_read_int64__ (struct GNUNET_BIO_ReadHandle *h, - const char *file, - int line, - int64_t *i) +GNUNET_BIO_read_int64 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + int64_t *i) { int64_t big; - if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof(int64_t))) + if (GNUNET_OK != GNUNET_BIO_read (h, what, &big, sizeof(int64_t))) return GNUNET_SYSERR; *i = GNUNET_ntohll (big); return GNUNET_OK; @@ -379,17 +482,29 @@ GNUNET_BIO_read_int64__ (struct GNUNET_BIO_ReadHandle *h, struct GNUNET_BIO_WriteHandle { /** - * Underlying file handle. + * The "backend" type. + */ + enum IOType type; + + /** + * Handle to a file on disk, if @e type is #IO_FILE. */ struct GNUNET_DISK_FileHandle *fd; /** - * I/O buffer. Do not free, allocated at the end of the struct. + * Error message, NULL if there were no errors. + */ + char *emsg; + + /** + * I/O buffer. + * This field is a void * because it is used to hold pointers to allocated + * structures or arrays and will be casted to the appropriate type. */ - char *buffer; + void *buffer; /** - * Number of bytes already in @e buffer. + * Number of bytes available in @e buffer. */ size_t have; @@ -403,25 +518,26 @@ struct GNUNET_BIO_WriteHandle /** * Open a file for writing. * - * @param fn file name to be opened + * @param fn name of the file to be opened * @return IO handle on success, NULL on error */ struct GNUNET_BIO_WriteHandle * -GNUNET_BIO_write_open (const char *fn) +GNUNET_BIO_write_open_file (const char *fn) { struct GNUNET_DISK_FileHandle *fd; struct GNUNET_BIO_WriteHandle *h; fd = GNUNET_DISK_file_open (fn, - GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE + GNUNET_DISK_OPEN_WRITE + | GNUNET_DISK_OPEN_TRUNCATE | GNUNET_DISK_OPEN_CREATE, GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); if (NULL == fd) return NULL; h = GNUNET_malloc (sizeof(struct GNUNET_BIO_WriteHandle) + BIO_BUFFER_SIZE); - h->buffer = (char *) &h[1]; + h->buffer = &h[1]; h->size = BIO_BUFFER_SIZE; h->fd = fd; return h; @@ -429,42 +545,94 @@ GNUNET_BIO_write_open (const char *fn) /** - * Close an open file for writing. + * Create a handle backed by an in-memory buffer. + * + * @return IO handle on success, NULL on error + */ +struct GNUNET_BIO_WriteHandle * +GNUNET_BIO_write_open_buffer (void) +{ + struct GNUNET_BIO_WriteHandle *h; + + h = GNUNET_new (struct GNUNET_BIO_WriteHandle); + h->type = IO_BUFFER; + h->buffer = (void *) GNUNET_malloc (sizeof (struct GNUNET_Buffer)); + return h; +} + + +/** + * Close an IO handle. + * If the handle was using a file, the file will be closed. * * @param h file handle + * @param emsg set to the (allocated) error message + * if the handle has an error message, the return value is #GNUNET_SYSERR * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int -GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h) +GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h, char **emsg) { - int ret; + int err; - ret = GNUNET_SYSERR; - if ((NULL != h->fd) && (GNUNET_OK == (ret = GNUNET_BIO_flush (h)))) - GNUNET_DISK_file_close (h->fd); + err = (NULL == h->emsg) ? GNUNET_OK : GNUNET_SYSERR; + if (NULL != emsg) + *emsg = h->emsg; + else + GNUNET_free_non_null (h->emsg); + switch (h->type) + { + case IO_FILE: + if (NULL == h->fd) + return GNUNET_SYSERR; + if (GNUNET_OK != GNUNET_BIO_flush (h)) + { + if (NULL != emsg) + *emsg = h->emsg; + else + GNUNET_free_non_null (h->emsg); + err = GNUNET_SYSERR; + } + else + { + GNUNET_DISK_file_close (h->fd); + } + break; + case IO_BUFFER: + GNUNET_buffer_clear ((struct GNUNET_Buffer *) h->buffer); + GNUNET_free (h->buffer); + break; + } GNUNET_free (h); - return ret; + return err; } /** - * Force a buffered writer to flush its buffer + * Force a file-based buffered writer to flush its buffer. + * If the handle does not use a file, this function returs #GNUNET_OK + * without doing anything. * - * @param h the writer handle - * @return #GNUNET_OK upon success. Upon failure #GNUNET_SYSERR is returned and - * the file is closed + * @param h the IO handle + * @return #GNUNET_OK upon success. Upon failure #GNUNET_SYSERR is returned + * and the file is closed */ int GNUNET_BIO_flush (struct GNUNET_BIO_WriteHandle *h) { ssize_t ret; + if (IO_FILE != h->type) + return GNUNET_OK; + ret = GNUNET_DISK_file_write (h->fd, h->buffer, h->have); if (ret != (ssize_t) h->have) { GNUNET_DISK_file_close (h->fd); h->fd = NULL; - return GNUNET_SYSERR; /* error */ + GNUNET_free_non_null (h->emsg); + GNUNET_asprintf (&h->emsg, _ ("Unable to flush buffer to file")); + return GNUNET_SYSERR; } h->have = 0; return GNUNET_OK; @@ -472,96 +640,213 @@ GNUNET_BIO_flush (struct GNUNET_BIO_WriteHandle *h) /** - * Write a buffer to a file. + * Get the IO handle's contents. + * If the handle doesn't use an in-memory buffer, this function returns + * #GNUNET_SYSERR. * - * @param h handle to open file - * @param buffer the data to write - * @param n number of bytes to write - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @param h the IO handle + * @param emsg set to the (allocated) error message + * if the handle has an error message the return value is #GNUNET_SYSERR + * @param contents where to store the pointer to the handle's contents + * @param size where to store the size of @e contents + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int -GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, - const void *buffer, - size_t n) +GNUNET_BIO_get_buffer_contents (struct GNUNET_BIO_WriteHandle *h, + char **emsg, + void **contents, + size_t *size) +{ + if (IO_BUFFER != h->type) + return GNUNET_SYSERR; + if (NULL == contents || NULL == size) + return GNUNET_SYSERR; + int ret = (NULL != h->emsg) ? GNUNET_SYSERR : GNUNET_OK; + if (NULL != emsg) + *emsg = h->emsg; + else + GNUNET_free_non_null (h->emsg); + *contents = GNUNET_buffer_reap ((struct GNUNET_Buffer *) h->buffer, size); + return ret; +} + + +/** + * Function used internally to write the contents of a buffer into a file. + * + * @param h the IO handle to write to + * @param what describes what is being written (for error message creation) + * @param source the buffer to write + * @param len the number of bytes to write + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +write_to_file (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const char *source, + size_t len) { - const char *src = buffer; size_t min; - size_t pos; + size_t pos = 0; + char *buffer = (char *) h->buffer; if (NULL == h->fd) + { + GNUNET_asprintf (&h->emsg, + _ ("Error while writing `%s' to file: %s"), + what, + _ ("No associated file")); return GNUNET_SYSERR; - pos = 0; + } + do { - /* first, just use buffer */ min = h->size - h->have; - if (min > n - pos) - min = n - pos; - GNUNET_memcpy (&h->buffer[h->have], &src[pos], min); + if (len - pos < min) + min = len - pos; + GNUNET_memcpy (&buffer[h->have], &source[pos], min); pos += min; h->have += min; - if (pos == n) - return GNUNET_OK; /* done */ + if (len == pos) + return GNUNET_OK; GNUNET_assert (h->have == h->size); if (GNUNET_OK != GNUNET_BIO_flush (h)) - return GNUNET_SYSERR; /* error */ + { + char *tmp = h->emsg; + GNUNET_asprintf (&h->emsg, + _ ("Error while writing `%s' to file: %s"), + what, + tmp); + GNUNET_free_non_null (tmp); + return GNUNET_SYSERR; + } } - while (pos < n); /* should always be true */ + while (pos < len); GNUNET_break (0); return GNUNET_OK; } /** - * Write a string to a file. + * Function used internally to write the contents of a buffer to another buffer. + * + * @param h the IO handle to write to + * @param what describes what is being written (for error message creation) + * @param source the buffer to write + * @param len the number of bytes to write + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +write_to_buffer (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const char *source, + size_t len) +{ + GNUNET_buffer_write ((struct GNUNET_Buffer *) h->buffer, source, len); + h->have += len; + return GNUNET_OK; +} + + +/** + * Write a buffer to a handle. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param buffer the data to write + * @param n number of bytes to write + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +int +GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const void *buffer, + size_t n) +{ + const char *src = buffer; + + if (NULL != h->emsg) + return GNUNET_SYSERR; + + if (0 == n) + return GNUNET_OK; + + switch (h->type) + { + case IO_FILE: + return write_to_file (h, what, src, n); + case IO_BUFFER: + return write_to_buffer (h, what, src, n); + default: + GNUNET_asprintf (&h->emsg, + _ ("Invalid handle type while writing `%s'"), + what); + return GNUNET_SYSERR; + } +} + + +/** + * Write a 0-terminated string. * - * @param h handle to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param s string to write (can be NULL) * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h, const char *s) +GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const char *s) { uint32_t slen; slen = (uint32_t) ((s == NULL) ? 0 : strlen (s) + 1); - if (GNUNET_OK != GNUNET_BIO_write_int32 (h, slen)) + if (GNUNET_OK != GNUNET_BIO_write_int32 (h, _ ("string length"), slen)) return GNUNET_SYSERR; if (0 != slen) - return GNUNET_BIO_write (h, s, slen - 1); + return GNUNET_BIO_write (h, what, s, slen - 1); return GNUNET_OK; } /** - * Write metadata container to a file. + * Write a metadata container. * - * @param h handle to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param m metadata to write * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h, + const char *what, const struct GNUNET_CONTAINER_MetaData *m) { ssize_t size; char *buf; if (m == NULL) - return GNUNET_BIO_write_int32 (h, 0); + return GNUNET_BIO_write_int32 (h, _ ("metadata length"), 0); buf = NULL; size = GNUNET_CONTAINER_meta_data_serialize ( m, &buf, MAX_META_DATA, GNUNET_CONTAINER_META_DATA_SERIALIZE_PART); - if (size == -1) + if (-1 == size) { GNUNET_free (buf); + GNUNET_free_non_null (h->emsg); + GNUNET_asprintf (&h->emsg, + _ ("Failed to serialize metadata `%s'"), + what); return GNUNET_SYSERR; } - if ((GNUNET_OK != GNUNET_BIO_write_int32 (h, (uint32_t) size)) || - (GNUNET_OK != GNUNET_BIO_write (h, buf, size))) + if ((GNUNET_OK != GNUNET_BIO_write_int32 (h, + _ ("metadata length"), + (uint32_t) size)) + || (GNUNET_OK != GNUNET_BIO_write (h, what, buf, size))) { GNUNET_free (buf); return GNUNET_SYSERR; @@ -574,34 +859,549 @@ GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h, /** * Write an (u)int32_t. * - * @param h hande to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param i 32-bit integer to write * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, int32_t i) +GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int32_t i) { int32_t big; big = htonl (i); - return GNUNET_BIO_write (h, &big, sizeof(int32_t)); + return GNUNET_BIO_write (h, what, &big, sizeof(int32_t)); } /** * Write an (u)int64_t. * - * @param h hande to open file + * @param h the IO handle to write to + * @param what what is being written (for error message creation) * @param i 64-bit integer to write * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, int64_t i) +GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int64_t i) { int64_t big; big = GNUNET_htonll (i); - return GNUNET_BIO_write (h, &big, sizeof(int64_t)); + return GNUNET_BIO_write (h, what, &big, sizeof(int64_t)); +} + + +/** + * Function used internally to read some bytes from within a read spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param target where to store the data + * @param target_size how many bytes to read + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +read_spec_handler_object (void *cls, + struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *target, + size_t target_size) +{ + return GNUNET_BIO_read (h, what, target, target_size); +} + + +/** + * Create the specification to read a certain amount of bytes. + * + * @param what describes what is being read (for error message creation) + * @param result the buffer to write the result to + * @param len the number of bytes to read + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_object (const char *what, + void *result, + size_t len) +{ + struct GNUNET_BIO_ReadSpec rs = { + .rh = &read_spec_handler_object, + .cls = NULL, + .what = what, + .target = result, + .size = len, + }; + + return rs; +} + + +/** + * Function used interally to read a string from within a read spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param target where to store the data + * @param target_size how many bytes to read + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +read_spec_handler_string (void *cls, + struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *target, + size_t target_size) +{ + char **result = target; + return GNUNET_BIO_read_string (h, what, result, target_size); +} + + +/** + * Create the specification to read a 0-terminated string. + * + * @param what describes what is being read (for error message creation) + * @param result where to store the pointer to the (allocated) string + * (note that *result could be set to NULL as well) + * @param max_length maximum allowed length for the string + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_string (const char *what, + char **result, + size_t max_length) +{ + struct GNUNET_BIO_ReadSpec rs = { + .rh = &read_spec_handler_string, + .cls = NULL, + .target = result, + .size = max_length, + }; + + return rs; +} + + +/** + * Function used internally to read a metadata container from within a read + * spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param target where to store the data + * @param target_size ignored + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +read_spec_handler_meta_data (void *cls, + struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *target, + size_t target_size) +{ + struct GNUNET_CONTAINER_MetaData **result = target; + return GNUNET_BIO_read_meta_data (h, what, result); +} + + +/** + * Create the specification to read a metadata container. + * + * @param what describes what is being read (for error message creation) + * @param result the buffer to store a pointer to the (allocated) metadata + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_meta_data (const char *what, + struct GNUNET_CONTAINER_MetaData **result) +{ + struct GNUNET_BIO_ReadSpec rs = { + .rh = &read_spec_handler_meta_data, + .cls = NULL, + .target = result, + .size = 0, + }; + + return rs; +} + + +/** + * Function used internally to read an (u)int32_t from within a read spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param target where to store the data + * @param target_size ignored + * @retun #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +read_spec_handler_int32 (void *cls, + struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *target, + size_t target_size) +{ + int32_t *result = target; + return GNUNET_BIO_read_int32 (h, what, result); +} + + +/** + * Create the specification to read an (u)int32_t. + * + * @param what describes what is being read (for error message creation) + * @param i where to store the data + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_int32 (const char *what, + int32_t *i) +{ + struct GNUNET_BIO_ReadSpec rs = { + .rh = &read_spec_handler_int32, + .cls = NULL, + .target = i, + .size = 0, + }; + + return rs; +} + + +/** + * Function used internally to read an (u)int64_t from within a read spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param target where to store the data + * @param target_size ignored + * @retun #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +read_spec_handler_int64 (void *cls, + struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *target, + size_t target_size) +{ + int64_t *result = target; + return GNUNET_BIO_read_int64 (h, what, result); +} + + +/** + * Create the specification to read an (u)int64_t. + * + * @param what describes what is being read (for error message creation) + * @param i where to store the data + * @return the read spec + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_int64 (const char *what, + int64_t *i) +{ + struct GNUNET_BIO_ReadSpec rs = { + .rh = &read_spec_handler_int64, + .cls = NULL, + .target = i, + .size = 0, + }; + + return rs; +} + + +/** + * Execute the read specifications in order. + * + * @param h the IO handle to read from + * @param rs array of read specs + * the last element must be #GNUNET_BIO_read_spec_end + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +GNUNET_BIO_read_spec_commit (struct GNUNET_BIO_ReadHandle *h, + struct GNUNET_BIO_ReadSpec *rs) +{ + int ret = GNUNET_OK; + + for (size_t i=0; NULL!=rs[i].rh; ++i) + { + ret = rs[i].rh (rs[i].cls, h, rs[i].what, rs[i].target, rs[i].size); + if (GNUNET_OK != ret) + return ret; + } + + return ret; +} + + +/** + * Function used internally to write some bytes from within a write spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the data to write + * @param source_size how many bytes to write + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +write_spec_handler_object (void *cls, + struct GNUNET_BIO_WriteHandle *h, + const char *what, + void *source, + size_t source_size) +{ + return GNUNET_BIO_write (h, what, source, source_size); +} + + +/** + * Create the specification to read some bytes. + * + * @param what describes what is being written (for error message creation) + * @param source the data to write + * @param size how many bytes should be written + * @return the write spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_object (const char *what, + void *source, + size_t size) +{ + struct GNUNET_BIO_WriteSpec ws = { + .wh = &write_spec_handler_object, + .cls = NULL, + .what = what, + .source = source, + .source_size = size, + }; + + return ws; +} + + +/** + * Function used internally to write a 0-terminated string from within a write + * spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the data to write + * @param source_size ignored + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +write_spec_handler_string (void *cls, + struct GNUNET_BIO_WriteHandle *h, + const char *what, + void *source, + size_t source_size) +{ + const char *s = source; + return GNUNET_BIO_write_string (h, what, s); +} + + +/** + * Create the specification to write a 0-terminated string. + * + * @param what describes what is being read (for error message creation) + * @param s string to write (can be NULL) + * @return the read spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_string (const char *what, + const char *s) +{ + struct GNUNET_BIO_WriteSpec ws = { + .wh = &write_spec_handler_string, + .cls = NULL, + .what = what, + .source = (void *) s, + .source_size = 0, + }; + + return ws; +} + + +/** + * Function used internally to write a metadata container from within a write + * spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the data to write + * @param source_size ignored + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +write_spec_handler_meta_data (void *cls, + struct GNUNET_BIO_WriteHandle *h, + const char *what, + void *source, + size_t source_size) +{ + const struct GNUNET_CONTAINER_MetaData *m = source; + return GNUNET_BIO_write_meta_data (h, what, m); +} + + +/** + * Create the specification to write a metadata container. + * + * @param what what is being written (for error message creation) + * @param m metadata to write + * @return the write spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_meta_data (const char *what, + const struct GNUNET_CONTAINER_MetaData *m) +{ + struct GNUNET_BIO_WriteSpec ws = { + .wh = &write_spec_handler_meta_data, + .cls = NULL, + .what = what, + .source = (void *) m, + .source_size = 0, + }; + + return ws; +} + + +/** + * Function used internally to write an (u)int32_t from within a write spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the data to write + * @param source_size ignored + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +write_spec_handler_int32 (void *cls, + struct GNUNET_BIO_WriteHandle *h, + const char *what, + void *source, + size_t source_size) +{ + int32_t i = *(int32_t *) source; + return GNUNET_BIO_write_int32 (h, what, i); +} + + +/** + * Create the specification to write an (u)int32_t. + * + * @param what describes what is being written (for error message creation) + * @param i pointer to a 32-bit integer + * @return the write spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_int32 (const char *what, + int32_t *i) +{ + struct GNUNET_BIO_WriteSpec ws = { + .wh = &write_spec_handler_int32, + .cls = NULL, + .what = what, + .source = i, + .source_size = 0, + }; + + return ws; +} + + +/** + * Function used internally to write an (u)int64_t from within a write spec. + * + * @param cls ignored, always NULL + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the data to write + * @param source_size ignored + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +write_spec_handler_int64 (void *cls, + struct GNUNET_BIO_WriteHandle *h, + const char *what, + void *source, + size_t source_size) +{ + int64_t i = *(int64_t *) source; + return GNUNET_BIO_write_int64 (h, what, i); +} + + +/** + * Create the specification to write an (u)int64_t. + * + * @param what describes what is being written (for error message creation) + * @param i pointer to a 64-bit integer + * @return the write spec + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_int64 (const char *what, + int64_t *i) +{ + struct GNUNET_BIO_WriteSpec ws = { + .wh = &write_spec_handler_int64, + .cls = NULL, + .what = what, + .source = i, + .source_size = 0, + }; + + return ws; +} + + +/** + * Execute the write specifications in order. + * + * @param h the IO handle to write to + * @param ws array of write specs + * the last element must be #GNUNET_BIO_write_spec_end + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +GNUNET_BIO_write_spec_commit (struct GNUNET_BIO_WriteHandle *h, + struct GNUNET_BIO_WriteSpec *ws) +{ + int ret = GNUNET_OK; + + for (size_t i=0; NULL!=ws[i].wh; ++i) + { + ret = ws[i].wh (ws[i].cls, h, ws[i].what, ws[i].source, ws[i].source_size); + if (GNUNET_OK != ret) + return ret; + } + + /* If it's a file-based handle, the flush makes sure that the data in the + buffer is actualy written to the disk. */ + if (IO_FILE == h->type) + ret = GNUNET_BIO_flush (h); + + return ret; } diff --git a/src/util/buffer.c b/src/util/buffer.c index dabf630c7..c865f6307 100644 --- a/src/util/buffer.c +++ b/src/util/buffer.c @@ -130,7 +130,26 @@ GNUNET_buffer_reap_str (struct GNUNET_Buffer *buf) buf->mem[buf->position++] = '\0'; } res = buf->mem; - *buf = (struct GNUNET_Buffer) { 0 }; + memset (buf, 0, sizeof (struct GNUNET_Buffer)); + return res; +} + + +/** + * Clear the buffer and return its contents. + * The caller is responsible to eventually #GNUNET_free + * the returned data. + * + * @param buf the buffer to reap the contents from + * @param size where to store the size of the returned data + * @returns the data contained in the string + */ +void * +GNUNET_buffer_reap (struct GNUNET_Buffer *buf, size_t *size) +{ + *size = buf->position; + void *res = buf->mem; + memset (buf, 0, sizeof (struct GNUNET_Buffer)); return res; } @@ -144,7 +163,7 @@ void GNUNET_buffer_clear (struct GNUNET_Buffer *buf) { GNUNET_free_non_null (buf->mem); - *buf = (struct GNUNET_Buffer) { 0 }; + memset (buf, 0, sizeof (struct GNUNET_Buffer)); } -- 2.26.2