>From 0cf140eae8a36699ef5cb917e1c60f1b3b020c0f Mon Sep 17 00:00:00 2001 From: Alessio Vanni Date: Sat, 9 May 2020 14:20:48 +0200 Subject: [PATCH] New BIO API --- src/include/gnunet_bio_lib.h | 550 ++++++++++++++--- src/util/bio.c | 1132 ++++++++++++++++++++++++++++------ 2 files changed, 1397 insertions(+), 285 deletions(-) diff --git a/src/include/gnunet_bio_lib.h b/src/include/gnunet_bio_lib.h index 2f715ec97..2c9048a2f 100644 --- a/src/include/gnunet_bio_lib.h +++ b/src/include/gnunet_bio_lib.h @@ -42,6 +42,9 @@ extern "C" #endif #endif +/****************************** READING API ********************************/ + + /** * Handle for buffered reading. */ @@ -59,71 +62,75 @@ GNUNET_BIO_read_open (const char *fn); /** - * Close an open file. Reports if any errors reading - * from the file were encountered. + * Create a handle from an existing buffer. * - * @param h file handle - * @param emsg set to the error message - * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + * @param buffer the buffer to use + * @param size the size in bytes of the buffer + (can be smaller than the real allocated size) + * @return IO handle on success, NULL on error */ -int -GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg); +struct GNUNET_BIO_ReadHandle * +GNUNET_BIO_read_create (void *buffer, size_t size); /** - * Read the contents of a binary file into a buffer. + * Destroy an IO handle. If the handle was using a file, the file will be + * closed. If the handle was using a buffer allocated externally, the buffer + * will @b not be freed. * - * @param h handle to an open file - * @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 + * @param h the handle + * @param emsg set to the (allocated) error message + * if emsg is present, the return value is #GNUNET_SYSERR + * @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); +GNUNET_BIO_read_destroy (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 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 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); + /** - * Read 0-terminated string from a file. + * Read a 0-terminated string from a handle. * - * @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 - * (note that *result could be set to NULL as well) + * @param result the buffer to store a pointer to the (allocated) string + * can be NULL in certain cases * @param max_length maximum allowed length for the string - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ 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 from a handle. * - * @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) metadata + * @param result the buffer to store a pointer to the (allocated) metadata. * @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); @@ -131,70 +138,57 @@ GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h, const char *what, * Read a float. * * @param h hande to open file + * @param what 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 what 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); - - -/** - * 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) +GNUNET_BIO_read_int32 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + 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 ****************************/ /** @@ -202,6 +196,7 @@ GNUNET_BIO_read_int64__ (struct GNUNET_BIO_ReadHandle *h, const char *file, */ struct GNUNET_BIO_WriteHandle; + /** * Open a file for writing. * @@ -213,30 +208,52 @@ GNUNET_BIO_write_open (const char *fn); /** - * Close an open file for writing. + * Create a handle from an existing buffer. + * + * @param buffer the buffer to use + * @param size the size in bytes of the buffer + (can be smaller than the real allocated size) + * @return IO handle on success, NULL on error + */ +struct GNUNET_BIO_WriteHandle * +GNUNET_BIO_write_create (void *buffer, size_t size); + + +/** + * Destroy an IO handle. If the handle was using a file, the file will be + * closed. If the handle was using a buffer allocated externally, the buffer + * will @b not be freed. * - * @param h file handle - * @return GNUNET_OK on success, GNUNET_SYSERR otherwise + * @param h the handle + * @param emsg set to the (allocated) error message + * if emsg is present, 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_destroy (struct GNUNET_BIO_WriteHandle *h, char **emsg); /** - * Write a buffer to a file. + * Write a buffer to an IO handle. + * The destination depends on how the handle was created. * - * @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 buffer the data to write * @param n number of bytes to write - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int -GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, const void *buffer, +GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const void *buffer, size_t n); /** - * 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 simply returns #GNUNET_OK + * without doing anything. * * @param h the writer handle * @return #GNUNET_OK upon success. Upon failure #GNUNET_SYSERR is returned and @@ -247,25 +264,30 @@ GNUNET_BIO_flush (struct GNUNET_BIO_WriteHandle *h); /** - * Write a string to a file. + * Write a string to an IO handle. * - * @param h handle to open file - * @param s string to write (can be NULL) - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @param h the IO handle + * @param what what is being written (for error message creation) + * @param s 0-terminated string to write (can be NULL) + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ 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 metadata container to a IO handle. * - * @param h handle to open file + * @param h the IO handle + * @param what what is being written (for error message creation) * @param m metadata to write - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h, + const char *what, const struct GNUNET_CONTAINER_MetaData *m); @@ -273,40 +295,374 @@ GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h, * Write a float. * * @param h hande to open file + * @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 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. + * Write a (u)int32 to an IO handle. * - * @param h hande to open file - * @param i 32-bit integer to write - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @param h the IO handle + * @param what what is being written (for error message creation) + * @param i 32-big integer to write + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ 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. + * Write a (u)int64 to an IO handle. * - * @param h hande to open file + * @param h the IO handle + * @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 + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int64_t i); + + +/*************************** READ/WRITE SPEC API *************************/ + + +/** + * These values identify a "primitive type" for data to be read/written. + */ +#define GNUNET_BIO_SPEC_OBJECT 0 +#define GNUNET_BIO_SPEC_INT32 2 +#define GNUNET_BIO_SPEC_INT64 4 +#define GNUNET_BIO_SPEC_FLOAT 8 +#define GNUNET_BIO_SPEC_DOUBLE 16 +#define GNUNET_BIO_SPEC_STRING 32 +#define GNUNET_BIO_SPEC_META 64 + + +/********************************* READ SPEC API ************************/ + + +/** + * Description of a read operation. + */ +struct GNUNET_BIO_ReadSpec +{ + /** + * What is being read in this operation. + */ + const char *what; + + /** + * The "type" of the data being read. + */ + int type; + + /** + * The handle to read from. + */ + struct GNUNET_BIO_ReadHandle *h; + + /** + * How many bytes should be read from the handle. + */ + size_t size; + + /** + * Where to store the data. + */ + void *result; +}; + + +/** + * Create a read spec to read a certain amount of bytes. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @param size how many bytes should be read + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_object (struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *result, + size_t size); + + +/** + * Create a read spec to read an (u)int32. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_int32 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + int32_t *result); + + +/** + * Create a read spec to read an (u)int64. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_int64 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + int64_t *result); + + +/** + * Create a read spec to read a float. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_float (struct GNUNET_BIO_ReadHandle *h, + const char *what, + float *result); + + +/** + * Create a read spec to read a double. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_double (struct GNUNET_BIO_ReadHandle *h, + const char *what, + double *result); + + +/** + * Create a read spec to read a 0-terminated string. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store a pointer to the (allocated) string + * can be NULL in certain cases + * @param max_length maximum allowed length for the string + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_string (struct GNUNET_BIO_ReadHandle *h, + const char *what, + char **result, + size_t max_length); + + +/** + * Create a read spec to read a metadata container. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result the buffer to store a pointer to the (allocated) metadata. + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_meta_data (struct GNUNET_BIO_ReadHandle *h, + const char *what, + struct GNUNET_CONTAINER_MetaData **result); + + +/** + * End of read operations specification. + */ +#define GNUNET_BIO_read_spec_end() { NULL, 0, NULL, 0, NULL } + + +/** + * Execute the provided read operations. + * + * @param rs an array of read spec + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +GNUNET_BIO_read_spec_commit (struct GNUNET_BIO_ReadSpec *rs); + + +/******************************* WRITE SPEC API ****************************/ + + +/** + * Description of a write operation. + */ +struct GNUNET_BIO_WriteSpec +{ + /** + * What is being read in this operation. + */ + const char *what; + + /** + * The "type" of the data being written. + */ + int type; + + /** + * The handle to write to. + */ + struct GNUNET_BIO_WriteHandle *h; + + /** + * How many bytes should be written to the handle. + */ + size_t size; + + /** + * The data to write to the handle. + */ + const void *source; +}; + + +/** + * Create a write spec to write a certain amount of bytes. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the data to write to the handle + * @param size how many bytes should be written + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_object (struct GNUNET_BIO_WriteHandle *h, + const char *what, + void *source, + size_t size); + + +/** + * Create a write spec to write an (u)int32. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source a pointer to a 32-bit integer + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_int32 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int32_t *source); + + +/** + * Create a write spec to write an (u)int64. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source a pointer to a 64-bit integer + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_int64 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int64_t *source); + + +/** + * Create a write spec to write a float. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source a pointer to a float + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_float (struct GNUNET_BIO_WriteHandle *h, + const char *what, + float *source); + + +/** + * Create a write spec to write a double. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source a pointer to a double + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_double (struct GNUNET_BIO_WriteHandle *h, + const char *what, + double *source); + + +/** + * Create a write spec to write a string + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the 0-terminated string to write + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_string (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const char *source); + + +/** + * Create a write spec to write a metadata container + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the metadata to write + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_meta_data (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const struct GNUNET_CONTAINER_MetaData *source); + + +/** + * End of write operations specification. + */ +#define GNUNET_BIO_write_spec_end() { NULL, 0, NULL, 0, NULL } + + +/** + * Execute the provided write operations. + * + * @param ws an array of write specs + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int -GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, int64_t i); +GNUNET_BIO_write_spec_commit (struct GNUNET_BIO_WriteSpec *ws); #if 0 /* keep Emacsens' auto-indent happy */ diff --git a/src/util/bio.c b/src/util/bio.c index e05258f73..424573945 100644 --- a/src/util/bio.c +++ b/src/util/bio.c @@ -36,39 +36,64 @@ /** - * Size for I/O buffers. + * Size for file-based I/O buffers. */ #define BIO_BUFFER_SIZE 65536 /** - * Maximum size allowed for meta data written/read from disk. + * Maximum read/written size allowed for meta data. * File-sharing limits to 64k, so this should be rather generous. */ #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 + * kind of "backend" for buffering (or even just for formatted I/O.) + */ +enum IOType +{ + /** + * The handle uses a file to read/write data. + */ + IO_FILE = 0, + + /** + * The data is entirely in memory withing a fixed-size buffer. + */ + 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; /** - * Error message, NULL if there were no errors. + * Error message. NULL is there were no errors. */ 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; @@ -78,7 +103,7 @@ struct GNUNET_BIO_ReadHandle size_t size; /** - * Current read offset in @e buffer. + * Current offset in @buffer. */ off_t pos; }; @@ -87,7 +112,7 @@ struct GNUNET_BIO_ReadHandle /** * Open a file for reading. * - * @param fn file name to be opened + * @param fn name of file to open * @return IO handle on success, NULL on error */ struct GNUNET_BIO_ReadHandle * @@ -103,78 +128,111 @@ GNUNET_BIO_read_open (const char *fn) h->buffer = (char *) &h[1]; h->size = BIO_BUFFER_SIZE; h->fd = fd; + h->type = IO_FILE; + return h; +} + + +/** + * Create a handle from an existing buffer. + * + * @param buffer the buffer to use + * @param size the size in bytes of the buffer + (can be smaller than the real allocated size) + * @return IO handle on success, NULL on error + */ +struct GNUNET_BIO_ReadHandle * +GNUNET_BIO_read_create (void *buffer, size_t size) +{ + struct GNUNET_BIO_ReadHandle *h; + if (NULL == buffer || 0 == size) + return NULL; + h = GNUNET_new (struct GNUNET_BIO_ReadHandle); + h->buffer = buffer; + h->size = size; + h->type = IO_BUFFER; return h; } /** - * Close an open file. Reports if any errors reading - * from the file were encountered. + * Destroy an IO handle. If the handle was using a file, the file will be + * closed. If the handle was using a buffer allocated externally, the buffer + * will @b not be freed. * - * @param h file handle - * @param emsg set to the error message + * @param h the handle + * @param emsg set to the (allocated) error message + * if emsg is present, the return value is #GNUNET_SYSERR * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int -GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg) +GNUNET_BIO_read_destroy (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. + * Internal function used to read from a file. + * The data read is first buffered so further reads will not generate + * disk activities unless necessary. * - * @param h handle to an open file + * @param h the IO handle * @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 + * @param result where to write the data + * @param len how many bytes to read + * @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_file (struct GNUNET_BIO_ReadHandle *h, + const char *what, + char *result, + size_t len) { - char *dst = result; size_t min; size_t pos; ssize_t ret; - if (NULL != h->emsg) - return GNUNET_SYSERR; pos = 0; do { - /* first, use buffer */ + /* Try to use the buffer first */ 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! */ + return GNUNET_OK; GNUNET_assert (((off_t) h->have) == h->pos); - /* fill buffer */ + /* Fill the buffer with more data */ ret = GNUNET_DISK_file_read (h->fd, h->buffer, h->size); if (-1 == ret) { GNUNET_asprintf (&h->emsg, - _ ("Error reading `%s': %s"), + _ ("Error while reading `%s' from file: %s"), what, strerror (errno)); return GNUNET_SYSERR; @@ -182,7 +240,7 @@ GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, if (0 == ret) { GNUNET_asprintf (&h->emsg, - _ ("Error reading `%s': %s"), + _ ("Error while reading `%s' from file: %s"), what, _ ("End of file")); return GNUNET_SYSERR; @@ -190,44 +248,132 @@ 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; +} + + +/** + * Internal function to read from a buffer. + * The data is simply copied from the managed buffer to the destination + * buffer. Additionally, attempting to read too much data will result in an + * error instead of implicitly reading less data. + * + * @param h the IO handle + * @param what describes what is being read (for error message creation) + * @param result where to write the data + * @param len how many bytes to read + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +static int +read_buffer (struct GNUNET_BIO_ReadHandle *h, + const char *what, + char *result, + size_t len) +{ + if (0 == len) + return GNUNET_OK; + 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 the contents of a binary file into a buffer. + * Read some contents 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 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 *dst = result; + int ret = GNUNET_OK; + + if (NULL != h->emsg) + return GNUNET_SYSERR; + + switch (h->type) + { + case IO_FILE: + ret = read_file (h, what, dst, len); + break; + case IO_BUFFER: + ret = read_buffer (h, what, dst, len); + break; + } + + return ret; +} + + +/** + * Read an (u)int32_t. + * + * @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 *what, + int32_t *i) { - char what[PATH_MAX + 1024]; + int32_t big; - GNUNET_snprintf (what, sizeof(what), "%s:%d", file, line); - return GNUNET_BIO_read (h, what, result, len); + if (GNUNET_OK != GNUNET_BIO_read (h, what, &big, sizeof (int32_t))) + return GNUNET_SYSERR; + *i = ntohl (big); + return GNUNET_OK; +} + + +/** + * Read an (u)int64_t. + * + * @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 *what, + int64_t *i) +{ + int64_t big; + if (GNUNET_OK != GNUNET_BIO_read (h, what, &big, sizeof (int64_t))) + return GNUNET_SYSERR; + *i = GNUNET_ntohll (big); + return GNUNET_OK; } /** - * Read 0-terminated string from a file. + * Read a 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 - * (note that *result could be set to NULL as well) + * @param result the buffer to store a pointer to the (allocated) string + * can be NULL in certain cases * @param max_length maximum allowed length for the string - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, @@ -238,10 +384,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 string `%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) @@ -263,7 +420,7 @@ GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, buf[--big] = '\0'; if (0 == big) return GNUNET_OK; - if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, big)) + if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, (int32_t) big)) { GNUNET_free (buf); *result = NULL; @@ -274,11 +431,11 @@ GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, /** - * Read metadata container from a file. + * Read a metadata container from a handle. * - * @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) metadata + * @param result the buffer to store a pointer to the (allocated) metadata. * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ int @@ -290,106 +447,70 @@ 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); - if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, size)) + if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, (int32_t) size)) { GNUNET_free (buf); return GNUNET_SYSERR; } meta = GNUNET_CONTAINER_meta_data_deserialize (buf, size); + GNUNET_free (buf); 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); *result = meta; return GNUNET_OK; } -/** - * 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 - * @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) -{ - int32_t big; - - if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof(int32_t))) - return GNUNET_SYSERR; - *i = ntohl (big); - return GNUNET_OK; -} - - -/** - * 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 - * @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) -{ - int64_t big; - - if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof(int64_t))) - return GNUNET_SYSERR; - *i = GNUNET_ntohll (big); - return GNUNET_OK; -} - - /** * Handle for buffered writing. */ 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 is there were no errors. + */ + char *emsg; + + /** + * I/O buffer. Do @b not free! */ char *buffer; /** - * Number of bytes already in @e buffer. + * Number of bytes available in the @e buffer. */ size_t have; @@ -403,7 +524,7 @@ struct GNUNET_BIO_WriteHandle /** * Open a file for writing. * - * @param fn file name to be opened + * @param fn name of file to open * @return IO handle on success, NULL on error */ struct GNUNET_BIO_WriteHandle * @@ -414,7 +535,8 @@ GNUNET_BIO_write_open (const char *fn) 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); @@ -429,26 +551,69 @@ GNUNET_BIO_write_open (const char *fn) /** - * Close an open file for writing. + * Create a handle from an existing buffer. + * + * @param buffer the buffer to use + * @param size the size in bytes of the buffer + (can be smaller than the real allocated size) + * @return IO handle on success, NULL on error + */ +struct GNUNET_BIO_WriteHandle * +GNUNET_BIO_write_create (void *buffer, size_t size) +{ + struct GNUNET_BIO_WriteHandle *h; + if (NULL == buffer || 0 == size) + return NULL; + h = GNUNET_new (struct GNUNET_BIO_WriteHandle); + h->buffer = buffer; + h->size = size; + h->type = IO_BUFFER; + return h; +} + + +/** + * Destroy an IO handle. If the handle was using a file, the file will be + * closed. If the handle was using a buffer allocated externally, the buffer + * will @b not be freed. * - * @param h file handle + * @param h the handle + * @param emsg set to the (allocated) error message + * if emsg is present, 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_destroy (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 (GNUNET_OK != GNUNET_BIO_flush (h)) + err = GNUNET_SYSERR; + else + GNUNET_DISK_file_close (h->fd); + break; + case IO_BUFFER: + break; + default: + 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 simply returns #GNUNET_OK + * without doing anything. * * @param h the writer handle * @return #GNUNET_OK upon success. Upon failure #GNUNET_SYSERR is returned and @@ -459,12 +624,17 @@ 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, _ ("Flushing buffer")); + return GNUNET_SYSERR; } h->have = 0; return GNUNET_OK; @@ -472,96 +642,221 @@ GNUNET_BIO_flush (struct GNUNET_BIO_WriteHandle *h) /** - * Write a buffer to a file. + * Internal function used to write to a file. + * The written data is first buffered in memory so that disk activity is + * generated only when necessary. * - * @param h handle to open file + * @param h the IO handle + * @param what describes 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 + * @param n how many bytes should be written + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int -GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, - const void *buffer, - size_t n) +write_file (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const char *buffer, + size_t n) { - const char *src = buffer; size_t min; size_t pos; - if (NULL == h->fd) - return GNUNET_SYSERR; pos = 0; do { - /* first, just use buffer */ + /* Try to use the buffer first */ min = h->size - h->have; - if (min > n - pos) + if (n - pos < min) min = n - pos; - GNUNET_memcpy (&h->buffer[h->have], &src[pos], min); + GNUNET_memcpy (&h->buffer[h->have], &buffer[pos], min); pos += min; h->have += min; - if (pos == n) - return GNUNET_OK; /* done */ + if (n == pos) + return GNUNET_OK; GNUNET_assert (h->have == h->size); if (GNUNET_OK != GNUNET_BIO_flush (h)) - return GNUNET_SYSERR; /* error */ + return GNUNET_SYSERR; } - while (pos < n); /* should always be true */ + while (pos < n); GNUNET_break (0); return GNUNET_OK; } /** - * Write a string to a file. + * Internal function to write to a buffer. + * The data is simply copied from the argument buffer to the destination + * buffer. Additionally, attempting to write too much data will result in an + * error instead of implicitly writing less data. * - * @param h handle to open file - * @param s string to write (can be NULL) - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @param h the IO handle + * @param what describes what is being written (for error message creation) + * @param buffer the data to write + * @param n how many bytes should be written + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +write_buffer (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const char *buffer, + size_t n) +{ + if (0 == n) + return GNUNET_OK; + if (h->size - h->have < n) + { + GNUNET_asprintf (&h->emsg, + _ ("Error while writing `%s': %s"), + what, + _ ("Not enough space in buffer")); + return GNUNET_SYSERR; + } + GNUNET_memcpy (h->buffer+h->have, buffer, + n); + h->have += n; + return GNUNET_OK; +} + + +/** + * Write a buffer to an IO handle. + * The destination depends on how the handle was created. + * + * @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 otherwise + */ +int +GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const void *buffer, + size_t n) +{ + const char *src = buffer; + int ret = GNUNET_OK; + + if (NULL != h->emsg) + return GNUNET_SYSERR; + + switch (h->type) + { + case IO_FILE: + ret = write_file (h, what, src, n); + break; + case IO_BUFFER: + ret = write_buffer (h, what, src, n); + break; + default: + break; + } + + return ret; +} + + +/** + * Write a (u)int32 to an IO handle. + * + * @param h the IO handle + * @param what what is being written (for error message creation) + * @param i 32-big integer to write + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +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, what, &big, sizeof (int32_t)); +} + + +/** + * Write a (u)int64 to an IO handle. + * + * @param h the IO handle + * @param what what is being written (for error message creation) + * @param i 64-bit integer to write + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +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, what, &big, sizeof (int64_t)); +} + + +/** + * Write a string to an IO handle. + * + * @param h the IO handle + * @param what what is being written (for error message creation) + * @param s 0-terminated string to write (can be NULL) + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ 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; + uint32_t len; - slen = (uint32_t) ((s == NULL) ? 0 : strlen (s) + 1); - if (GNUNET_OK != GNUNET_BIO_write_int32 (h, slen)) + len = (uint32_t) ((s == NULL) ? 0 : strlen (s) + 1); + if (GNUNET_OK != GNUNET_BIO_write_int32 (h, _ ("string length"), len)) return GNUNET_SYSERR; - if (0 != slen) - return GNUNET_BIO_write (h, s, slen - 1); + if (0 != len) + return GNUNET_BIO_write (h, what, s, len - 1); return GNUNET_OK; } /** - * Write metadata container to a file. + * Write metadata container to a IO handle. * - * @param h handle to open file + * @param h the IO handle + * @param what what is being written (for error message creation) * @param m metadata to write - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ 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); + if (NULL == m) + return GNUNET_BIO_write_int32 (h, what, 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; @@ -572,36 +867,497 @@ GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h, /** - * Write an (u)int32_t. + * Create a read spec to read a certain amount of bytes. * - * @param h hande to open file - * @param i 32-bit integer to write - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @param size how many bytes should be read + * @return the read operation specification */ -int -GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, int32_t i) +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_object (struct GNUNET_BIO_ReadHandle *h, + const char *what, + void *result, + size_t size) { - int32_t big; + struct GNUNET_BIO_ReadSpec rs = { + .what = what, + .type = GNUNET_BIO_SPEC_OBJECT, + .h = h, + .size = size, + .result = result, + }; + + return rs; +} - big = htonl (i); - return GNUNET_BIO_write (h, &big, sizeof(int32_t)); + +/** + * Create a read spec to read an (u)int32. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_int32 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + int32_t *result) +{ + struct GNUNET_BIO_ReadSpec rs = { + .what = what, + .type = GNUNET_BIO_SPEC_INT32, + .h = h, + .size = sizeof (int32_t), + .result = result, + }; + + return rs; } /** - * Write an (u)int64_t. + * Create a read spec to read an (u)int64. * - * @param h hande to open file - * @param i 64-bit integer to write - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_int64 (struct GNUNET_BIO_ReadHandle *h, + const char *what, + int64_t *result) +{ + struct GNUNET_BIO_ReadSpec rs = { + .what = what, + .type = GNUNET_BIO_SPEC_INT64, + .h = h, + .size = sizeof (int64_t), + .result = result, + }; + + return rs; +} + + +/** + * Create a read spec to read a float. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_float (struct GNUNET_BIO_ReadHandle *h, + const char *what, + float *result) +{ + struct GNUNET_BIO_ReadSpec rs = { + .what = what, + .type = GNUNET_BIO_SPEC_FLOAT, + .h = h, + .size = sizeof (float), + .result = result, + }; + + return rs; +} + + +/** + * Create a read spec to read a double. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store the data + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_double (struct GNUNET_BIO_ReadHandle *h, + const char *what, + double *result) +{ + struct GNUNET_BIO_ReadSpec rs = { + .what = what, + .type = GNUNET_BIO_SPEC_DOUBLE, + .h = h, + .size = sizeof (double), + .result = result, + }; + + return rs; +} + + +/** + * Create a read spec to read a 0-terminated string. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result where to store a pointer to the (allocated) string + * can be NULL in certain cases + * @param max_length maximum allowed length for the string + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_string (struct GNUNET_BIO_ReadHandle *h, + const char *what, + char **result, + size_t max_length) +{ + struct GNUNET_BIO_ReadSpec rs = { + .what = what, + .type = GNUNET_BIO_SPEC_STRING, + .h = h, + .size = max_length, + .result = result, + }; + + return rs; +} + + +/** + * Create a read spec to read a metadata container. + * + * @param h the IO handle to read from + * @param what what is being read (for error message creation) + * @param result the buffer to store a pointer to the (allocated) metadata. + * @return the read operation specification + */ +struct GNUNET_BIO_ReadSpec +GNUNET_BIO_read_spec_meta_data (struct GNUNET_BIO_ReadHandle *h, + const char *what, + struct GNUNET_CONTAINER_MetaData **result) +{ + struct GNUNET_BIO_ReadSpec rs = { + .what = what, + .type = GNUNET_BIO_SPEC_META, + .h = h, + .size = MAX_META_DATA, + .result = result, + }; + + return rs; +} + + +/** + * Execute the provided read operations. + * + * @param rs an array of read specs + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise */ int -GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, int64_t i) +GNUNET_BIO_read_spec_commit (struct GNUNET_BIO_ReadSpec *rs) { - int64_t big; + int ret; - big = GNUNET_htonll (i); - return GNUNET_BIO_write (h, &big, sizeof(int64_t)); + for (size_t i=0; NULL!=rs[i].h; ++i) + { + if (NULL != rs[i].h->emsg) + return GNUNET_SYSERR; + switch (rs[i].type) + { + case GNUNET_BIO_SPEC_OBJECT: + ret = GNUNET_BIO_read (rs[i].h, + rs[i].what, + (char *) (rs[i].result), + rs[i].size); + break; + case GNUNET_BIO_SPEC_INT32: + ret = GNUNET_BIO_read_int32 (rs[i].h, + rs[i].what, + (int32_t *) (rs[i].result)); + break; + case GNUNET_BIO_SPEC_INT64: + ret = GNUNET_BIO_read_int64 (rs[i].h, + rs[i].what, + (int64_t *) (rs[i].result)); + break; + case GNUNET_BIO_SPEC_FLOAT: + ret = GNUNET_BIO_read_float (rs[i].h, + rs[i].what, + (float *) (rs[i].result)); + break; + case GNUNET_BIO_SPEC_DOUBLE: + ret = GNUNET_BIO_read_double (rs[i].h, + rs[i].what, + (double *) (rs[i].result)); + break; + case GNUNET_BIO_SPEC_STRING: + ret = GNUNET_BIO_read_string (rs[i].h, + rs[i].what, + (char **) (rs[i].result), + rs[i].size); + break; + case GNUNET_BIO_SPEC_META: + ret = GNUNET_BIO_read_meta_data ( + rs[i].h, + rs[i].what, + (struct GNUNET_CONTAINER_MetaData **) (rs[i].result)); + break; + default: + GNUNET_free_non_null (rs[i].h->emsg); + GNUNET_asprintf (&(rs[i].h->emsg), + _ ("Invalid data type while reading `%s'"), + rs[i].what); + ret = GNUNET_SYSERR; + break; + } + if (GNUNET_OK != ret) + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Create a write spec to write a certain amount of bytes. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the data to write to the handle + * @param size how many bytes should be written + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_object (struct GNUNET_BIO_WriteHandle *h, + const char *what, + void *source, + size_t size) +{ + struct GNUNET_BIO_WriteSpec ws = { + .what = what, + .type = GNUNET_BIO_SPEC_OBJECT, + .h = h, + .size = size, + .source = source, + }; + + return ws; +} + + +/** + * Create a write spec to write an (u)int32. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source a pointer to a 32-bit integer + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_int32 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int32_t *source) +{ + struct GNUNET_BIO_WriteSpec ws = { + .what = what, + .type = GNUNET_BIO_SPEC_INT32, + .h = h, + .size = sizeof (int32_t), + .source = source, + }; + + return ws; +} + + +/** + * Create a write spec to write an (u)int64. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source a pointer to a 64-bit integer + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_int64 (struct GNUNET_BIO_WriteHandle *h, + const char *what, + int64_t *source) +{ + struct GNUNET_BIO_WriteSpec ws = { + .what = what, + .type = GNUNET_BIO_SPEC_INT64, + .h = h, + .size = sizeof (int64_t), + .source = source, + }; + + return ws; +} + + +/** + * Create a write spec to write a float. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source a pointer to a float + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_float (struct GNUNET_BIO_WriteHandle *h, + const char *what, + float *source) +{ + struct GNUNET_BIO_WriteSpec ws = { + .what = what, + .type = GNUNET_BIO_SPEC_FLOAT, + .h = h, + .size = sizeof (float), + .source = source, + }; + + return ws; +} + + +/** + * Create a write spec to write a double. + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source a pointer to a double + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_double (struct GNUNET_BIO_WriteHandle *h, + const char *what, + double *source) +{ + struct GNUNET_BIO_WriteSpec ws = { + .what = what, + .type = GNUNET_BIO_SPEC_DOUBLE, + .h = h, + .size = sizeof (double), + .source = source, + }; + + return ws; +} + + +/** + * Create a write spec to write a string + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the 0-terminated string to write + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_string (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const char *source) +{ + struct GNUNET_BIO_WriteSpec ws = { + .what = what, + .type = GNUNET_BIO_SPEC_STRING, + .h = h, + .size = ((NULL == source) ? 0 : strlen (source)), + .source = source, + }; + + return ws; +} + + +/** + * Create a write spec to write a metadata container + * + * @param h the IO handle to write to + * @param what what is being written (for error message creation) + * @param source the metadata to write + * @return the write operation specification + */ +struct GNUNET_BIO_WriteSpec +GNUNET_BIO_write_spec_meta_data (struct GNUNET_BIO_WriteHandle *h, + const char *what, + const struct GNUNET_CONTAINER_MetaData *source) +{ + struct GNUNET_BIO_WriteSpec ws = { + .what = what, + .type = GNUNET_BIO_SPEC_META, + .h = h, + .size = MAX_META_DATA, + .source = source, + }; + + return ws; +} + + +/** + * Execute the provided write operations. + * + * @param ws an array of write specs + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise + */ +int +GNUNET_BIO_write_spec_commit (struct GNUNET_BIO_WriteSpec *ws) +{ + int ret; + for (size_t i=0; NULL!=ws[i].h; ++i) + { + if (NULL != ws[i].h->emsg) + return GNUNET_SYSERR; + switch (ws[i].type) + { + case GNUNET_BIO_SPEC_OBJECT: + ret = GNUNET_BIO_write (ws[i].h, + ws[i].what, + ws[i].source, + ws[i].size); + break; + case GNUNET_BIO_SPEC_INT32: + ret = GNUNET_BIO_write_int32 (ws[i].h, + ws[i].what, + *((int32_t *) ws[i].source)); + break; + case GNUNET_BIO_SPEC_INT64: + ret = GNUNET_BIO_write_int64 (ws[i].h, + ws[i].what, + *((int64_t *) ws[i].source)); + break; + case GNUNET_BIO_SPEC_FLOAT: + ret = GNUNET_BIO_write_float (ws[i].h, + ws[i].what, + *((float *) ws[i].source)); + break; + case GNUNET_BIO_SPEC_DOUBLE: + ret = GNUNET_BIO_write_double (ws[i].h, + ws[i].what, + *((double *) ws[i].source)); + break; + case GNUNET_BIO_SPEC_STRING: + ret = GNUNET_BIO_write_string (ws[i].h, + ws[i].what, + (const char *) ws[i].source); + break; + case GNUNET_BIO_SPEC_META: + ret = GNUNET_BIO_write_meta_data ( + ws[i].h, + ws[i].what, + (const struct GNUNET_CONTAINER_MetaData *) ws[i].source); + break; + default: + GNUNET_free_non_null (ws[i].h->emsg); + GNUNET_asprintf (&(ws[i].h->emsg), + _ ("Invalid data type while writing `%s'"), + ws[i].what); + ret = GNUNET_SYSERR; + break; + } + if (GNUNET_OK != ret) + return GNUNET_SYSERR; + } + return GNUNET_OK; } -- 2.26.2