gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-exchange] branch master updated (4a8fcc27 -> d6ca052


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated (4a8fcc27 -> d6ca052d)
Date: Thu, 17 Oct 2019 15:50:20 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a change to branch master
in repository exchange.

    from 4a8fcc27 New error code,
     new 7e149e9a gitignore
     new d6ca052d implement buffer API and use it for URL construction

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .gitignore               |   2 +
 src/include/taler_util.h | 164 ++++++++++++++++++
 src/util/test_url.c      |   6 +-
 src/util/util.c          | 428 ++++++++++++++++++++++++++++++++++++-----------
 4 files changed, 497 insertions(+), 103 deletions(-)

diff --git a/.gitignore b/.gitignore
index f2ef1e4f..5bb943a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -118,3 +118,5 @@ src/lib/test_exchange_api_overlapping_keys_bug
 src/lib/test_exchange_api_home/.local/share/taler/exchange/revocations/
 src/wire-plugins/test_wire_plugin_legacy_taler_bank
 uncrustify.cfg
+vgcore.*
+tags
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 9cfcb3dc..c767a32e 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -74,6 +74,151 @@
                        __LINE__, gcry_strerror (rc)); abort (); } while (0)
 
 
+/**
+ * Dynamically growing buffer.  Can be used to construct
+ * strings and other objects with dynamic size.
+ *
+ * This structure should, in most cases, be stack-allocated and
+ * zero-initialized, like:
+ *
+ *   struct TALER_Buffer my_buffer = { 0 };
+ */
+struct TALER_Buffer
+{
+  /**
+   * Capacity of the buffer.
+   */
+  size_t capacity;
+
+  /**
+   * Current write position.
+   */
+  size_t position;
+
+  /**
+   * Backing memory.
+   */
+  char *mem;
+
+  /**
+   * Log a warning if the buffer is grown over its initially allocated 
capacity.
+   */
+  int warn_grow;
+};
+
+
+/**
+ * Initialize a buffer with the given capacity.
+ *
+ * When a buffer is allocated with this function, a warning is logged
+ * when the buffer exceeds the initial capacity.
+ *
+ * @param buf the buffer to initialize
+ * @param capacity the capacity (in bytes) to allocate for @a buf
+ */
+void
+TALER_buffer_prealloc (struct TALER_Buffer *buf, size_t capacity);
+
+
+/**
+ * Make sure that at least @a n bytes remaining in the buffer.
+ *
+ * @param buf buffer to potentially grow
+ * @param n number of bytes that should be available to write
+ */
+void
+TALER_buffer_ensure_remaining (struct TALER_Buffer *buf, size_t n);
+
+
+/**
+ * Write bytes to the buffer.
+ *
+ * Grows the buffer if necessary.
+ *
+ * @param buf buffer to write to
+ * @param data data to read from
+ * @param len number of bytes to copy from @a data to @a buf
+ *
+ */
+void
+TALER_buffer_write (struct TALER_Buffer *buf, const char *data, size_t len);
+
+
+/**
+ * Write a 0-terminated string to a buffer, excluding the 0-terminator.
+ *
+ * Grows the buffer if necessary.
+ *
+ * @param buf the buffer to write to
+ * @param str the string to write to @a buf
+ */
+void
+TALER_buffer_write_str (struct TALER_Buffer *buf, const char *str);
+
+
+/**
+ * Write a path component to a buffer, ensuring that
+ * there is exactly one slash between the previous contents
+ * of the buffer and the new string.
+ *
+ * @param buf buffer to write to
+ * @param str string containing the new path component
+ */
+void
+TALER_buffer_write_path (struct TALER_Buffer *buf, const char *str);
+
+
+/**
+ * Write a 0-terminated formatted string to a buffer, excluding the
+ * 0-terminator.
+ *
+ * Grows the buffer if necessary.
+ *
+ * @param buf the buffer to write to
+ * @param fmt format string
+ * @param ... format arguments
+ */
+void
+TALER_buffer_write_fstr (struct TALER_Buffer *buf, const char *fmt, ...);
+
+
+/**
+ * Write a 0-terminated formatted string to a buffer, excluding the
+ * 0-terminator.
+ *
+ * Grows the buffer if necessary.
+ *
+ * @param buf the buffer to write to
+ * @param fmt format string
+ * @param args format argument list
+ */
+void
+TALER_buffer_write_vfstr (struct TALER_Buffer *buf, const char *fmt, va_list 
args);
+
+
+/**
+ * Clear the buffer and return the string it contained.
+ * The caller is responsible to eventually #GNUNET_free
+ * the returned string.
+ *
+ * The returned string is always 0-terminated.
+ *
+ * @param buf the buffer to reap the string from
+ * @returns the buffer contained in the string
+ */
+char *
+TALER_buffer_reap_str (struct TALER_Buffer *buf);
+
+
+/**
+ * Free the backing memory of the given buffer.
+ * Does not free the memory of the buffer control structure,
+ * which is typically stack-allocated.
+ */
+void
+TALER_buffer_clear (struct TALER_Buffer *buf);
+
+
 /**
  * Initialize Gcrypt library.
  */
