Hello,

the patch attached to this e-mail is another attempt at improving BIO,
using Christian's suggestions.

It adds a read/write spec API based on callbacks, read/write operations
on in-memory buffers and adds a new API to "gnunet_buffer_lib.h".

Some things addressed below:

Christian Grothoff <[email protected]> writes:

> Indeed, simply adding a GNUNET_buffer_reap() that returns a void * +
> size_t instead of a C-string would do the trick, and should be trivial.

I implemented this function, but also changed

*buf = struct GNUNET_Buffer { 0 };

and similar to a call to `memset'.

I did this because I wasn't 100% sure about handling a `GNUNET_Buffer'
inside `GNUNET_BIO_WriteHandle' the way it was suggested in
"gnunet_buffer_lib.h", so I took the easy way out and allocated the
buffer dynamically.  Because the assignment would change the pointer, a
slightly more expensive call to `memset' is used.

Slightly OT:
> Likely firemail.cc runs some way too aggressive spam filter? Anyway,
> cc'ing you directly may help...

The fact is, I also receive my own mails to the list and when I sent the
very first mail I didn't get anything.  That was what prompted me to
check the web archive.  Anyway, I received my second mail (the one you
replied to) so everything seems to be fixed, whatever the problem was.

Thanks,
A.V.

>From 8ac66216adada0dd45c3a29caaf7a4e63bb2d626 Mon Sep 17 00:00:00 2001
From: Alessio Vanni <[email protected]>
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

Reply via email to