Change union value type to contain uint8_t types instead of char.
[pspp-builds.git] / src / data / value.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18 #include <data/value.h>
19
20 #include <data/val-type.h>
21 #include <data/variable.h>
22 #include <libpspp/hash.h>
23 #include <libpspp/pool.h>
24 #include <libpspp/str.h>
25 #include <gl/unistr.h>
26
27 #include "minmax.h"
28 #include "xalloc.h"
29
30 /* Copies the contents of string value SRC with width SRC_WIDTH
31    to string value DST with width DST_WIDTH.  If SRC_WIDTH is
32    greater than DST_WIDTH, then only the first DST_WIDTH bytes
33    are copied; if DST_WIDTH is greater than SRC_WIDTH, then DST
34    is padded on the right with PAD bytes.
35
36    SRC and DST must be string values; that is, SRC_WIDTH and
37    DST_WIDTH must both be positive.
38
39    It is important that SRC_WIDTH and DST_WIDTH be the actual
40    widths with which SRC and DST were initialized.  Passing,
41    e.g., smaller values in order to copy only a prefix of SRC or
42    modify only a prefix of DST will not work in every case. */
43 void
44 value_copy_rpad (union value *dst, int dst_width,
45                  const union value *src, int src_width,
46                  char pad)
47 {
48   u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width,
49                  value_str (src, src_width), src_width,
50                  pad);
51 }
52
53 /* Copies the contents of null-terminated string SRC to string
54    value DST with width DST_WIDTH.  If SRC is more than DST_WIDTH
55    bytes long, then only the first DST_WIDTH bytes are copied; if
56    DST_WIDTH is greater than the length of SRC, then DST is
57    padded on the right with PAD bytes.
58
59    DST must be a string value; that is, DST_WIDTH must be
60    positive.
61
62    It is important that DST_WIDTH be the actual width with which
63    DST was initialized.  Passing, e.g., a smaller value in order
64    to modify only a prefix of DST will not work in every case. */
65 void
66 value_copy_str_rpad (union value *dst, int dst_width, const uint8_t *src,
67                      char pad)
68 {
69   value_copy_buf_rpad (dst, dst_width, src, u8_strlen (src), pad);
70 }
71
72 /* Copies the SRC_LEN bytes at SRC to string value DST with width
73    DST_WIDTH.  If SRC_LEN is greater than DST_WIDTH, then only
74    the first DST_WIDTH bytes are copied; if DST_WIDTH is greater
75    than SRC_LEN, then DST is padded on the right with PAD bytes.
76
77    DST must be a string value; that is, DST_WIDTH must be
78    positive.
79
80    It is important that DST_WIDTH be the actual width with which
81    DST was initialized.  Passing, e.g., a smaller value in order
82    to modify only a prefix of DST will not work in every case. */
83 void
84 value_copy_buf_rpad (union value *dst, int dst_width,
85                      const uint8_t *src, size_t src_len, char pad)
86 {
87   u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width, src, src_len, pad);
88 }
89
90 /* Sets V to the system-missing value for data of the given
91    WIDTH. */
92 void
93 value_set_missing (union value *v, int width)
94 {
95   if (width != -1)
96     {
97       if (width == 0)
98         v->f = SYSMIS;
99       else
100         memset (value_str_rw (v, width), ' ', width);
101     }
102 }
103
104 /* Compares A and B, which both have the given WIDTH, and returns
105    a strcmp()-type result. */
106 int
107 value_compare_3way (const union value *a, const union value *b, int width)
108 {
109   return (width == -1 ? 0
110           : width == 0 ? (a->f < b->f ? -1 : a->f > b->f)
111           : memcmp (value_str (a, width), value_str (b, width), width));
112 }
113
114 /* Returns true if A and B, which must both have the given WIDTH,
115    have equal contents, false if their contents differ. */
116 bool
117 value_equal (const union value *a, const union value *b, int width)
118 {
119   return (width == -1 ? true
120           : width == 0 ? a->f == b->f
121           : !memcmp (value_str (a, width), value_str (b, width), width));
122 }
123
124 /* Returns a hash of the data in VALUE, which must have the given
125    WIDTH, folding BASIS into the hash value calculation. */
126 unsigned int
127 value_hash (const union value *value, int width, unsigned int basis)
128 {
129   return (width == -1 ? basis
130           : width == 0 ? hash_double (value->f, basis)
131           : hash_bytes (value_str (value, width), width, basis));
132 }
133
134 /* Tests whether VALUE may be resized from OLD_WIDTH to
135    NEW_WIDTH, using the following rules that match those for
136    resizing missing values and value labels.  First, OLD_WIDTH
137    and NEW_WIDTH must be both numeric or both string.  Second, if
138    NEW_WIDTH is less than OLD_WIDTH, then the bytes that would be
139    trimmed off the right end of VALUE must be all spaces. */
140 bool
141 value_is_resizable (const union value *value, int old_width, int new_width)
142 {
143   if (old_width == new_width)
144     return true;
145   else if (val_type_from_width (old_width) != val_type_from_width (new_width))
146     return false;
147   else
148     {
149       const uint8_t *str = value_str (value, old_width);
150       int i;
151
152       for (i = new_width; i < old_width; i++)
153         if (str[i] != ' ')
154           return false;
155       return true;
156     }
157 }
158
159 /* Resizes VALUE from OLD_WIDTH to NEW_WIDTH.  The arguments must
160    satisfy the rules specified above for value_is_resizable. */
161 void
162 value_resize (union value *value, int old_width, int new_width)
163 {
164   assert (value_is_resizable (value, old_width, new_width));
165   if (new_width != old_width)
166     {
167       union value tmp;
168       value_init (&tmp, new_width);
169       value_copy_rpad (&tmp, new_width, value, old_width, ' ');
170       value_destroy (value, old_width);
171       *value = tmp;
172     }
173 }
174
175 /* Returns true if resizing a value from OLD_WIDTH to NEW_WIDTH
176    actually changes anything, false otherwise.  If false is
177    returned, calls to value_resize() with the specified
178    parameters may be omitted without any ill effects.
179
180    This is generally useful only if many values can skip being
181    resized from OLD_WIDTH to NEW_WIDTH.  Otherwise you might as
182    well just call value_resize directly. */
183 bool
184 value_needs_resize (int old_width, int new_width)
185 {
186   assert (val_type_from_width (old_width) == val_type_from_width (new_width));
187
188   /* We need to call value_resize if either the new width is
189      longer than the old width (in which case the new characters
190      must be set to spaces) or if either width is a long string.
191      (We could omit resizing if both the old and new widths were
192      long and the new width was shorter, but we choose to do so
193      anyway in hopes of saving memory.) */
194   return (old_width != new_width
195            && (new_width > old_width
196                || old_width > MAX_SHORT_STRING
197                || new_width > MAX_SHORT_STRING));
198 }
199
200 /* Same as value_init, except that memory for VALUE (if
201    necessary) is allocated from POOL and will be freed
202    automatically when POOL is destroyed.
203
204    VALUE must not be freed manually by calling value_destroy.  If
205    it needs to be resized, it must be done using
206    value_resize_pool instead of value_resize. */
207 void
208 value_init_pool (struct pool *pool, union value *value, int width)
209 {
210   if (width > MAX_SHORT_STRING)
211     value->long_string = pool_alloc_unaligned (pool, width);
212 }
213
214 /* Same as value_resize, except that VALUE must have been
215    allocated from POOL using value_init_pool.
216
217    This function causes some memory in POOL to be wasted in some
218    cases (until the pool is freed), so it should only be done if
219    this is acceptable. */
220 void
221 value_resize_pool (struct pool *pool, union value *value,
222                    int old_width, int new_width)
223 {
224   assert (value_is_resizable (value, old_width, new_width));
225   if (new_width > old_width)
226     {
227       if (new_width > MAX_SHORT_STRING)
228         {
229           uint8_t *new_long_string = pool_alloc_unaligned (pool, new_width);
230           memcpy (new_long_string, value_str (value, old_width), old_width);
231           value->long_string = new_long_string;
232         }
233       memset (value_str_rw (value, new_width) + old_width, ' ',
234               new_width - old_width);
235     }
236 }