From cf3772b2b7dd91ae8f0815d28cc7ed26e462d938 Mon Sep 17 00:00:00 2001
From: Kevin McCarthy <kevin@8t8.us>
Date: Sat, 6 Oct 2018 13:53:33 -0700
Subject: [PATCH] Add buffer pool functions.

Also add a few helper functions: mutt_buffer_clear(),
mutt_buffer_strcpy(), and a macro mutt_b2s to grab the buffer data as
a const char *.
---
 init.c        |   1 +
 main.c        |   1 +
 mutt/buffer.c | 131 +++++++++++++++++++++++++++++++++++++++++---------
 mutt/buffer.h |  35 +++++++++-----
 4 files changed, 133 insertions(+), 35 deletions(-)

diff --git a/init.c b/init.c
index d7dba3514..4dfad56bf 100644
--- a/init.c
+++ b/init.c
@@ -2870,6 +2870,7 @@ int mutt_init(bool skip_sys_rc, struct ListHead *commands)
   TagFormats = mutt_hash_create(64, 0);
 
   mutt_menu_init();
+  mutt_buffer_pool_init();
 
   snprintf(AttachmentMarker, sizeof(AttachmentMarker), "\033]9;%" PRIu64 "\a",
            mutt_rand64());
diff --git a/main.c b/main.c
index cc6546361..0b0c740ca 100644
--- a/main.c
+++ b/main.c
@@ -1240,6 +1240,7 @@ main_exit:
   mutt_list_free(&queries);
   crypto_module_free();
   mutt_window_free();
+  mutt_buffer_pool_free();
   mutt_envlist_free();
   mutt_free_opts();
   mutt_free_keys();
diff --git a/mutt/buffer.c b/mutt/buffer.c
index c0f10bf5d..58c37e2fc 100644
--- a/mutt/buffer.c
+++ b/mutt/buffer.c
@@ -33,9 +33,14 @@
 #include <stdio.h>
 #include <string.h>
 #include "buffer.h"
+#include "logging.h"
 #include "memory.h"
 #include "string2.h"
 
+static size_t BufferPoolCount = 0;
+static size_t BufferPoolLen = 0;
+static struct Buffer **BufferPool = NULL;
+
 /**
  * mutt_buffer_new - Create and initialise a Buffer
  * @retval ptr New Buffer
@@ -53,32 +58,32 @@ struct Buffer *mutt_buffer_new(void)
 
 /**
  * mutt_buffer_init - Initialise a new Buffer
- * @param b Buffer to initialise
+ * @param buf Buffer to initialise
  * @retval ptr Initialised Buffer
  *
  * This must not be called on a Buffer that already contains data.
  */
-struct Buffer *mutt_buffer_init(struct Buffer *b)
+struct Buffer *mutt_buffer_init(struct Buffer *buf)
 {
-  if (!b)
+  if (!buf)
     return NULL;
-  memset(b, 0, sizeof(struct Buffer));
-  return b;
+  memset(buf, 0, sizeof(struct Buffer));
+  return buf;
 }
 
 /**
  * mutt_buffer_reset - Reset an existing Buffer
- * @param b Buffer to reset
+ * @param buf Buffer to reset
  *
  * This can be called on a Buffer to reset the pointers,
  * effectively emptying it.
  */
