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