static size_t count_strings (const struct caseproto *,
size_t idx, size_t count);
-/* Returns the number of bytes to allocate for a struct caseproto
- with room for N_WIDTHS elements in its widths[] array. */
-static inline size_t
-caseproto_size (size_t n_widths)
-{
- return (offsetof (struct caseproto, widths)
- + n_widths * sizeof (((struct caseproto *) NULL)->widths[0]));
-}
-
/* Creates and returns a case prototype that initially has no
widths. */
struct caseproto *
caseproto_create (void)
{
- enum { N_ALLOCATE = 4 };
- struct caseproto *proto = xmalloc (caseproto_size (N_ALLOCATE));
- proto->ref_cnt = 1;
- proto->strings = NULL;
- proto->n_strings = 0;
- proto->n_widths = 0;
- proto->allocated_widths = N_ALLOCATE;
+ struct caseproto *proto = xmalloc (sizeof *proto);
+ *proto = (struct caseproto) {
+ .ref_cnt = 1,
+ };
+ return proto;
+}
+
+struct caseproto * MALLOC_LIKE
+caseproto_from_widths (short int *widths, size_t n)
+{
+ struct caseproto *proto = xmalloc (sizeof *proto);
+ *proto = (struct caseproto) {
+ .ref_cnt = 1,
+ .n_widths = n,
+ .allocated_widths = n,
+ .widths = widths,
+ };
+ proto->n_strings = count_strings (proto, 0, n);
return proto;
}
return proto;
}
-/* Returns a replacement for PROTO that is unshared and has
- enough room for at least N_WIDTHS widths before additional
- memory is needed. */
-struct caseproto *
-caseproto_reserve (struct caseproto *proto, size_t n_widths)
-{
- proto = caseproto_unshare (proto);
- if (n_widths > proto->allocated_widths)
- {
- proto->allocated_widths = MAX (proto->allocated_widths * 2, n_widths);
- proto = xrealloc (proto, caseproto_size (proto->allocated_widths));
- }
- return proto;
-}
-
/* Returns a replacement for PROTO with WIDTH appended. */
struct caseproto *
caseproto_add_width (struct caseproto *proto, int width)
{
- assert (width >= -1 && width <= MAX_STRING);
+ assert (width >= 0 && width <= MAX_STRING);
- proto = caseproto_reserve (proto, proto->n_widths + 1);
+ proto = caseproto_unshare (proto);
+ if (proto->n_widths >= proto->allocated_widths)
+ proto->widths = x2nrealloc (proto->widths, &proto->allocated_widths,
+ sizeof *proto->widths);
proto->widths[proto->n_widths++] = width;
- proto->n_strings += count_strings (proto, proto->n_widths - 1, 1);
+ if (width > 0)
+ proto->n_strings++;
return proto;
}
-/* Returns a replacement for PROTO with the width at index IDX
- replaced by WIDTH. IDX may be greater than the current number
- of widths in PROTO, in which case any gap is filled in by
- widths of -1. */
+/* Returns a replacement for PROTO with the width at index IDX replaced by
+ WIDTH. */
struct caseproto *
caseproto_set_width (struct caseproto *proto, size_t idx, int width)
{
- assert (width >= -1 && width <= MAX_STRING);
+ assert (idx < proto->n_widths);
+ assert (width >= 0 && width <= MAX_STRING);
- proto = caseproto_reserve (proto, idx + 1);
- while (idx >= proto->n_widths)
- proto->widths[proto->n_widths++] = -1;
+ proto = caseproto_unshare (proto);
proto->n_strings -= count_strings (proto, idx, 1);
proto->widths[idx] = width;
proto->n_strings += count_strings (proto, idx, 1);
struct caseproto *
caseproto_insert_width (struct caseproto *proto, size_t before, int width)
{
+ assert (width >= 0 && width <= MAX_STRING);
assert (before <= proto->n_widths);
- proto = caseproto_reserve (proto, proto->n_widths + 1);
+ proto = caseproto_unshare (proto);
+ if (proto->n_widths >= proto->allocated_widths)
+ proto->widths = x2nrealloc (proto->widths, &proto->allocated_widths,
+ sizeof *proto->widths);
proto->n_strings += value_needs_init (width);
insert_element (proto->widths, proto->n_widths, sizeof *proto->widths,
before);
same as the N widths starting at B_START in B, false if any of
the corresponding widths differ. */
bool
-caseproto_equal (const struct caseproto *a, size_t a_start,
- const struct caseproto *b, size_t b_start,
- size_t n)
+caseproto_range_equal (const struct caseproto *a, size_t a_start,
+ const struct caseproto *b, size_t b_start,
+ size_t n)
{
size_t i;
return true;
}
+/* Returns true if A and B have the same widths, false otherwise. */
+bool
+caseproto_equal (const struct caseproto *a, const struct caseproto *b)
+{
+ return (a == b ? true
+ : a->n_widths != b->n_widths ? false
+ : caseproto_range_equal (a, 0, b, 0, a->n_widths));
+}
+
/* Returns true if an array of values that is to be used for
data of the format specified in PROTO needs to be initialized
by calling caseproto_init_values, false if that step may be
caseproto_free__ (struct caseproto *proto)
{
free (proto->strings);
+ free (proto->widths);
free (proto);
}
assert (n == proto->n_strings);
}
+/* Returns a caseproto that can be modified without affecting the contents of
+ any caseproto shared with OLD.
+
+ The returned caseproto has no strings cache. This is helpful because the
+ caller might be about to invalidate it. */
static struct caseproto *
caseproto_unshare (struct caseproto *old)
{
- struct caseproto *new;
- if (old->ref_cnt > 1)
+ assert (old->ref_cnt > 0);
+ if (old->ref_cnt <= 1)
{
- new = xmemdup (old, caseproto_size (old->allocated_widths));
- new->ref_cnt = 1;
- --old->ref_cnt;
+ free (old->strings);
+ old->strings = NULL;
+ return old;
}
- else
- {
- new = old;
- free (new->strings);
- }
- new->strings = NULL;
+
+ struct caseproto *new = xmalloc (sizeof *new);
+ *new = (struct caseproto) {
+ .ref_cnt = 1,
+ .n_strings = old->n_strings,
+ .n_widths = old->n_widths,
+ .allocated_widths = old->allocated_widths,
+ .widths = xmemdup (old->widths, old->allocated_widths * sizeof *old->widths),
+ };
+ --old->ref_cnt;
return new;
}