work on lexer
[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 void
40 value_copy_rpad (union value *dst, int dst_width,
41                  const union value *src, int src_width,
42                  char pad)
43 {
44   u8_buf_copy_rpad (dst->s, dst_width, src->s, src_width, pad);
45 }
46
47 /* Copies the contents of null-terminated string SRC to string
48    value DST with width DST_WIDTH.  If SRC is more than DST_WIDTH
49    bytes long, then only the first DST_WIDTH bytes are copied; if
50    DST_WIDTH is greater than the length of SRC, then DST is
51    padded on the right with PAD bytes.
52
53    DST must be a string value; that is, DST_WIDTH must be
54    positive. */
55 void
56 value_copy_str_rpad (union value *dst, int dst_width, const uint8_t *src,
57                      char pad)
58 {
59   value_copy_buf_rpad (dst, dst_width, src, u8_strlen (src), pad);
60 }
61
62 /* Copies the SRC_LEN bytes at SRC to string value DST with width
63    DST_WIDTH.  If SRC_LEN is greater than DST_WIDTH, then only
64    the first DST_WIDTH bytes are copied; if DST_WIDTH is greater
65    than SRC_LEN, then DST is padded on the right with PAD bytes.
66
67    DST must be a string value; that is, DST_WIDTH must be
68    positive. */
69 void
70 value_copy_buf_rpad (union value *dst, int dst_width,
71                      const uint8_t *src, size_t src_len, char pad)
72 {
73   u8_buf_copy_rpad (dst->s, dst_width, src, src_len, pad);
74 }
75
76 /* Sets V to the system-missing value for data of the given
77    WIDTH. */
78 void
79 value_set_missing (union value *v, int width)
80 {
81   if (width != -1)
82     {
83       if (width == 0)
84         v->f = SYSMIS;
85       else
86         memset (v->s, ' ', width);
87     }
88 }
89
90 /* Compares A and B, which both have the given WIDTH, and returns
91    a strcmp()-type result. */
92 int
93 value_compare_3way (const union value *a, const union value *b, int width)
94 {
95   return (width == -1 ? 0
96           : width == 0 ? (a->f < b->f ? -1 : a->f > b->f)
97           : memcmp (a->s, b->s, width));
98 }
99
100 /* Returns true if A and B, which must both have the given WIDTH,
101    have equal contents, false if their contents differ. */
102 bool
103 value_equal (const union value *a, const union value *b, int width)
104 {
105   return (width == -1 ? true
106           : width == 0 ? a->f == b->f
107           : !memcmp (a->s, b->s, width));
108 }
109
110 /* Returns a hash of the data in VALUE, which must have the given
111    WIDTH, folding BASIS into the hash value calculation. */
112 unsigned int
113 value_hash (const union value *value, int width, unsigned int basis)
114 {
115   return (width == -1 ? basis
116           : width == 0 ? hash_double (value->f, basis)
117           : hash_bytes (value->s, width, basis));
118 }
119
120 /* Tests whether VALUE may be resized from OLD_WIDTH to
121    NEW_WIDTH, using the following rules that match those for
122    resizing missing values and value labels.  First, OLD_WIDTH
123    and NEW_WIDTH must be both numeric or both string.  Second, if
124    NEW_WIDTH is less than OLD_WIDTH, then the bytes that would be
125    trimmed off the right end of VALUE must be all spaces. */
126 bool
127 value_is_resizable (const union value *value, int old_width, int new_width)
128 {
129   if (old_width == new_width)
130     return true;
131   else if (val_type_from_width (old_width) != val_type_from_width (new_width))
132     return false;
133   else
134     {
135       const uint8_t *str = value->s;
136       int i;
137
138       for (i = new_width; i < old_width; i++)
139         if (str[i] != ' ')
140           return false;
141       return true;
142     }
143 }
144
145 /* Resizes VALUE from OLD_WIDTH to NEW_WIDTH.  The arguments must
146    satisfy the rules specified above for value_is_resizable. */
147 void
148 value_resize (union value *value, int old_width, int new_width)
149 {
150   assert (value_is_resizable (value, old_width, new_width));
151   if (new_width != old_width && new_width > 0)
152     {
153       union value tmp;
154       value_init (&tmp, new_width);
155       value_copy_rpad (&tmp, new_width, value, old_width, ' ');
156       value_destroy (value, old_width);
157       *value = tmp;
158     }
159 }
160
161 /* Returns true if VALUE, with the given WIDTH, is all spaces, false otherwise.
162    Returns false if VALUE is numeric. */
163 bool
164 value_is_spaces (const union value *value, int width)
165 {
166   int i;
167
168   for (i = 0; i < width; i++)
169     if (value->s[i] != ' ')
170       return false;
171
172   return true;
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   return old_width != new_width;
189 }
190
191 /* Same as value_init, except that memory for VALUE (if
192    necessary) is allocated from POOL and will be freed
193    automatically when POOL is destroyed.
194
195    VALUE must not be freed manually by calling value_destroy.  If
196    it needs to be resized, it must be done using
197    value_resize_pool instead of value_resize. */
198 void
199 value_init_pool (struct pool *pool, union value *value, int width)
200 {
201   if (width > 0)
202     value->s = pool_alloc_unaligned (pool, width);
203 }
204
205 /* Same as value_clone(), except that memory for VALUE (if necessary) is
206    allocated from POOL and will be freed automatically when POOL is destroyed.
207
208    VALUE must not be freed manually by calling value_destroy().  If it needs to
209    be resized, it must be done using value_resize_pool() instead of
210    value_resize(). */
211 void
212 value_clone_pool (struct pool *pool,
213                   union value *value, const union value *src, int width)
214 {
215   if (width > 0)
216     value->s = pool_clone_unaligned (pool, src->s, width);
217   else
218     value->f = src->f;
219 }
220
221 /* Same as value_resize, except that VALUE must have been
222    allocated from POOL using value_init_pool.
223
224    This function causes some memory in POOL to be wasted in some
225    cases (until the pool is freed), so it should only be done if
226    this is acceptable. */
227 void
228 value_resize_pool (struct pool *pool, union value *value,
229                    int old_width, int new_width)
230 {
231   assert (value_is_resizable (value, old_width, new_width));
232   if (new_width > old_width)
233     {
234       uint8_t *new_string = pool_alloc_unaligned (pool, new_width);
235       memcpy (new_string, value->s, old_width);
236       value->s = new_string;
237
238       memset (value->s + old_width, ' ', new_width - old_width);
239     }
240 }