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 N widths removed
146 starting at index IDX. */
148 caseproto_remove_widths (struct caseproto *proto, size_t idx, size_t n)
150 assert (caseproto_range_is_valid (proto, idx, n));
152 proto = caseproto_unshare (proto);
153 proto->n_strings -= count_strings (proto, idx, n);
154 remove_range (proto->widths, proto->n_widths, sizeof *proto->widths,
156 proto->n_widths -= n;
160 /* Returns a replacement for PROTO in which the N 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, n));
169 assert (caseproto_range_is_valid (proto, new_start, n));
171 proto = caseproto_unshare (proto);
172 move_range (proto->widths, proto->n_widths, sizeof *proto->widths,
173 old_start, new_start, n);
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_range_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 A and B have the same widths, false otherwise. */
226 caseproto_equal (const struct caseproto *a, const struct caseproto *b)
228 return (a == b ? true
229 : a->n_widths != b->n_widths ? false
230 : caseproto_range_equal (a, 0, b, 0, a->n_widths));
233 /* Returns true if an array of values that is to be used for
234 data of the format specified in PROTO needs to be initialized
235 by calling caseproto_init_values, false if that step may be
236 skipped because such an initialization would be a no-op anyhow.
238 This optimization is useful only when a large number of
239 initializations of such arrays may be skipped as a group. */
241 caseproto_needs_init_values (const struct caseproto *proto)
243 return proto->n_strings > 0;
246 /* Initializes the values in VALUES as required by PROTO, by
247 calling value_init() on each value for which this is required.
248 The data in VALUES have indeterminate contents until
251 VALUES must have at least caseproto_get_n_widths(PROTO)
252 elements; only that many elements of VALUES are initialized.
254 The caller retains ownership of PROTO. */
256 caseproto_init_values (const struct caseproto *proto, union value values[])
258 init_strings (proto, 0, proto->n_strings, values);
261 /* Like caseproto_init_values, but returns false instead of
262 terminating if memory cannot be obtained. */
264 caseproto_try_init_values (const struct caseproto *proto, union value values[])
266 return try_init_strings (proto, 0, proto->n_strings, values);
269 /* Initializes the data in VALUES that are in NEW but not in OLD,
270 destroys the data in VALUES that are in OLD but not NEW, and
271 does not modify the data in VALUES that are in both OLD and
272 NEW. VALUES must previously have been initialized as required
273 by OLD using e.g. caseproto_init_values. The data in VALUES
274 that are in NEW but not in OLD will have indeterminate
275 contents until explicitly written.
277 OLD and NEW must be conformable for this operation, as
278 reported by caseproto_is_conformable.
280 The caller retains ownership of OLD and NEW. */
282 caseproto_reinit_values (const struct caseproto *old,
283 const struct caseproto *new, union value values[])
285 size_t old_n_long = old->n_strings;
286 size_t new_n_long = new->n_strings;
288 expensive_assert (caseproto_is_conformable (old, new));
290 if (new_n_long > old_n_long)
291 init_strings (new, old_n_long, new_n_long, values);
292 else if (new_n_long < old_n_long)
293 destroy_strings (old, new_n_long, old_n_long, values);
296 /* Frees the values in VALUES as required by PROTO, by calling
297 value_destroy() on each value for which this is required. The
298 values must previously have been initialized using
299 e.g. caseproto_init_values.
301 The caller retains ownership of PROTO. */
303 caseproto_destroy_values (const struct caseproto *proto, union value values[])
305 destroy_strings (proto, 0, proto->n_strings, values);
308 /* Copies COUNT values, whose widths are given by widths in PROTO
309 starting with index IDX, from SRC to DST. The caller must
310 ensure that the values in SRC and DST were appropriately
311 initialized using e.g. caseproto_init_values. */
313 caseproto_copy (const struct caseproto *proto, size_t idx, size_t count,
314 union value *dst, const union value *src)
318 assert (caseproto_range_is_valid (proto, idx, count));
319 for (i = 0; i < count; i++)
320 value_copy (&dst[idx + i], &src[idx + i], proto->widths[idx + i]);
324 caseproto_free__ (struct caseproto *proto)
326 free (proto->strings);
331 caseproto_refresh_string_cache__ (const struct caseproto *proto_)
333 struct caseproto *proto = CONST_CAST (struct caseproto *, proto_);
336 assert (proto->strings == NULL);
337 assert (proto->n_strings > 0);
339 proto->strings = xmalloc (proto->n_strings * sizeof *proto->strings);
341 for (i = 0; i < proto->n_widths; i++)
342 if (proto->widths[i] > 0)
343 proto->strings[n++] = i;
344 assert (n == proto->n_strings);
347 static struct caseproto *
348 caseproto_unshare (struct caseproto *old)
350 struct caseproto *new;
351 if (old->ref_cnt > 1)
353 new = xmemdup (old, caseproto_size (old->allocated_widths));
367 try_init_strings (const struct caseproto *proto,
368 size_t first, size_t last, union value values[])
372 if (last > 0 && proto->strings == NULL)
373 caseproto_refresh_string_cache__ (proto);
375 for (i = first; i < last; i++)
377 size_t idx = proto->strings[i];
378 if (!value_try_init (&values[idx], proto->widths[idx]))
380 destroy_strings (proto, first, i, values);
388 init_strings (const struct caseproto *proto,
389 size_t first, size_t last, union value values[])
391 if (!try_init_strings (proto, first, last, values))
396 destroy_strings (const struct caseproto *proto, size_t first, size_t last,
397 union value values[])
401 if (last > 0 && proto->strings == NULL)
402 caseproto_refresh_string_cache__ (proto);
404 for (i = first; i < last; i++)
406 size_t idx = proto->strings[i];
407 value_destroy (&values[idx], proto->widths[idx]);
412 count_strings (const struct caseproto *proto, size_t idx, size_t count)
417 for (i = 0; i < count; i++)
418 n += proto->widths[idx + i] > 0;