-void mutt_buffer_reset(struct Buffer *b)
+void mutt_buffer_reset(struct Buffer *buf)
 {
-  if (!b)
+  if (!buf)
     return;
-  memset(b->data, 0, b->dsize);
-  b->dptr = b->data;
+  memset(buf->data, 0, buf->dsize);
+  buf->dptr = buf->data;
 }
 
 /**
@@ -117,12 +122,7 @@ size_t mutt_buffer_add(struct Buffer *buf, const char *s, size_t len)
     return 0;
 
   if ((buf->dptr + len + 1) > (buf->data + buf->dsize))
-  {
-    size_t offset = buf->dptr - buf->data;
-    buf->dsize += (len < 128) ? 128 : len + 1;
-    mutt_mem_realloc(&buf->data, buf->dsize);
-    buf->dptr = buf->data + offset;
-  }
+    mutt_buffer_increase_size(buf, buf->dsize + (len < 128 ? 128 : len + 1));
   if (!buf->dptr)
     return 0;
   memcpy(buf->dptr, s, len);
@@ -172,9 +172,7 @@ int mutt_buffer_printf(struct Buffer *buf, const char *fmt, ...)
   if (blen == 0)
   {
     blen = 128;
-    buf->dsize += blen;
-    mutt_mem_realloc(&buf->data, buf->dsize);
-    buf->dptr = buf->data + doff;
+    mutt_buffer_increase_size(buf, buf->dsize + blen);
   }
   len = vsnprintf(buf->dptr, blen, fmt, ap);
   if (len >= blen)
@@ -182,9 +180,7 @@ int mutt_buffer_printf(struct Buffer *buf, const char *fmt, ...)
     blen = ++len - blen;
     if (blen < 128)
       blen = 128;
-    buf->dsize += blen;
-    mutt_mem_realloc(&buf->data, buf->dsize);
-    buf->dptr = buf->data + doff;
+    mutt_buffer_increase_size(buf, buf->dsize + blen);
     len = vsnprintf(buf->dptr, len, fmt, ap_retry);
   }
   if (len > 0)
@@ -255,6 +251,19 @@ struct Buffer *mutt_buffer_alloc(size_t size)
   return b;
 }
 
+/**
+ * mutt_buffer_strcpy - Copy a string into a Buffer
+ * @param buf Buffer to overwrite
+ * @param s   String to copy
+ *
+ * Overwrites any existing content.
+ */
+void mutt_buffer_strcpy(struct Buffer *buf, const char *s)
+{
+  mutt_buffer_reset(buf);
+  mutt_buffer_addstr(buf, s);
+}
+
 /**
  * mutt_buffer_increase_size - Increase the allocated size of a buffer
  * @param buf      Buffer to change
@@ -273,3 +282,81 @@ void mutt_buffer_increase_size(struct Buffer *buf, size_t new_size)
   mutt_mem_realloc(&buf->data, buf->dsize);
   buf->dptr = buf->data + offset;
 }
+
+/**
+ * increase_buffer_pool - Increase the size of the Buffer pool
+ */
+static void increase_buffer_pool(void)
+{
+  struct Buffer *newbuf;
+
+  BufferPoolLen += 5;
+  mutt_mem_realloc(&BufferPool, BufferPoolLen * sizeof(struct Buffer *));
+  while (BufferPoolCount < 5)
+  {
+    newbuf = mutt_buffer_alloc(LONG_STRING);
+    BufferPool[BufferPoolCount++] = newbuf;
+  }
+}
+
+/**
+ * mutt_buffer_pool_init - Initialise the Buffer pool
+ */
+void mutt_buffer_pool_init(void)
+{
+  increase_buffer_pool();
+}
+
+/**
+ * mutt_buffer_pool_free - Release the Buffer pool
+ */
+void mutt_buffer_pool_free(void)
+{
+  if (BufferPoolCount != BufferPoolLen)
+  {
+    mutt_debug(1, "Buffer pool leak: %zu/%zu\n", BufferPoolCount, BufferPoolLen);
+  }
+  while (BufferPoolCount)
+    mutt_buffer_free(&BufferPool[--BufferPoolCount]);
+  FREE(&BufferPool);
+  BufferPoolLen = 0;
+}
+
+/**
+ * mutt_buffer_pool_get - Get a Buffer from the pool
+ * @retval ptr Buffer
+ */
+struct Buffer *mutt_buffer_pool_get(void)
+{
+  if (BufferPoolCount == 0)
+    increase_buffer_pool();
+  return BufferPool[--BufferPoolCount];
+}
+
+/**
+ * mutt_buffer_pool_release - Free a Buffer from the pool
+ * @param pbuf Buffer to free
+ */
+void mutt_buffer_pool_release(struct Buffer **pbuf)
+{
+  if (!pbuf || !*pbuf)
+    return;
+
+  if (BufferPoolCount >= BufferPoolLen)
+  {
+    mutt_debug(1, "Internal buffer pool error\n");
+    mutt_buffer_free(pbuf);
+    return;
+  }
+
+  struct Buffer *buf = *pbuf;
+  if (buf->dsize > (LONG_STRING * 2))
+  {
+    buf->dsize = LONG_STRING;
+    mutt_mem_realloc(&buf->data, buf->dsize);
+  }
+  mutt_buffer_reset(buf);
+  BufferPool[BufferPoolCount++] = buf;
+
+  *pbuf = NULL;
+}
diff --git a/mutt/buffer.h b/mutt/buffer.h
index 32892621d..2c0fbdaf6 100644
--- a/mutt/buffer.h
+++ b/mutt/buffer.h
@@ -38,19 +38,28 @@ struct Buffer
   int destroy;  /**< destroy 'data' when done? */
 };
 
-#define MoreArgs(p) (*p->dptr && (*p->dptr != ';') && (*p->dptr != '#'))
-
-size_t         mutt_buffer_add(struct Buffer *buf, const char *s, size_t len);
-size_t         mutt_buffer_addch(struct Buffer *buf, char c);
-size_t         mutt_buffer_addstr(struct Buffer *buf, const char *s);
-struct Buffer *mutt_buffer_alloc(size_t size);
-void           mutt_buffer_free(struct Buffer **p);
-struct Buffer *mutt_buffer_from(const char *seed);
+/* Convert a buffer to a const char * "string" */
+#define mutt_b2s(buf) (buf->data ? (const char *) buf->data : "")
+
+#define MoreArgs(buf) (*buf->dptr && (*buf->dptr != ';') && (*buf->dptr != '#'))
+
+size_t         mutt_buffer_add          (struct Buffer *buf, const char *s, size_t len);
+size_t         mutt_buffer_addch        (struct Buffer *buf, char c);
+size_t         mutt_buffer_addstr       (struct Buffer *buf, const char *s);
+struct Buffer *mutt_buffer_alloc        (size_t size);
+void           mutt_buffer_free         (struct Buffer **p);
+struct Buffer *mutt_buffer_from         (const char *seed);
 void           mutt_buffer_increase_size(struct Buffer *buf, size_t new_size);
-struct Buffer *mutt_buffer_init(struct Buffer *b);
-bool           mutt_buffer_is_empty(const struct Buffer *buf);
-struct Buffer *mutt_buffer_new(void);
-int            mutt_buffer_printf(struct Buffer *buf, const char *fmt, ...);
-void           mutt_buffer_reset(struct Buffer *b);
+struct Buffer *mutt_buffer_init         (struct Buffer *buf);
+bool           mutt_buffer_is_empty     (const struct Buffer *buf);
+struct Buffer *mutt_buffer_new          (void);
+int            mutt_buffer_printf       (struct Buffer *buf, const char *fmt, ...);
+void           mutt_buffer_reset        (struct Buffer *buf);
+void           mutt_buffer_strcpy       (struct Buffer *buf, const char *s);
+
+void           mutt_buffer_pool_free    (void);
+struct Buffer *mutt_buffer_pool_get     (void);
+void           mutt_buffer_pool_init    (void);
+void           mutt_buffer_pool_release (struct Buffer **pbuf);
 
 #endif /* MUTT_LIB_BUFFER_H */
-- 
2.40.0