pivot table procedure conceptually works
[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 (value_str_rw (dst, dst_width), dst_width,
50                  value_str (src, src_width), src_width,
51                  pad);
52 }
53
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.
59
60    DST must be a string value; that is, DST_WIDTH must be
61    positive.
62
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. */
66 void
67 value_copy_str_rpad (union value *dst, int dst_width, const uint8_t *src,
68                      char pad)
69 {
70   value_copy_buf_rpad (dst, dst_width, src, u8_strlen (src), pad);
71 }
72
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.
77
78    DST must be a string value; that is, DST_WIDTH must be
79    positive.
80
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. */
84 void
85 value_copy_buf_rpad (union value *dst, int dst_width,
86                      const uint8_t *src, size_t src_len, char pad)
87 {
88   u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width, src, src_len, pad);
89 }
90
91 /* Sets V to the system-missing value for data of the given
92    WIDTH. */
93 void
94 value_set_missing (union value *v, int width)
95 {
96   if (width != -1)
97     {
98       if (width == 0)
99         v->f = SYSMIS;
100       else
101         memset (value_str_rw (v, width), ' ', width);
102     }
103 }
104
105 /* Compares A and B, which both have the given WIDTH, and returns
106    a strcmp()-type result. */
107 int
108 value_compare_3way (const union value *a, const union value *b, int width)
109 {
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));
113 }
114
115 /* Returns true if A and B, which must both have the given WIDTH,
116    have equal contents, false if their contents differ. */
117 bool
118 value_equal (const union value *a, const union value *b, int width)
119 {
120   return (width == -1 ? true
121           : width == 0 ? a->f == b->f
122           : !memcmp (value_str (a, width), value_str (b, width), width));
123 }
124
125 /* Returns a hash of the data in VALUE, which must have the given
126    WIDTH, folding BASIS into the hash value calculation. */
127 unsigned int
128 value_hash (const union value *value, int width, unsigned int basis)
129 {
130   return (width == -1 ? basis
131           : width == 0 ? hash_double (value->f, basis)
132           : hash_bytes (value_str (value, width), width, basis));
133 }
134
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. */
141 bool
142 value_is_resizable (const union value *value, int old_width, int new_width)
143 {
144   if (old_width == new_width)
145     return true;
146   else if (val_type_from_width (old_width) != val_type_from_width (new_width))
147     return false;
148   else
149     {
150       const uint8_t *str = value_str (value, old_width);
151       int i;
152
153       for (i = new_width; i < old_width; i++)
154         if (str[i] != ' ')
155           return false;
156       return true;
157     }
158 }
159
160 /* Resizes VALUE from OLD_WIDTH to NEW_WIDTH.  The arguments must
161    satisfy the rules specified above for value_is_resizable. */
162 void
163 value_resize (union value *value, int old_width, int new_width)
164 {
165   assert (value_is_resizable (value, old_width, new_width));
166   if (new_width != old_width)
167     {
168       union value tmp;
169       value_init (&tmp, new_width);
170       value_copy_rpad (&tmp, new_width, value, old_width, ' ');
171       value_destroy (value, old_width);
172       *value = tmp;
173     }
174 }
175
176 /* Returns true if VALUE, with the given WIDTH, is all spaces, false otherwise.
177    Returns false if VALUE is numeric. */
178 bool
179 value_is_spaces (const union value *value, int width)
180 {
181   const uint8_t *s = value_str (value, width);
182   int i;
183
184   for (i = 0; i < width; i++)
185     if (s[i] != ' ')
186       return false;
187
188   return true;
189 }
190
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.
195
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. */
199 bool
200 value_needs_resize (int old_width, int new_width)
201 {
202   assert (val_type_from_width (old_width) == val_type_from_width (new_width));
203
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));
214 }
215
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.
219
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. */
223 void
224 value_init_pool (struct pool *pool, union value *value, int width)
225 {
226   if (width > MAX_SHORT_STRING)
227     value->long_string = pool_alloc_unaligned (pool, width);
228 }
229
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.
232
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
235    value_resize(). */
236 void
237 value_clone_pool (struct pool *pool,
238                   union value *value, const union value *src, int width)
239 {
240   if (width > MAX_SHORT_STRING)
241     value->long_string = pool_clone_unaligned (pool, src->long_string, width);
242   else
243     *value = *src;
244 }
245
246 /* Same as value_resize, except that VALUE must have been
247    allocated from POOL using value_init_pool.
248
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. */
252 void
253 value_resize_pool (struct pool *pool, union value *value,
254                    int old_width, int new_width)
255 {
256   assert (value_is_resizable (value, old_width, new_width));
257   if (new_width > old_width)
258     {
259       if (new_width > MAX_SHORT_STRING)
260         {
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;
264         }
265       memset (value_str_rw (value, new_width) + old_width, ' ',
266               new_width - old_width);
267     }
268 }