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) {
52 do_unref (void *proto_)
54 struct caseproto *proto = proto_;
55 caseproto_unref (proto);
58 /* Creates and returns a new reference to PROTO. When POOL is
59 destroyed, the new reference will be destroyed (unrefed). */
61 caseproto_ref_pool (const struct caseproto *proto_, struct pool *pool)
63 struct caseproto *proto = caseproto_ref (proto_);
64 pool_register (pool, do_unref, proto);
68 /* Returns a replacement for PROTO that is unshared and has
69 enough room for at least N_WIDTHS widths before additional
72 caseproto_reserve (struct caseproto *proto, size_t n_widths)
74 proto = caseproto_unshare (proto);
75 if (n_widths > proto->allocated_widths)
77 proto->allocated_widths = MAX (proto->allocated_widths * 2, n_widths);
78 proto->widths = xnrealloc (proto->widths, proto->allocated_widths,
79 sizeof *proto->widths);
84 /* Returns a replacement for PROTO with WIDTH appended. */
86 caseproto_add_width (struct caseproto *proto, int width)
88 assert (width >= -1 && width <= MAX_STRING);
90 proto = caseproto_unshare (proto);
91 if (proto->n_widths >= proto->allocated_widths)
92 proto->widths = x2nrealloc (proto->widths, &proto->allocated_widths,
93 sizeof *proto->widths);
94 proto->widths[proto->n_widths++] = width;
101 /* Returns a replacement for PROTO with the width at index IDX
102 replaced by WIDTH. IDX may be greater than the current number
103 of widths in PROTO, in which case any gap is filled in by
106 caseproto_set_width (struct caseproto *proto, size_t idx, int width)
108 assert (width >= -1 && width <= MAX_STRING);
110 proto = caseproto_reserve (proto, idx + 1);
111 while (idx >= proto->n_widths)
112 proto->widths[proto->n_widths++] = -1;
113 proto->n_strings -= count_strings (proto, idx, 1);
114 proto->widths[idx] = width;
115 proto->n_strings += count_strings (proto, idx, 1);
120 /* Returns a replacement for PROTO with WIDTH inserted just
121 before index BEFORE, or just after the last element if BEFORE
122 is the number of widths in PROTO. */
124 caseproto_insert_width (struct caseproto *proto, size_t before, int width)
126 assert (before <= proto->n_widths);
128 proto = caseproto_reserve (proto, proto->n_widths + 1);
129 proto->n_strings += value_needs_init (width);
130 insert_element (proto->widths, proto->n_widths, sizeof *proto->widths,
132 proto->widths[before] = width;
138 /* Returns a replacement for PROTO with N widths removed
139 starting at index IDX. */
141 caseproto_remove_widths (struct caseproto *proto, size_t idx, size_t n)
143 assert (caseproto_range_is_valid (proto, idx, n));
145 proto = caseproto_unshare (proto);
146 proto->n_strings -= count_strings (proto, idx, n);
147 remove_range (proto->widths, proto->n_widths, sizeof *proto->widths,
149 proto->n_widths -= n;
153 /* Returns a replacement for PROTO in which the N widths
154 starting at index OLD_WIDTH now start at index NEW_WIDTH, with
155 other widths shifting out of the way to make room. */
157 caseproto_move_widths (struct caseproto *proto,
158 size_t old_start, size_t new_start,
161 assert (caseproto_range_is_valid (proto, old_start, n));
162 assert (caseproto_range_is_valid (proto, new_start, n));
164 proto = caseproto_unshare (proto);
165 move_range (proto->widths, proto->n_widths, sizeof *proto->widths,
166 old_start, new_start, n);
170 /* Returns true if PROTO contains COUNT widths starting at index
171 OFS, false if any of those widths are out of range for
174 caseproto_range_is_valid (const struct caseproto *proto,
175 size_t ofs, size_t count)
177 return (count <= proto->n_widths
178 && ofs <= proto->n_widths
179 && ofs + count <= proto->n_widths);
182 /* Returns true if A and B have the same widths along their
183 common length. (When this is so, a case with prototype A may
184 be extended or truncated to have prototype B without having to
185 change any existing values, and vice versa.) */
187 caseproto_is_conformable (const struct caseproto *a, const struct caseproto *b)
192 min = MIN (a->n_widths, b->n_widths);
193 for (i = 0; i < min; i++)
194 if (a->widths[i] != b->widths[i])
199 /* Returns true if the N widths starting at A_START in A are the
200 same as the N widths starting at B_START in B, false if any of
201 the corresponding widths differ. */
203 caseproto_range_equal (const struct caseproto *a, size_t a_start,
204 const struct caseproto *b, size_t b_start,
209 assert (caseproto_range_is_valid (a, a_start, n));
210 assert (caseproto_range_is_valid (b, b_start, n));
211 for (i = 0; i < n; i++)
212 if (a->widths[a_start + i] != b->widths[b_start + i])
217 /* Returns true if A and B have the same widths, false otherwise. */
219 caseproto_equal (const struct caseproto *a, const struct caseproto *b)
221 return (a == b ? true
222 : a->n_widths != b->n_widths ? false
223 : caseproto_range_equal (a, 0, b, 0, a->n_widths));
226 /* Returns true if an array of values that is to be used for
227 data of the format specified in PROTO needs to be initialized
228 by calling caseproto_init_values, false if that step may be
229 skipped because such an initialization would be a no-op anyhow.
231 This optimization is useful only when a large number of
232 initializations of such arrays may be skipped as a group. */
234 caseproto_needs_init_values (const struct caseproto *proto)
236 return proto->n_strings > 0;
239 /* Initializes the values in VALUES as required by PROTO, by
240 calling value_init() on each value for which this is required.
241 The data in VALUES have indeterminate contents until
244 VALUES must have at least caseproto_get_n_widths(PROTO)
245 elements; only that many elements of VALUES are initialized.
247 The caller retains ownership of PROTO. */
249 caseproto_init_values (const struct caseproto *proto, union value values[])
251 init_strings (proto, 0, proto->n_strings, values);
254 /* Like caseproto_init_values, but returns false instead of
255 terminating if memory cannot be obtained. */
257 caseproto_try_init_values (const struct caseproto *proto, union value values[])
259 return try_init_strings (proto, 0, proto->n_strings, values);
262 /* Initializes the data in VALUES that are in NEW but not in OLD,
263 destroys the data in VALUES that are in OLD but not NEW, and
264 does not modify the data in VALUES that are in both OLD and
265 NEW. VALUES must previously have been initialized as required
266 by OLD using e.g. caseproto_init_values. The data in VALUES
267 that are in NEW but not in OLD will have indeterminate
268 contents until explicitly written.
270 OLD and NEW must be conformable for this operation, as
271 reported by caseproto_is_conformable.
273 The caller retains ownership of OLD and NEW. */
275 caseproto_reinit_values (const struct caseproto *old,
276 const struct caseproto *new, union value values[])
278 size_t old_n_long = old->n_strings;
279 size_t new_n_long = new->n_strings;
281 expensive_assert (caseproto_is_conformable (old, new));
283 if (new_n_long > old_n_long)
284 init_strings (new, old_n_long, new_n_long, values);
285 else if (new_n_long < old_n_long)
286 destroy_strings (old, new_n_long, old_n_long, values);
289 /* Frees the values in VALUES as required by PROTO, by calling
290 value_destroy() on each value for which this is required. The
291 values must previously have been initialized using
292 e.g. caseproto_init_values.
294 The caller retains ownership of PROTO. */
296 caseproto_destroy_values (const struct caseproto *proto, union value values[])
298 destroy_strings (proto, 0, proto->n_strings, values);
301 /* Copies COUNT values, whose widths are given by widths in PROTO
302 starting with index IDX, from SRC to DST. The caller must
303 ensure that the values in SRC and DST were appropriately
304 initialized using e.g. caseproto_init_values. */
306 caseproto_copy (const struct caseproto *proto, size_t idx, size_t count,
307 union value *dst, const union value *src)
311 assert (caseproto_range_is_valid (proto, idx, count));
312 for (i = 0; i < count; i++)
313 value_copy (&dst[idx + i], &src[idx + i], proto->widths[idx + i]);
317 caseproto_free__ (struct caseproto *proto)
319 free (proto->strings);
320 free (proto->widths);
325 caseproto_refresh_string_cache__ (const struct caseproto *proto_)
327 struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
330 assert (proto->strings == NULL);
331 assert (proto->n_strings > 0);
333 proto->strings = xmalloc (proto->n_strings * sizeof *proto->strings);
335 for (i = 0; i < proto->n_widths; i++)
336 if (proto->widths[i] > 0)
337 proto->strings[n++] = i;
338 assert (n == proto->n_strings);
341 /* Returns a caseproto that can be modified without affecting the contents of
342 any caseproto shared with OLD.
344 The returned caseproto has no strings cache. This is helpful because the
345 caller might be about to invalidate it. */
346 static struct caseproto *
347 caseproto_unshare (struct caseproto *old)
349 assert (old->ref_cnt > 0);
350 if (old->ref_cnt <= 1)
357 struct caseproto *new = xmalloc (sizeof *new);
358 *new = (struct caseproto) {
360 .n_strings = old->n_strings,
361 .n_widths = old->n_widths,
362 .allocated_widths = old->allocated_widths,
363 .widths = xmemdup (old->widths, old->allocated_widths * sizeof *old->widths),
370 try_init_strings (const struct caseproto *proto,
371 size_t first, size_t last, union value values[])
375 if (last > 0 && proto->strings == NULL)
376 caseproto_refresh_string_cache__ (proto);
378 for (i = first; i < last; i++)
380 size_t idx = proto->strings[i];
381 if (!value_try_init (&values[idx], proto->widths[idx]))
383 destroy_strings (proto, first, i, values);
391 init_strings (const struct caseproto *proto,
392 size_t first, size_t last, union value values[])
394 if (!try_init_strings (proto, first, last, values))
399 destroy_strings (const struct caseproto *proto, size_t first, size_t last,
400 union value values[])
404 if (last > 0 && proto->strings == NULL)
405 caseproto_refresh_string_cache__ (proto);
407 for (i = first; i < last; i++)
409 size_t idx = proto->strings[i];
410 value_destroy (&values[idx], proto->widths[idx]);
415 count_strings (const struct caseproto *proto, size_t idx, size_t count)
420 for (i = 0; i < count; i++)
421 n += proto->widths[idx + i] > 0;