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