1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 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/value.h"
21 #include "data/val-type.h"
22 #include "data/variable.h"
23 #include "libpspp/hash-functions.h"
24 #include "libpspp/pool.h"
25 #include "libpspp/str.h"
26 #include "gl/unistr.h"
28 #include "gl/minmax.h"
29 #include "gl/xalloc.h"
31 /* Copies the contents of string value SRC with width SRC_WIDTH
32 to string value DST with width DST_WIDTH. If SRC_WIDTH is
33 greater than DST_WIDTH, then only the first DST_WIDTH bytes
34 are copied; if DST_WIDTH is greater than SRC_WIDTH, then DST
35 is padded on the right with PAD bytes.
37 SRC and DST must be string values; that is, SRC_WIDTH and
38 DST_WIDTH must both be positive.
40 It is important that SRC_WIDTH and DST_WIDTH be the actual
41 widths with which SRC and DST were initialized. Passing,
42 e.g., smaller values in order to copy only a prefix of SRC or
43 modify only a prefix of DST will not work in every case. */
45 value_copy_rpad (union value *dst, int dst_width,
46 const union value *src, int src_width,
49 u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width,
50 value_str (src, src_width), src_width,
54 /* Copies the contents of null-terminated string SRC to string
55 value DST with width DST_WIDTH. If SRC is more than DST_WIDTH
56 bytes long, then only the first DST_WIDTH bytes are copied; if
57 DST_WIDTH is greater than the length of SRC, then DST is
58 padded on the right with PAD bytes.
60 DST must be a string value; that is, DST_WIDTH must be
63 It is important that DST_WIDTH be the actual width with which
64 DST was initialized. Passing, e.g., a smaller value in order
65 to modify only a prefix of DST will not work in every case. */
67 value_copy_str_rpad (union value *dst, int dst_width, const uint8_t *src,
70 value_copy_buf_rpad (dst, dst_width, src, u8_strlen (src), pad);
73 /* Copies the SRC_LEN bytes at SRC to string value DST with width
74 DST_WIDTH. If SRC_LEN is greater than DST_WIDTH, then only
75 the first DST_WIDTH bytes are copied; if DST_WIDTH is greater
76 than SRC_LEN, then DST is padded on the right with PAD bytes.
78 DST must be a string value; that is, DST_WIDTH must be
81 It is important that DST_WIDTH be the actual width with which
82 DST was initialized. Passing, e.g., a smaller value in order
83 to modify only a prefix of DST will not work in every case. */
85 value_copy_buf_rpad (union value *dst, int dst_width,
86 const uint8_t *src, size_t src_len, char pad)
88 u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width, src, src_len, pad);
91 /* Sets V to the system-missing value for data of the given
94 value_set_missing (union value *v, int width)
101 memset (value_str_rw (v, width), ' ', width);
105 /* Compares A and B, which both have the given WIDTH, and returns
106 a strcmp()-type result. */
108 value_compare_3way (const union value *a, const union value *b, int width)
110 return (width == -1 ? 0
111 : width == 0 ? (a->f < b->f ? -1 : a->f > b->f)
112 : memcmp (value_str (a, width), value_str (b, width), width));
115 /* Returns true if A and B, which must both have the given WIDTH,
116 have equal contents, false if their contents differ. */
118 value_equal (const union value *a, const union value *b, int width)
120 return (width == -1 ? true
121 : width == 0 ? a->f == b->f
122 : !memcmp (value_str (a, width), value_str (b, width), width));
125 /* Returns a hash of the data in VALUE, which must have the given
126 WIDTH, folding BASIS into the hash value calculation. */
128 value_hash (const union value *value, int width, unsigned int basis)
130 return (width == -1 ? basis
131 : width == 0 ? hash_double (value->f, basis)
132 : hash_bytes (value_str (value, width), width, basis));
135 /* Tests whether VALUE may be resized from OLD_WIDTH to
136 NEW_WIDTH, using the following rules that match those for
137 resizing missing values and value labels. First, OLD_WIDTH
138 and NEW_WIDTH must be both numeric or both string. Second, if
139 NEW_WIDTH is less than OLD_WIDTH, then the bytes that would be
140 trimmed off the right end of VALUE must be all spaces. */
142 value_is_resizable (const union value *value, int old_width, int new_width)
144 if (old_width == new_width)
146 else if (val_type_from_width (old_width) != val_type_from_width (new_width))
150 const uint8_t *str = value_str (value, old_width);
153 for (i = new_width; i < old_width; i++)
160 /* Resizes VALUE from OLD_WIDTH to NEW_WIDTH. The arguments must
161 satisfy the rules specified above for value_is_resizable. */
163 value_resize (union value *value, int old_width, int new_width)
165 assert (value_is_resizable (value, old_width, new_width));
166 if (new_width != old_width)
169 value_init (&tmp, new_width);
170 value_copy_rpad (&tmp, new_width, value, old_width, ' ');
171 value_destroy (value, old_width);
176 /* Returns true if VALUE, with the given WIDTH, is all spaces, false otherwise.
177 Returns false if VALUE is numeric. */
179 value_is_spaces (const union value *value, int width)
181 const uint8_t *s = value_str (value, width);
184 for (i = 0; i < width; i++)
191 /* Returns true if resizing a value from OLD_WIDTH to NEW_WIDTH
192 actually changes anything, false otherwise. If false is
193 returned, calls to value_resize() with the specified
194 parameters may be omitted without any ill effects.
196 This is generally useful only if many values can skip being
197 resized from OLD_WIDTH to NEW_WIDTH. Otherwise you might as
198 well just call value_resize directly. */
200 value_needs_resize (int old_width, int new_width)
202 assert (val_type_from_width (old_width) == val_type_from_width (new_width));
204 /* We need to call value_resize if either the new width is
205 longer than the old width (in which case the new characters
206 must be set to spaces) or if either width is a long string.
207 (We could omit resizing if both the old and new widths were
208 long and the new width was shorter, but we choose to do so
209 anyway in hopes of saving memory.) */
210 return (old_width != new_width
211 && (new_width > old_width
212 || old_width > MAX_SHORT_STRING
213 || new_width > MAX_SHORT_STRING));
216 /* Same as value_init, except that memory for VALUE (if
217 necessary) is allocated from POOL and will be freed
218 automatically when POOL is destroyed.
220 VALUE must not be freed manually by calling value_destroy. If
221 it needs to be resized, it must be done using
222 value_resize_pool instead of value_resize. */
224 value_init_pool (struct pool *pool, union value *value, int width)
226 if (width > MAX_SHORT_STRING)
227 value->long_string = pool_alloc_unaligned (pool, width);
230 /* Same as value_clone(), except that memory for VALUE (if necessary) is
231 allocated from POOL and will be freed automatically when POOL is destroyed.
233 VALUE must not be freed manually by calling value_destroy(). If it needs to
234 be resized, it must be done using value_resize_pool() instead of
237 value_clone_pool (struct pool *pool,
238 union value *value, const union value *src, int width)
240 if (width > MAX_SHORT_STRING)
241 value->long_string = pool_clone_unaligned (pool, src->long_string, width);
246 /* Same as value_resize, except that VALUE must have been
247 allocated from POOL using value_init_pool.
249 This function causes some memory in POOL to be wasted in some
250 cases (until the pool is freed), so it should only be done if
251 this is acceptable. */
253 value_resize_pool (struct pool *pool, union value *value,
254 int old_width, int new_width)
256 assert (value_is_resizable (value, old_width, new_width));
257 if (new_width > old_width)
259 if (new_width > MAX_SHORT_STRING)
261 uint8_t *new_long_string = pool_alloc_unaligned (pool, new_width);
262 memcpy (new_long_string, value_str (value, old_width), old_width);
263 value->long_string = new_long_string;
265 memset (value_str_rw (value, new_width) + old_width, ' ',
266 new_width - old_width);