@@ -202,6 +347,25 @@ TALER_url_absolute_raw (const char *proto,
                         ...);
 
 
+/**
+ * Make an absolute URL for the given parameters.
+ *
+ * @param proto protocol for the URL (typically https)
+ * @param host hostname for the URL
+ * @param prefix prefix for the URL
+ * @param path path for the URL
+ * @param args NULL-terminated key-value pairs (char *) for query parameters,
+ *        the value will be url-encoded
+ * @returns the URL, must be freed with #GNUNET_free
+ */
+char *
+TALER_url_absolute_raw_va (const char *proto,
+                     const char *host,
+                     const char *prefix,
+                     const char *path,
+                     va_list args);
+
+
 /**
  * Make an absolute URL for a given MHD connection.
  *
diff --git a/src/util/test_url.c b/src/util/test_url.c
index f6aab0dc..da43f81a 100644
--- a/src/util/test_url.c
+++ b/src/util/test_url.c
@@ -32,7 +32,11 @@
 static void
 cf (char *input, char *expected)
 {
-  GNUNET_assert (0 == strcmp (input, expected));
+  if (0 != strcmp (input, expected))
+  {
+    printf ("got '%s' but expected '%s'\n", input, expected);
+    GNUNET_assert (0);
+  }
   GNUNET_free (input);
 }
 
diff --git a/src/util/util.c b/src/util/util.c
index 3341aa29..0520d031 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -193,89 +193,57 @@ is_reserved (char c)
   return GNUNET_YES;
 }
 
+/**
+ * Get the length of a string after it has been
+ * urlencoded.
+ *
+ * @param s the string
+ * @returns the size of the urlencoded @a s
+ */
+static size_t
+urlencode_len (const char *s)
+{
+  size_t len = 0;
+  for (; *s != '\0'; len++, s++)
+    if (GNUNET_YES == is_reserved (*s))
+      len += 2;
+  return len;
+}
 
 /**
  * URL-encode a string according to rfc3986.
  *
+ * @param buf buffer to write the result to
  * @param s string to encode
- * @returns the urlencoded string, the caller must free it with GNUNET_free
  */
-char *
-TALER_urlencode (const char *s)
+static void
+buffer_write_urlencode (struct TALER_Buffer *buf, const char *s)
 {
-  unsigned int new_size;
-  unsigned int i;
-  unsigned int t;
-  char *out;
+  TALER_buffer_ensure_remaining (buf, urlencode_len (s) + 1);
 
-  new_size = strlen (s);
-  for (i = 0; i < strlen (s); i++)
-    if (GNUNET_YES == is_reserved (s[i]))
-      new_size += 2;
-  out = GNUNET_malloc (new_size + 1);
-  for (i = 0, t = 0; i < strlen (s); i++, t++)
+  for (size_t i = 0; i < strlen (s); i++)
   {
     if (GNUNET_YES == is_reserved (s[i]))
-    {
-      snprintf (&out[t], 4, "%%%02X", s[i]);
-      t += 2;
-      continue;
-    }
-    out[t] = s[i];
+      TALER_buffer_write_fstr (buf, "%%%02X", s[i]);
+    else
+      buf->mem[buf->position++] = s[i];
   }
-  return out;
 }
 
 
 /**
- * Grow a string in a buffer with the given size.
- * The buffer is re-allocated if necessary.
- *
- * @param s pointer to string buffer
- * @param p the string to append
- * @param n pointer to the allocated size of n
- * @returns pointer to the resulting buffer,
- *          might differ from @a s (!!)
- */
-static char *
-grow_string (char **s, const char *p, size_t *n)
-{
-  for (; strlen (*s) + strlen (p) >= *n; *n *= 2);
-  *s = GNUNET_realloc (*s, *n);
-  GNUNET_assert (NULL != s);
-  strncat (*s, p, *n);
-  return *s;
-}
-
-
-/**
- * Grow a string in a buffer with the given size.
- * The buffer is re-allocated if necessary.
- *
- * Ensures that slashes are removed or added when joining paths.
+ * URL-encode a string according to rfc3986.
  *
- * @param s pointer to string buffer
- * @param p the string to append
- * @param n pointer to the allocated size of n
- * @returns pointer to the resulting buffer,
- *          might differ from @a s (!!)
+ * @param s string to encode
+ * @returns the urlencoded string, the caller must free it with GNUNET_free
  */
