aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/ecpg/preproc/util.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-10-14 13:55:08 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2024-10-14 13:55:08 -0400
commit1acd0f55274fab8c936779a0f2b738f6005cc48f (patch)
treecfcd82b581fa2cfc0ce25608f0d0adaf38811159 /src/interfaces/ecpg/preproc/util.c
parentf18231e817599246fc99a798c9bf57ab785db91f (diff)
downloadpostgresql-1acd0f55274fab8c936779a0f2b738f6005cc48f.tar.gz
postgresql-1acd0f55274fab8c936779a0f2b738f6005cc48f.zip
ecpg: improve preprocessor's memory management.
Invent a notion of "local" storage that will automatically be reclaimed at the end of each statement. Use this for location strings as well as other visibly short-lived data within the parser. Also, make cat_str and make_str return local storage and not free their inputs, which allows dispensing with a whole lot of retail mm_strdup calls. We do have to add some new ones in places where a local-lifetime string needs to be added to a longer-lived data structure, but on balance there are a lot less mm_strdup calls than before. In hopes of flushing out places where changes were necessary, I changed YYLTYPE from "char *" to "const char *", which forced const-ification of various function arguments that probably should've been like that all along. This still leaks somewhat more memory than v17, but that will be cleaned up in future commits. Discussion: https://postgr.es/m/2011420.1713493114@sss.pgh.pa.us
Diffstat (limited to 'src/interfaces/ecpg/preproc/util.c')
-rw-r--r--src/interfaces/ecpg/preproc/util.c119
1 files changed, 97 insertions, 22 deletions
diff --git a/src/interfaces/ecpg/preproc/util.c b/src/interfaces/ecpg/preproc/util.c
index cb1eca7f3cb..f177df32488 100644
--- a/src/interfaces/ecpg/preproc/util.c
+++ b/src/interfaces/ecpg/preproc/util.c
@@ -104,33 +104,117 @@ mm_strdup(const char *string)
return new;
}
+
/*
- * String concatenation
+ * "Local" memory management support
+ *
+ * These functions manage memory that is only needed for a short time
+ * (processing of one input statement) within the ecpg grammar.
+ * Data allocated with these is not meant to be freed separately;
+ * rather it's freed by calling reclaim_local_storage() at the end
+ * of each statement cycle.
*/
+typedef struct loc_chunk
+{
+ struct loc_chunk *next; /* list link */
+ unsigned int chunk_used; /* index of first unused byte in data[] */
+ unsigned int chunk_avail; /* # bytes still available in data[] */
+ char data[FLEXIBLE_ARRAY_MEMBER]; /* actual storage */
+} loc_chunk;
+
+#define LOC_CHUNK_OVERHEAD MAXALIGN(offsetof(loc_chunk, data))
+#define LOC_CHUNK_MIN_SIZE 8192
+
+/* Head of list of loc_chunks */
+static loc_chunk *loc_chunks = NULL;
+
/*
- * Concatenate 2 strings, inserting a space between them unless either is empty
+ * Allocate local space of the requested size.
*
- * The input strings are freed.
+ * Exits on OOM.
+ */
+void *
+loc_alloc(size_t size)
+{
+ void *result;
+ loc_chunk *cur_chunk = loc_chunks;
+
+ /* Ensure all allocations are adequately aligned */
+ size = MAXALIGN(size);
+
+ /* Need a new chunk? */
+ if (cur_chunk == NULL || size > cur_chunk->chunk_avail)
+ {
+ size_t chunk_size = Max(size, LOC_CHUNK_MIN_SIZE);
+
+ cur_chunk = mm_alloc(chunk_size + LOC_CHUNK_OVERHEAD);
+ /* Depending on alignment rules, we could waste a bit here */
+ cur_chunk->chunk_used = LOC_CHUNK_OVERHEAD - offsetof(loc_chunk, data);
+ cur_chunk->chunk_avail = chunk_size;
+ /* New chunk becomes the head of the list */
+ cur_chunk->next = loc_chunks;
+ loc_chunks = cur_chunk;
+ }
+
+ result = cur_chunk->data + cur_chunk->chunk_used;
+ cur_chunk->chunk_used += size;
+ cur_chunk->chunk_avail -= size;
+ return result;
+}
+
+/*
+ * Copy given string into local storage
+ */
+char *
+loc_strdup(const char *string)
+{
+ char *result = loc_alloc(strlen(string) + 1);
+
+ strcpy(result, string);
+ return result;
+}
+
+/*
+ * Reclaim local storage when appropriate
+ */
+void
+reclaim_local_storage(void)
+{
+ loc_chunk *cur_chunk,
+ *next_chunk;
+
+ for (cur_chunk = loc_chunks; cur_chunk; cur_chunk = next_chunk)
+ {
+ next_chunk = cur_chunk->next;
+ free(cur_chunk);
+ }
+ loc_chunks = NULL;
+}
+
+
+/*
+ * String concatenation support routines. These return "local" (transient)
+ * storage.
+ */
+
+/*
+ * Concatenate 2 strings, inserting a space between them unless either is empty
*/
char *
-cat2_str(char *str1, char *str2)
+cat2_str(const char *str1, const char *str2)
{
- char *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + 2);
+ char *res_str = (char *) loc_alloc(strlen(str1) + strlen(str2) + 2);
strcpy(res_str, str1);
if (strlen(str1) != 0 && strlen(str2) != 0)
strcat(res_str, " ");
strcat(res_str, str2);
- free(str1);
- free(str2);
return res_str;
}
/*
* Concatenate N strings, inserting spaces between them unless they are empty
- *
- * The input strings are freed.
*/
char *
cat_str(int count,...)
@@ -154,36 +238,27 @@ cat_str(int count,...)
/*
* Concatenate 2 strings, with no space between
- *
- * The input strings are freed.
*/
char *
-make2_str(char *str1, char *str2)
+make2_str(const char *str1, const char *str2)
{
- char *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + 1);
+ char *res_str = (char *) loc_alloc(strlen(str1) + strlen(str2) + 1);
strcpy(res_str, str1);
strcat(res_str, str2);
- free(str1);
- free(str2);
return res_str;
}
/*
* Concatenate 3 strings, with no space between
- *
- * The input strings are freed.
*/
char *
-make3_str(char *str1, char *str2, char *str3)
+make3_str(const char *str1, const char *str2, const char *str3)
{
- char *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + 1);
+ char *res_str = (char *) loc_alloc(strlen(str1) + strlen(str2) + strlen(str3) + 1);
strcpy(res_str, str1);
strcat(res_str, str2);
strcat(res_str, str3);
- free(str1);
- free(str2);
- free(str3);
return res_str;
}