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 /* Creates and returns a case prototype that initially has no
42 caseproto_create (void)
44 struct caseproto *proto = xmalloc (sizeof *proto);
45 *proto = (struct caseproto) {
51 struct caseproto * MALLOC_LIKE
52 caseproto_from_widths (short int *widths, size_t n)
54 struct caseproto *proto = xmalloc (sizeof *proto);
55 *proto = (struct caseproto) {
58 .allocated_widths = n,
61 proto->n_strings = count_strings (proto, 0, n);
66 do_unref (void *proto_)
68 struct caseproto *proto = proto_;
69 caseproto_unref (proto);
72 /* Creates and returns a new reference to PROTO. When POOL is
73 destroyed, the new reference will be destroyed (unrefed). */
75 caseproto_ref_pool (const struct caseproto *proto_, struct pool *pool)
77 struct caseproto *proto = caseproto_ref (proto_);
78 pool_register (pool, do_unref, proto);
82 /* Returns a replacement for PROTO with WIDTH appended. */
84 caseproto_add_width (struct caseproto *proto, int width)
86 assert (width >= 0 && width <= MAX_STRING);
88 proto = caseproto_unshare (proto);
89 if (proto->n_widths >= proto->allocated_widths)
90 proto->widths = x2nrealloc (proto->widths, &proto->allocated_widths,
91 sizeof *proto->widths);
92 proto->widths[proto->n_widths++] = width;
99 /* Returns a replacement for PROTO with the width at index IDX replaced by
102 caseproto_set_width (struct caseproto *proto, size_t idx, int width)
104 assert (idx < proto->n_widths);
105 assert (width >= 0 && width <= MAX_STRING);
107 proto = caseproto_unshare (proto);
108 proto->n_strings -= count_strings (proto, idx, 1);
109 proto->widths[idx] = width;
110 proto->n_strings += count_strings (proto, idx, 1);
115 /* Returns a replacement for PROTO with WIDTH inserted just
116 before index BEFORE, or just after the last element if BEFORE
117 is the number of widths in PROTO. */
119 caseproto_insert_width (struct caseproto *proto, size_t before, int width)
121 assert (width >= 0 && width <= MAX_STRING);
122 assert (before <= proto->n_widths);
124 proto = caseproto_unshare (proto);
125 if (proto->n_widths >= proto->allocated_widths)
126 proto->widths = x2nrealloc (proto->widths, &proto->allocated_widths,
127 sizeof *proto->widths);
128 proto->n_strings += value_needs_init (width);
129 insert_element (proto->widths, proto->n_widths, sizeof *proto->widths,
131 proto->widths[before] = width;
137 /* Returns a replacement for PROTO with N widths removed
138 starting at index IDX. */
140 caseproto_remove_widths (struct caseproto *proto, size_t idx, size_t n)
142 assert (caseproto_range_is_valid (proto, idx, n));
144 proto = caseproto_unshare (proto);
145 proto->n_strings -= count_strings (proto, idx, n);
146 remove_range (proto->widths, proto->n_widths, sizeof *proto->widths,
148 proto->n_widths -= n;
152 /* Returns a replacement for PROTO in which the N widths
153 starting at index OLD_WIDTH now start at index NEW_WIDTH, with
154 other widths shifting out of the way to make room. */
156 caseproto_move_widths (struct caseproto *proto,
157 size_t old_start, size_t new_start,
160 assert (caseproto_range_is_valid (proto, old_start, n));
161 assert (caseproto_range_is_valid (proto, new_start, n));
163 proto = caseproto_unshare (proto);
164 move_range (proto->widths, proto->n_widths, sizeof *proto->widths,
165 old_start, new_start, n);
169 /* Returns true if PROTO contains COUNT widths starting at index
170 OFS, false if any of those widths are out of range for
173 caseproto_range_is_valid (const struct caseproto *proto,
174 size_t ofs, size_t count)
176 return (count <= proto->n_widths
177 && ofs <= proto->n_widths
178 && ofs + count <= proto->n_widths);
181 /* Returns true if A and B have the same widths along their
182 common length. (When this is so, a case with prototype A may
183 be extended or truncated to have prototype B without having to
184 change any existing values, and vice versa.) */
186 caseproto_is_conformable (const struct caseproto *a, const struct caseproto *b)
191 min = MIN (a->n_widths, b->n_widths);
192 for (i = 0; i < min; i++)
193 if (a->widths[i] != b->widths[i])
198 /* Returns true if the N widths starting at A_START in A are the
199 same as the N widths starting at B_START in B, false if any of
200 the corresponding widths differ. */
202 caseproto_range_equal (const struct caseproto *a, size_t a_start,
203 const struct caseproto *b, size_t b_start,
208 assert (caseproto_range_is_valid (a, a_start, n));
209 assert (caseproto_range_is_valid (b, b_start, n));
210 for (i = 0; i < n; i++)
211 if (a->widths[a_start + i] != b->widths[b_start + i])
216 /* Returns true if A and B have the same widths, false otherwise. */
218 caseproto_equal (const struct caseproto *a, const struct caseproto *b)
220 return (a == b ? true
221 : a->n_widths != b->n_widths ? false
222 : caseproto_range_equal (a, 0, b, 0, a->n_widths));
225 /* Returns true if an array of values that is to be used for
226 data of the format specified in PROTO needs to be initialized
227 by calling caseproto_init_values, false if that step may be
228 skipped because such an initialization would be a no-op anyhow.
230 This optimization is useful only when a large number of
231 initializations of such arrays may be skipped as a group. */
233 caseproto_needs_init_values (const struct caseproto *proto)
235 return proto->n_strings > 0;
238 /* Initializes the values in VALUES as required by PROTO, by
239 calling value_init() on each value for which this is required.
240 The data in VALUES have indeterminate contents until
243 VALUES must have at least caseproto_get_n_widths(PROTO)
244 elements; only that many elements of VALUES are initialized.
246 The caller retains ownership of PROTO. */
248 caseproto_init_values (const struct caseproto *proto, union value values[])
250 init_strings (proto, 0, proto->n_strings, values);
253 /* Like caseproto_init_values, but returns false instead of
254 terminating if memory cannot be obtained. */
256 caseproto_try_init_values (const struct caseproto *proto, union value values[])
258 return try_init_strings (proto, 0, proto->n_strings, values);
261 /* Initializes the data in VALUES that are in NEW but not in OLD,
262 destroys the data in VALUES that are in OLD but not NEW, and
263 does not modify the data in VALUES that are in both OLD and
264 NEW. VALUES must previously have been initialized as required
265 by OLD using e.g. caseproto_init_values. The data in VALUES
266 that are in NEW but not in OLD will have indeterminate
267 contents until explicitly written.
269 OLD and NEW must be conformable for this operation, as
270 reported by caseproto_is_conformable.
272 The caller retains ownership of OLD and NEW. */
274 caseproto_reinit_values (const struct caseproto *old,
275 const struct caseproto *new, union value values[])
277 size_t old_n_long = old->n_strings;
278 size_t new_n_long = new->n_strings;
280 expensive_assert (caseproto_is_conformable (old, new));
282 if (new_n_long > old_n_long)
283 init_strings (new, old_n_long, new_n_long, values);
284 else if (new_n_long < old_n_long)
285 destroy_strings (old, new_n_long, old_n_long, values);
288 /* Frees the values in VALUES as required by PROTO, by calling
289 value_destroy() on each value for which this is required. The
290 values must previously have been initialized using
291 e.g. caseproto_init_values.
293 The caller retains ownership of PROTO. */
295 caseproto_destroy_values (const struct caseproto *proto, union value values[])
297 destroy_strings (proto, 0, proto->n_strings, values);
300 /* Copies COUNT values, whose widths are given by widths in PROTO
301 starting with index IDX, from SRC to DST. The caller must
302 ensure that the values in SRC and DST were appropriately
303 initialized using e.g. caseproto_init_values. */
305 caseproto_copy (const struct caseproto *proto, size_t idx, size_t count,
306 union value *dst, const union value *src)
310 assert (caseproto_range_is_valid (proto, idx, count));
311 for (i = 0; i < count; i++)
312 value_copy (&dst[idx + i], &src[idx + i], proto->widths[idx + i]);
316 caseproto_free__ (struct caseproto *proto)
318 free (proto->strings);
319 free (proto->widths);
324 caseproto_refresh_string_cache__ (const struct caseproto *proto_)
326 struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
329 assert (proto->strings == NULL);
330 assert (proto->n_strings > 0);
332 proto->strings = xmalloc (proto->n_strings * sizeof *proto->strings);
334 for (i = 0; i < proto->n_widths; i++)
335 if (proto->widths[i] > 0)
336 proto->strings[n++] = i;
337 assert (n == proto->n_strings);
340 /* Returns a caseproto that can be modified without affecting the contents of
341 any caseproto shared with OLD.
343 The returned caseproto has no strings cache. This is helpful because the
344 caller might be about to invalidate it. */
345 static struct caseproto *
346 caseproto_unshare (struct caseproto *old)
348 assert (old->ref_cnt > 0);
349 if (old->ref_cnt <= 1)
356 struct caseproto *new = xmalloc (sizeof *new);
357 *new = (struct caseproto) {
359 .n_strings = old->n_strings,
360 .n_widths = old->n_widths,
361 .allocated_widths = old->allocated_widths,
362 .widths = xmemdup (old->widths, old->allocated_widths * sizeof *old->widths),
369 try_init_strings (const struct caseproto *proto,
370 size_t first, size_t last, union value values[])
374 if (last > 0 && proto->strings == NULL)
375 caseproto_refresh_string_cache__ (proto);
377 for (i = first; i < last; i++)
379 size_t idx = proto->strings[i];
380 if (!value_try_init (&values[idx], proto->widths[idx]))
382 destroy_strings (proto, first, i, values);
390 init_strings (const struct caseproto *proto,
391 size_t first, size_t last, union value values[])
393 if (!try_init_strings (proto, first, last, values))
398 destroy_strings (const struct caseproto *proto, size_t first, size_t last,
399 union value values[])
403 if (last > 0 && proto->strings == NULL)
404 caseproto_refresh_string_cache__ (proto);
406 for (i = first; i < last; i++)
408 size_t idx = proto->strings[i];
409 value_destroy (&values[idx], proto->widths[idx]);
414 count_strings (const struct caseproto *proto, size_t idx, size_t count)
419 for (i = 0; i < count; i++)
420 n += proto->widths[idx + i] > 0;