-static char *
-grow_string_path (char **s, const char *p, size_t *n)
+char *
+TALER_urlencode (const char *s)
 {
-  char a = (0 == strlen (*s)) ? '\0' : (*s)[strlen (*s) - 1];
-  char b = (0 == strlen (p)) ? '\0' : p[0];
+  struct TALER_Buffer buf = { 0 };
 
-  if ( (a == '/') && (b == '/'))
-  {
-    p++;
-  }
-  else if ( (a != '/') && (b != '/'))
-  {
-    if (NULL == (*s = grow_string (s, "/", n)))
-      return NULL;
-  }
-  return grow_string (s, p, n);
+  buffer_write_urlencode (&buf, s);
+  return TALER_buffer_reap_str (&buf);
 }
 
 
@@ -286,36 +254,73 @@ grow_string_path (char **s, const char *p, size_t *n)
  * @param path path of the url
  * @param ... NULL-terminated key-value pairs (char *) for query parameters,
  *        the value will be url-encoded
- * @returns the URL, must be freed with #GNUNET_free
+ * @returns the URL (must be freed with #GNUNET_free) or
+ *          NULL if an error occured.
  */
 char *
 TALER_url_join (const char *base_url,
                 const char *path,
                 ...)
 {
-  size_t n = 256;
-  char *res = GNUNET_malloc (n);
   unsigned int iparam = 0;
-  char *enc;
   va_list args;
+  struct TALER_Buffer buf = { 0 };
+  size_t len = 0;
 
-  GNUNET_assert (NULL != res);
   GNUNET_assert (NULL != base_url);
   GNUNET_assert (NULL != path);
-  GNUNET_assert (strlen (base_url) > 0);
 
-  // Must be an actual base URL!
-  GNUNET_assert ('/' == base_url[strlen (base_url) - 1]);
+  if (strlen (base_url) == 0)
+  {
+    /* base URL can't be empty */
+    GNUNET_break (0);
+    return NULL;
+  }
 
-  // Path must be relative to existing path of base URL
-  GNUNET_assert ('/' != path[0]);
+  if ('/' != base_url[strlen (base_url) - 1])
+  {
+    /* Must be an actual base URL! */
+    GNUNET_break (0);
+    return NULL;
+  }
 
-  grow_string (&res, base_url, &n);
+  if ('/' == path[0])
+  {
+    /* The path must be relative. */
+    GNUNET_break (0);
+    return NULL;
+  }
 
-  grow_string_path (&res, path, &n);
+  // Path should be relative to existing path of base URL
+  GNUNET_break ('/' != path[0]);
+
+  if ('/' == path[0])
+    GNUNET_break (0);
+
+  /* 1st pass: compute length */
+  len += strlen (base_url) + strlen (path);
 
   va_start (args, path);
+  while (1)
+  {
+    char *key;
+    char *value;
+    key = va_arg (args, char *);
+    if (NULL == key)
+      break;
+    value = va_arg (args, char *);
+    if (NULL == value)
+      continue;
+    len += urlencode_len (value) + strlen (key) + 2;
+  }
+  va_end (args);
 
+  TALER_buffer_prealloc (&buf, len);
+
+  TALER_buffer_write_str (&buf, base_url);
+  TALER_buffer_write_str (&buf, path);
+
+  va_start (args, path);
   while (1)
   {
     char *key;
@@ -326,18 +331,15 @@ TALER_url_join (const char *base_url,
     value = va_arg (args, char *);
     if (NULL == value)
       continue;
-    grow_string (&res, (0 == iparam) ? "?" : "&", &n);
+    TALER_buffer_write_str (&buf, (0 == iparam) ? "?" : "&");
     iparam++;
-    grow_string (&res, key, &n);
-    grow_string (&res, "=", &n);
-    enc = TALER_urlencode (value);
-    grow_string (&res, enc, &n);
-    GNUNET_free (enc);
+    TALER_buffer_write_str (&buf, key);
+    TALER_buffer_write_str (&buf, "=");
+    buffer_write_urlencode (&buf, value);
   }
-
   va_end (args);
 
-  return res;
+  return TALER_buffer_reap_str (&buf);
 }
 
 
@@ -353,25 +355,45 @@ TALER_url_join (const char *base_url,
  * @returns the URL, must be freed with #GNUNET_free
  */
 char *
-url_absolute_raw_va (const char *proto,
+TALER_url_absolute_raw_va (const char *proto,
                      const char *host,
                      const char *prefix,
                      const char *path,
                      va_list args)
 {
-  size_t n = 256;
-  char *res = GNUNET_malloc (n);
-  char *enc;
+  struct TALER_Buffer buf = { 0 };
   unsigned int iparam = 0;
+  size_t len = 0;
+  va_list args2;
 
-  grow_string (&res, proto, &n);
-  grow_string (&res, "://", &n);
-  grow_string (&res, host, &n);
+  len += strlen (proto) + strlen( "://") + strlen (host);
+  len += strlen (prefix) + strlen (path);
 
-  grow_string_path (&res, prefix, &n);
+  va_copy (args2, args);
+  while (1)
+  {
+    char *key;
+    char *value;
+    key = va_arg (args2, char *);
+    if (NULL == key)
+      break;
+    value = va_arg (args2, char *);
+    if (NULL == value)
+      continue;
+    len += urlencode_len (value) + strlen (key) + 2;
+  }
+  va_end (args2);
+
+  TALER_buffer_prealloc (&buf, len);
 
-  grow_string_path (&res, path, &n);
+  TALER_buffer_write_str (&buf, proto);
+  TALER_buffer_write_str (&buf, "://");
+  TALER_buffer_write_str (&buf, host);
 
+  TALER_buffer_write_path (&buf, prefix);
+  TALER_buffer_write_path (&buf, path);
+
+  va_copy (args2, args);
   while (1)
   {
     char *key;
@@ -382,16 +404,15 @@ url_absolute_raw_va (const char *proto,
     value = va_arg (args, char *);
     if (NULL == value)
       continue;
-    grow_string (&res, (0 == iparam) ? "?" : "&", &n);
+    TALER_buffer_write_str (&buf, (0 == iparam) ? "?" : "&");
     iparam++;
-    grow_string (&res, key, &n);
-    grow_string (&res, "=", &n);
-    enc = TALER_urlencode (value);
-    grow_string (&res, enc, &n);
-    GNUNET_free (enc);
+    TALER_buffer_write_str (&buf, key);
+    TALER_buffer_write_str (&buf, "=");
+    buffer_write_urlencode (&buf, value);
   }
+  va_end (args2);
 
-  return res;
+  return TALER_buffer_reap_str (&buf);
 }
 
 
@@ -417,7 +438,7 @@ TALER_url_absolute_raw (const char *proto,
   va_list args;
 
   va_start (args, path);
-  result = url_absolute_raw_va (proto, host, prefix, path, args);
+  result = TALER_url_absolute_raw_va (proto, host, prefix, path, args);
   va_end (args);
   return result;
 }
@@ -516,10 +537,213 @@ TALER_url_absolute_mhd (struct MHD_Connection 
*connection,
   }
 
   va_start (args, path);
-  result = url_absolute_raw_va (proto, host, prefix, path, args);
+  result = TALER_url_absolute_raw_va (proto, host, prefix, path, args);
   va_end (args);
   return result;
 }
 
 
+/**
+ * Initialize a buffer with the given capacity.
+ *
+ * When a buffer is allocated with this function, a warning is logged
+ * when the buffer exceeds the initial capacity.
+ *
+ * @param buf the buffer to initialize
+ * @param capacity the capacity (in bytes) to allocate for @a buf
+ */
+void
+TALER_buffer_prealloc (struct TALER_Buffer *buf, size_t capacity)
+{
+  /* Buffer should be zero-initialized */
+  GNUNET_assert (0 == buf->mem);
+  GNUNET_assert (0 == buf->capacity);
+  GNUNET_assert (0 == buf->position);
+  buf->mem = GNUNET_malloc (capacity);
+  buf->capacity = capacity;
+  buf->warn_grow = GNUNET_YES;
+}
+
+
+/**
+ * Make sure that at least @a n bytes remaining in the buffer.
+ *
+ * @param buf buffer to potentially grow
+ * @param n number of bytes that should be available to write
+ */
+void
+TALER_buffer_ensure_remaining (struct TALER_Buffer *buf, size_t n)
+{
+  size_t new_capacity = buf->position + n;
+
+  if (new_capacity <= buf->capacity)
+    return;
+  /* warn if calculation of expected size was wrong */
+  GNUNET_break (GNUNET_YES != buf->warn_grow);
+  if (new_capacity < buf->capacity * 2)
+    new_capacity = buf->capacity * 2;
+  buf->capacity = new_capacity;
+  if (NULL != buf->mem)
+    buf->mem = GNUNET_realloc (buf->mem, new_capacity);
+  else
+    buf->mem = GNUNET_malloc (new_capacity);
+}
+
+
+/**
+ * Write bytes to the buffer.
+ *
+ * Grows the buffer if necessary.
+ *
+ * @param buf buffer to write to
+ * @param data data to read from
+ * @param len number of bytes to copy from @a data to @a buf
+ *
+ */
+void
+TALER_buffer_write (struct TALER_Buffer *buf, const char *data, size_t len)
+{
+  TALER_buffer_ensure_remaining (buf, len);
+  memcpy (buf->mem + buf->position, data, len);
+  buf->position += len;
+}
+
+
+/**
+ * Write a 0-terminated string to a buffer, excluding the 0-terminator.
+ *
+ * @param buf the buffer to write to
+ * @param str the string to write to @a buf
+ */
+void
+TALER_buffer_write_str (struct TALER_Buffer *buf, const char *str)
+{
+  size_t len = strlen (str);
+
+  TALER_buffer_write (buf, str, len);
+}
+
+
+/**
+ * Clear the buffer and return the string it contained.
+ * The caller is responsible to eventually #GNUNET_free
+ * the returned string.
+ *
+ * The returned string is always 0-terminated.
+ *
+ * @param buf the buffer to reap the string from
+ * @returns the buffer contained in the string
+ */
+char *
+TALER_buffer_reap_str (struct TALER_Buffer *buf)
+{
+  char *res;
+
+  /* ensure 0-termination */
+  if ( (0 == buf->position) || ('\0' != buf->mem[buf->position - 1]))
+  {
+    TALER_buffer_ensure_remaining (buf, 1);
+    buf->mem[buf->position++] = '\0';
+  }
+  res = buf->mem;
+  *buf = (struct TALER_Buffer) { 0 };
+  return res;
+}
+
+
+/**
+ * Free the backing memory of the given buffer.
+ * Does not free the memory of the buffer control structure,
+ * which is typically stack-allocated.
+ */
+void
+TALER_buffer_clear (struct TALER_Buffer *buf)
+{
+  GNUNET_free_non_null (buf->mem);
+  *buf = (struct TALER_Buffer) { 0 };
+}
+
+
+/**
+ * Write a path component to a buffer, ensuring that
+ * there is exactly one slash between the previous contents
+ * of the buffer and the new string.
+ *
+ * @param buf buffer to write to
+ * @param str string containing the new path component
+ */
+void
+TALER_buffer_write_path (struct TALER_Buffer *buf, const char *str)
+{
+  size_t len = strlen (str);
+
+  if (0 == len)
+    return;
+  if ('/' == str[0])
+  {
+    str++;
+    len--;
+  }
+  if ( (0 == buf->position) || ('/' != buf->mem[buf->position]) )
+  {
+    TALER_buffer_ensure_remaining (buf, 1);
+    buf->mem[buf->position++] = '/';
+  }
+  TALER_buffer_write (buf, str, len);
+}
+
+
+/**
+ * Write a 0-terminated formatted string to a buffer, excluding the
+ * 0-terminator.
+ *
+ * Grows the buffer if necessary.
+ *
+ * @param buf the buffer to write to
+ * @param fmt format string
+ * @param ... format arguments
+ */
+void
+TALER_buffer_write_fstr (struct TALER_Buffer *buf, const char *fmt, ...)
+{
+  va_list args;
+
+  va_start (args, fmt);
+  TALER_buffer_write_vfstr (buf, fmt, args);
+  va_end (args);
+}
+
+
+/**
+ * Write a 0-terminated formatted string to a buffer, excluding the
+ * 0-terminator.
+ *
+ * Grows the buffer if necessary.
+ *
+ * @param buf the buffer to write to
+ * @param fmt format string
+ * @param args format argument list
+ */
+void
+TALER_buffer_write_vfstr (struct TALER_Buffer *buf, const char *fmt, va_list 
args)
+{
+  size_t res;
+  va_list args2;
+
+  va_copy (args2, args);
+  res = vsnprintf (NULL, 0, fmt, args2);
+  va_end (args2);
+
+  GNUNET_assert (res >= 0);
+  TALER_buffer_ensure_remaining (buf, res + 1);
+
+  va_copy (args2, args);
+  res = vsnprintf (buf->mem + buf->position, res + 1, fmt, args2);
+  va_end (args2);
+
+  GNUNET_assert (res >= 0);
+  buf->position += res;
+  GNUNET_assert (buf->position <= buf->capacity);
+}
+
 /* end of util.c */

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]