]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
libctf: create: vlen growth and prefix addition (NEEDS REVIEW)
authorNick Alcock <nick.alcock@oracle.com>
Thu, 24 Apr 2025 14:48:16 +0000 (15:48 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Fri, 25 Apr 2025 17:07:42 +0000 (18:07 +0100)
This commit modifies ctf_grow_vlen to account for the recent changes to
ctf_dtdef_t, and adds a new ctf_add_prefix function to add a prefix to an
existing type, moving the dtd_data and dtd_vlen up accordinly.

It deserves close review, since this is probably the single greatest bug
cluster in libctf: the number of times I added to a variable of type
ctf_type_t and assumed it would move it in bytes rather than ctf_type_t
units is hard to believe.

libctf/ctf-create.c

index f58df00fb01fa1af032dfed450835ff851747578..5cd5898b6691409a68f600c400f1dab27a1c6c1f 100644 (file)
@@ -74,30 +74,96 @@ ctf_grow_ptrtab (ctf_dict_t *fp)
   return 0;
 }
 
-/* Make sure a vlen has enough space: expand it otherwise.  Unlike the ptrtab,
-   which grows quite slowly, the vlen grows in big jumps because it is quite
-   expensive to expand: the caller has to scan the old vlen for string refs
-   first and remove them, then re-add them afterwards.  The initial size is
-   more or less arbitrary.  */
+/* Make sure a vlen has enough space: expand it otherwise.  Either grow it to
+   roughly enough space for VBYTES, or add precisely VBYTES on to the space
+   reserved for the vlen.  (In one mode, VBYTES is a minimum: in the other,
+   it's an addend.  */
+
 static int
-ctf_grow_vlen (ctf_dict_t *fp, ctf_dtdef_t *dtd, size_t vlen)
+ctf_add_vlen (ctf_dict_t *fp, ctf_dtdef_t *dtd, size_t vbytes, int additive)
 {
-  unsigned char *old = dtd->dtd_vlen;
+  unsigned char *old = (unsigned char *) dtd->dtd_buf;
 
-  if (dtd->dtd_vlen_alloc > vlen)
-    return 0;
+  size_t size = dtd->dtd_buf_size;
+  size_t old_size = size;
+  size_t vlen_size = dtd->dtd_vlen_size;
+  size_t prefix_size = size - vlen_size;
+  size_t old_data_index = dtd->dtd_data - dtd->dtd_buf;
+  size_t old_vlen_offset = dtd->dtd_vlen - old;
+
+  if (!additive)
+    {
+      if ((size - vlen_size) > vbytes)
+       return 0;
 
-  if ((dtd->dtd_vlen = realloc (dtd->dtd_vlen,
-                               dtd->dtd_vlen_alloc * 2)) == NULL)
+      while (vlen_size < vbytes)
+       {
+         size *= 2;
+         vlen_size = size - prefix_size;
+       }
+    }
+  else
     {
-      dtd->dtd_vlen = old;
+      vlen_size += vbytes;
+      size += vbytes;
+    }
+
+  if ((dtd->dtd_buf = realloc (dtd->dtd_buf, size)) == NULL)
+    {
+      dtd->dtd_buf = (ctf_type_t *) old;
       return (ctf_set_errno (fp, ENOMEM));
     }
-  memset (dtd->dtd_vlen + dtd->dtd_vlen_alloc, 0, dtd->dtd_vlen_alloc);
-  dtd->dtd_vlen_alloc *= 2;
+
+  memset (((unsigned char *) dtd->dtd_buf) + old_size, 0, size - old_size);
+  dtd->dtd_data = dtd->dtd_buf + old_data_index;
+  dtd->dtd_vlen = ((unsigned char *) dtd->dtd_buf) + old_vlen_offset;
+  dtd->dtd_buf_size = size;
+
   return 0;
 }
 
+/* Make sure a vlen has enough space: expand it otherwise.  Grow it in fairly
+   big jumps, for amortized-constant-time growth.  */
+
+static int
+ctf_grow_vlen (ctf_dict_t *fp, ctf_dtdef_t *dtd, size_t vbytes)
+{
+  return ctf_add_vlen (fp, dtd, vbytes, 0);
+}
+
+/* Add a prefix to a given DTD, at the end of the prefix chain, and return it.
+   Make sure the vlen has enough room for at least VBYTES bytes, too. */
+static ctf_type_t *
+ctf_add_prefix (ctf_dict_t *fp, ctf_dtdef_t *dtd, size_t vbytes)
+{
+  ctf_type_t *new_prefix;
+  size_t old_buf_size = dtd->dtd_buf_size;
+
+  /* Grow the type, then tweak the vlen forwards and move things around to leave
+     a gap.  If we run off the end of the headers without finding a non-prefix,
+     something is wrong.  */
+
+  if (vbytes == 0)
+    {
+      if (ctf_add_vlen (fp, dtd, sizeof (ctf_type_t), 1) < 0)
+       return NULL;                            /* errno is set for us.  */
+    }
+  else
+    {
+      if (ctf_grow_vlen (fp, dtd, vbytes + sizeof (ctf_type_t)) < 0)
+       return NULL;                            /* errno is set for us.  */
+    }
+
+  new_prefix = dtd->dtd_data;
+  memmove (dtd->dtd_data + 1, dtd->dtd_data, old_buf_size
+          - ((unsigned char *) dtd->dtd_data - (unsigned char *) dtd->dtd_buf));
+  dtd->dtd_vlen += sizeof (ctf_type_t);
+  dtd->dtd_data++;
+  memset (new_prefix, 0, sizeof (ctf_type_t));
+
+  return new_prefix;
+}
+
 /* To create an empty CTF dict, we just declare a zeroed header and call
    ctf_bufopen() on it.  If ctf_bufopen succeeds, we mark the new dict r/w and
    initialize the dynamic members.  We start assigning type IDs at 1 because