1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "data/caseproto.h"
21 #include "data/val-type.h"
22 #include "data/value.h"
23 #include "libpspp/array.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/pool.h"
27 #include "gl/minmax.h"
29 static struct caseproto *caseproto_unshare (struct caseproto *);
30 static bool try_init_strings (const struct caseproto *,
31 size_t first, size_t last, union value[]);
32 static void init_strings (const struct caseproto *,
33 size_t first, size_t last, union value[]);
34 static void destroy_strings (const struct caseproto *,
35 size_t first, size_t last, union value[]);
36 static size_t count_strings (const struct caseproto *,
37 size_t idx, size_t count);
39 /* Returns the number of bytes to allocate for a struct caseproto
40 with room for N_WIDTHS elements in its widths[] array. */
42 caseproto_size (size_t n_widths)
44 return (offsetof (struct caseproto, widths)
45 + n_widths * sizeof (((struct caseproto *) NULL)->widths[0]));
48 /* Creates and returns a case prototype that initially has no
51 caseproto_create (void)
53 enum { N_ALLOCATE = 4 };
54 struct caseproto *proto = xmalloc (caseproto_size (N_ALLOCATE));
56 proto->strings = NULL;
59 proto->allocated_widths = N_ALLOCATE;
64 do_unref (void *proto_)
66 struct caseproto *proto = proto_;
67 caseproto_unref (proto);
70 /* Creates and returns a new reference to PROTO. When POOL is
71 destroyed, the new reference will be destroyed (unrefed). */
73 caseproto_ref_pool (const struct caseproto *proto_, struct pool *pool)
75 struct caseproto *proto = caseproto_ref (proto_);
76 pool_register (pool, do_unref, proto);
80 /* Returns a replacement for PROTO that is unshared and has
81 enough room for at least N_WIDTHS widths before additional
84 caseproto_reserve (struct caseproto *proto, size_t n_widths)
86 proto = caseproto_unshare (proto);
87 if (n_widths > proto->allocated_widths)
89 proto->allocated_widths *= MAX (proto->allocated_widths * 2, n_widths);
90 proto = xrealloc (proto, caseproto_size (proto->allocated_widths));
95 /* Returns a replacement for PROTO with WIDTH appended. */
97 caseproto_add_width (struct caseproto *proto, int width)
99 assert (width >= -1 && width <= MAX_STRING);
101 proto = caseproto_reserve (proto, proto->n_widths + 1);
102 proto->widths[proto->n_widths++] = width;
103 proto->n_strings += count_strings (proto, proto->n_widths - 1, 1);
108 /* Returns a replacement for PROTO with the width at index IDX
109 replaced by WIDTH. IDX may be greater than the current number
110 of widths in PROTO, in which case any gap is filled in by
113 caseproto_set_width (struct caseproto *proto, size_t idx, int width)
115 assert (width >= -1 && width <= MAX_STRING);
117 proto = caseproto_reserve (proto, idx + 1);
118 while (idx >= proto->n_widths)
119 proto->widths[proto->n_widths++] = -1;
120 proto->n_strings -= count_strings (proto, idx, 1);
121 proto->widths[idx] = width;
122 proto->n_strings += count_strings (proto, idx, 1);
127 /* Returns a replacement for PROTO with WIDTH inserted just
128 before index BEFORE, or just after the last element if BEFORE
129 is the number of widths in PROTO. */
131 caseproto_insert_width (struct caseproto *proto, size_t before, int width)
133 assert (before <= proto->n_widths);
135 proto = caseproto_reserve (proto, proto->n_widths + 1);
136 proto->n_strings += value_needs_init (width);
137 insert_element (proto->widths, proto->n_widths, sizeof *proto->widths,
139 proto->widths[before] = width;
145 /* Returns a replacement for PROTO with CNT widths removed
146 starting at index IDX. */
148 caseproto_remove_widths (struct caseproto *proto, size_t idx, size_t cnt)
150 assert (caseproto_range_is_valid (proto, idx, cnt));
152 proto = caseproto_unshare (proto);
153 proto->n_strings -= count_strings (proto, idx, cnt);
154 remove_range (proto->widths, proto->n_widths, sizeof *proto->widths,
156 proto->n_widths -= cnt;
160 /* Returns a replacement for PROTO in which the CNT widths
161 starting at index OLD_WIDTH now start at index NEW_WIDTH, with
162 other widths shifting out of the way to make room. */
164 caseproto_move_widths (struct caseproto *proto,
165 size_t old_start, size_t new_start,
168 assert (caseproto_range_is_valid (proto, old_start, cnt));
169 assert (caseproto_range_is_valid (proto, new_start, cnt));
171 proto = caseproto_unshare (proto);
172 move_range (proto->widths, proto->n_widths, sizeof *proto->widths,
173 old_start, new_start, cnt);
177 /* Returns true if PROTO contains COUNT widths starting at index
178 OFS, false if any of those widths are out of range for
181 caseproto_range_is_valid (const struct caseproto *proto,
182 size_t ofs, size_t count)
184 return (count <= proto->n_widths
185 && ofs <= proto->n_widths
186 && ofs + count <= proto->n_widths);
189 /* Returns true if A and B have the same widths along their
190 common length. (When this is so, a case with prototype A may
191 be extended or truncated to have prototype B without having to
192 change any existing values, and vice versa.) */
194 caseproto_is_conformable (const struct caseproto *a, const struct caseproto *b)
199 min = MIN (a->n_widths, b->n_widths);
200 for (i = 0; i < min; i++)
201 if (a->widths[i] != b->widths[i])
206 /* Returns true if the N widths starting at A_START in A are the
207 same as the N widths starting at B_START in B, false if any of
208 the corresponding widths differ. */
210 caseproto_equal (const struct caseproto *a, size_t a_start,
211 const struct caseproto *b, size_t b_start,
216 assert (caseproto_range_is_valid (a, a_start, n));
217 assert (caseproto_range_is_valid (b, b_start, n));
218 for (i = 0; i < n; i++)
219 if (a->widths[a_start + i] != b->widths[b_start + i])
224 /* Returns true if an array of values that is to be used for
225 data of the format specified in PROTO needs to be initialized
226 by calling caseproto_init_values, false if that step may be
227 skipped because such an initialization would be a no-op anyhow.
229 This optimization is useful only when a large number of
230 initializations of such arrays may be skipped as a group. */
232 caseproto_needs_init_values (const struct caseproto *proto)
234 return proto->n_strings > 0;
237 /* Initializes the values in VALUES as required by PROTO, by
238 calling value_init() on each value for which this is required.
239 The data in VALUES have indeterminate contents until
242 VALUES must have at least caseproto_get_n_widths(PROTO)
243 elements; only that many elements of VALUES are initialized.
245 The caller retains ownership of PROTO. */
247 caseproto_init_values (const struct caseproto *proto, union value values[])
249 init_strings (proto, 0, proto->n_strings, values);
252 /* Like caseproto_init_values, but returns false instead of
253 terminating if memory cannot be obtained. */
255 caseproto_try_init_values (const struct caseproto *proto, union value values[])
257 return try_init_strings (proto, 0, proto->n_strings, values);
260 /* Initializes the data in VALUES that are in NEW but not in OLD,
261 destroys the data in VALUES that are in OLD but not NEW, and
262 does not modify the data in VALUES that are in both OLD and
263 NEW. VALUES must previously have been initialized as required
264 by OLD using e.g. caseproto_init_values. The data in VALUES
265 that are in NEW but not in OLD will have indeterminate
266 contents until explicitly written.
268 OLD and NEW must be conformable for this operation, as
269 reported by caseproto_is_conformable.
271 The caller retains ownership of OLD and NEW. */
273 caseproto_reinit_values (const struct caseproto *old,
274 const struct caseproto *new, union value values[])
276 size_t old_n_long = old->n_strings;
277 size_t new_n_long = new->n_strings;
279 expensive_assert (caseproto_is_conformable (old, new));
281 if (new_n_long > old_n_long)
282 init_strings (new, old_n_long, new_n_long, values);
283 else if (new_n_long < old_n_long)
284 destroy_strings (old, new_n_long, old_n_long, values);
287 /* Frees the values in VALUES as required by PROTO, by calling
288 value_destroy() on each value for which this is required. The
289 values must previously have been initialized using
290 e.g. caseproto_init_values.
292 The caller retains ownership of PROTO. */
294 caseproto_destroy_values (const struct caseproto *proto, union value values[])
296 destroy_strings (proto, 0, proto->n_strings, values);
299 /* Copies COUNT values, whose widths are given by widths in PROTO
300 starting with index IDX, from SRC to DST. The caller must
301 ensure that the values in SRC and DST were appropriately
302 initialized using e.g. caseproto_init_values. */
304 caseproto_copy (const struct caseproto *proto, size_t idx, size_t count,
305 union value *dst, const union value *src)
309 assert (caseproto_range_is_valid (proto, idx, count));
310 for (i = 0; i < count; i++)
311 value_copy (&dst[idx + i], &src[idx + i], proto->widths[idx + i]);
315 caseproto_free__ (struct caseproto *proto)
317 free (proto->strings);
322 caseproto_refresh_string_cache__ (const struct caseproto *proto_)
324 struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
327 assert (proto->strings == NULL);
328 assert (proto->n_strings > 0);
330 proto->strings = xmalloc (proto->n_strings * sizeof *proto->strings);
332 for (i = 0; i < proto->n_widths; i++)
333 if (proto->widths[i] > 0)
334 proto->strings[n++] = i;
335 assert (n == proto->n_strings);
338 static struct caseproto *
339 caseproto_unshare (struct caseproto *old)
341 struct caseproto *new;
342 if (old->ref_cnt > 1)
344 new = xmemdup (old, caseproto_size (old->allocated_widths));
358 try_init_strings (const struct caseproto *proto,
359 size_t first, size_t last, union value values[])
363 if (last > 0 && proto->strings == NULL)
364 caseproto_refresh_string_cache__ (proto);
366 for (i = first; i < last; i++)
368 size_t idx = proto->strings[i];
369 if (!value_try_init (&values[idx], proto->widths[idx]))
371 destroy_strings (proto, first, i, values);
379 init_strings (const struct caseproto *proto,
380 size_t first, size_t last, union value values[])
382 if (!try_init_strings (proto, first, last, values))
387 destroy_strings (const struct caseproto *proto, size_t first, size_t last,
388 union value values[])
392 if (last > 0 && proto->strings == NULL)
393 caseproto_refresh_string_cache__ (proto);
395 for (i = first; i < last; i++)
397 size_t idx = proto->strings[i];
398 value_destroy (&values[idx], proto->widths[idx]);
403 count_strings (const struct caseproto *proto, size_t idx, size_t count)
408 for (i = 0; i < count; i++)
409 n += proto->widths[idx + i] > 0;