Fri Dec 19 15:08:38 2003 Ben Pfaff <blp@gnu.org>
[pspp-builds.git] / src / value-labels.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "alloc.h"
25 #include "hash.h"
26 #include "value-labels.h"
27
28 static hsh_compare_func compare_int_val_lab;
29 static hsh_hash_func hash_int_val_lab;
30 static hsh_free_func free_int_val_lab;
31
32 struct atom;
33 static struct atom *atom_create (const char *string);
34 static void atom_destroy (struct atom *);
35 static const char *atom_to_string (const struct atom *);
36
37 /* A set of value labels. */
38 struct val_labs 
39   {
40     int width;                  /* 0=numeric, otherwise string width. */
41     struct hsh_table *labels;   /* Hash table of `struct int_val_lab's. */
42   };
43
44 /* Creates and returns a new, empty set of value labels with the
45    given WIDTH, which must designate a numeric (0) or short
46    string (1...MAX_SHORT_STRING inclusive) width. */
47 struct val_labs *
48 val_labs_create (int width) 
49 {
50   struct val_labs *vls;
51
52   assert (width >= 0);
53
54   vls = xmalloc (sizeof *vls);
55   vls->width = width;
56   vls->labels = NULL;
57   return vls;
58 }
59
60 /* Creates and returns a new set of value labels identical to
61    VLS. */
62 struct val_labs *
63 val_labs_copy (const struct val_labs *vls) 
64 {
65   struct val_labs *copy;
66   struct val_labs_iterator *i;
67   struct val_lab *vl;
68
69   assert (vls != NULL);
70
71   copy = val_labs_create (vls->width);
72   for (vl = val_labs_first (vls, &i); vl != NULL;
73        vl = val_labs_next (vls, &i)) 
74     val_labs_add (copy, vl->value, vl->label);
75   return copy;
76 }
77
78 /* Changes the width of VLS to NEW_WIDTH.  If VLS is numeric,
79    NEW_WIDTH must be 0, otherwise it must be within the range
80    1...MAX_SHORT_STRING inclusive. */
81 void
82 val_labs_set_width (struct val_labs *vls, int new_width) 
83 {
84   assert (vls != NULL);
85   assert ((vls->width == 0) == (new_width == 0));
86
87   vls->width = new_width;
88 }
89
90 /* Destroys VLS. */
91 void
92 val_labs_destroy (struct val_labs *vls) 
93 {
94   if (vls != NULL && vls->labels != NULL) 
95     hsh_destroy (vls->labels);
96 }
97
98 /* Removes all the value labels from VLS. */
99 void
100 val_labs_clear (struct val_labs *vls) 
101 {
102   assert (vls != NULL);
103
104   hsh_destroy (vls->labels);
105   vls->labels = NULL;
106 }
107
108 /* Returns the number of value labels in VLS. */
109 size_t
110 val_labs_count (struct val_labs *vls) 
111 {
112   assert (vls != NULL);
113
114   if (vls->labels == NULL)
115     return 0;
116   else
117     return hsh_count (vls->labels);
118 }
119 \f
120 /* One value label in internal format. */
121 struct int_val_lab
122   {
123     union value value;          /* The value being labeled. */
124     struct atom *label;         /* A ref-counted string. */
125   };
126
127 /* Creates and returns an int_val_lab based on VALUE and
128    LABEL. */
129 static struct int_val_lab *
130 create_int_val_lab (struct val_labs *vls, union value value, const char *label) 
131 {
132   struct int_val_lab *ivl;
133
134   assert (label != NULL);
135   assert (vls->width <= MAX_SHORT_STRING);
136   
137   ivl = xmalloc (sizeof *ivl);
138   ivl->value = value;
139   if (vls->width > 0)
140     memset (ivl->value.s + vls->width, ' ', MAX_SHORT_STRING - vls->width);
141   ivl->label = atom_create (label);
142
143   return ivl;
144 }
145
146 /* If VLS does not already contain a value label for VALUE, adds
147    LABEL for it and returns nonzero.  Otherwise, returns zero.
148    Behavior is undefined if VLS's width is greater than
149    MAX_SHORT_STRING. */
150 int
151 val_labs_add (struct val_labs *vls, union value value, const char *label) 
152 {
153   struct int_val_lab *ivl;
154   void **vlpp;
155
156   assert (vls != NULL);
157   assert (vls->width <= MAX_SHORT_STRING);
158   assert (label != NULL);
159
160   if (vls->labels == NULL) 
161     vls->labels = hsh_create (8, compare_int_val_lab, hash_int_val_lab,
162                               free_int_val_lab, vls);
163
164   ivl = create_int_val_lab (vls, value, label);
165   vlpp = hsh_probe (vls->labels, ivl);
166   if (*vlpp == NULL) 
167     {
168       *vlpp = ivl;
169       return 1; 
170     }
171   else 
172     {
173       free_int_val_lab (ivl, vls);
174       return 0;
175     }
176 }
177
178 /* Sets LABEL as the value label for VALUE in VLS.  Returns zero
179    if there wasn't already a value label for VALUE, or nonzero if
180    there was.  Behavior is undefined if VLS's width is greater
181    than MAX_SHORT_STRING. */
182 int
183 val_labs_replace (struct val_labs *vls, union value value, const char *label) 
184 {
185   struct int_val_lab *ivl;
186
187   assert (vls != NULL);
188   assert (vls->width <= MAX_SHORT_STRING);
189   assert (label != NULL);
190
191   if (vls->labels == NULL)
192     {
193       val_labs_add (vls, value, label);
194       return 0;
195     }
196
197   ivl = hsh_replace (vls->labels, create_int_val_lab (vls, value, label));
198   if (ivl == NULL) 
199     return 0;
200   else 
201     {
202       free_int_val_lab (ivl, vls);
203       return 1;
204     }
205 }
206
207 /* Removes any value label for VALUE within VLS.  Returns nonzero
208    if a value label was removed. Behavior is undefined if VLS's
209    width is greater than MAX_SHORT_STRING. */
210 int 
211 val_labs_remove (struct val_labs *vls, union value value) 
212 {
213   assert (vls != NULL);
214   assert (vls->width <= MAX_SHORT_STRING);
215
216   if (vls->labels != NULL) 
217     {
218       struct int_val_lab *ivl = create_int_val_lab (vls, value, "");
219       int deleted = hsh_delete (vls->labels, &ivl);
220       free (ivl);
221       return deleted;
222     }
223   else
224     return 0;
225 }
226
227 /* Searches VLS for a value label for VALUE.  If successful,
228    returns the label; otherwise, returns a null pointer.  If
229    VLS's width is greater than MAX_SHORT_STRING, always returns a
230    null pointer. */
231 const char *
232 val_labs_find (const struct val_labs *vls, union value value) 
233 {
234   assert (vls != NULL);
235
236   if (vls->width > MAX_SHORT_STRING)
237     return NULL;
238
239   if (vls->labels != NULL) 
240     {
241       struct int_val_lab ivl, *vlp;
242
243       ivl.value = value;
244       vlp = hsh_find (vls->labels, &ivl);
245       if (vlp != NULL)
246         return atom_to_string (vlp->label);
247     }
248   return NULL;
249 }
250 \f
251 /* A value labels iterator. */
252 struct val_labs_iterator 
253   {
254     void **labels;              /* The labels, in order. */
255     void **lp;                  /* Current label. */
256     struct val_lab vl;          /* Structure presented to caller. */
257   };
258
259 /* Sets up *IP for iterating through the value labels in VLS in
260    no particular order.  Returns the first value label or a null
261    pointer if VLS is empty.  If the return value is non-null,
262    then val_labs_next() may be used to continue iterating or
263    val_labs_done() to free up the iterator.  Otherwise, neither
264    function may be called for *IP. */
265 struct val_lab *
266 val_labs_first (const struct val_labs *vls, struct val_labs_iterator **ip) 
267 {
268   struct val_labs_iterator *i;
269
270   assert (vls != NULL);
271   assert (ip != NULL);
272
273   if (vls->labels == NULL || vls->width > MAX_SHORT_STRING)
274     return NULL;
275
276   i = *ip = xmalloc (sizeof *i);
277   i->labels = hsh_data_copy (vls->labels);
278   i->lp = i->labels;
279   return val_labs_next (vls, ip);
280 }
281
282 /* Sets up *IP for iterating through the value labels in VLS in
283    sorted order of values.  Returns the first value label or a
284    null pointer if VLS is empty.  If the return value is
285    non-null, then val_labs_next() may be used to continue
286    iterating or val_labs_done() to free up the iterator.
287    Otherwise, neither function may be called for *IP. */
288 struct val_lab *
289 val_labs_first_sorted (const struct val_labs *vls,
290                        struct val_labs_iterator **ip)
291 {
292   struct val_labs_iterator *i;
293
294   assert (vls != NULL);
295   assert (ip != NULL);
296
297   if (vls->labels == NULL || vls->width > MAX_SHORT_STRING)
298     return NULL;
299
300   i = *ip = xmalloc (sizeof *i);
301   i->lp = i->labels = hsh_sort_copy (vls->labels);
302   return val_labs_next (vls, ip);
303 }
304
305 /* Returns the next value label in an iteration begun by
306    val_labs_first() or val_labs_first_sorted().  If the return
307    value is non-null, then val_labs_next() may be used to
308    continue iterating or val_labs_done() to free up the iterator.
309    Otherwise, neither function may be called for *IP. */
310 struct val_lab *
311 val_labs_next (const struct val_labs *vls, struct val_labs_iterator **ip)
312 {
313   struct val_labs_iterator *i;
314   struct int_val_lab *ivl;
315   
316   assert (vls != NULL);
317   assert (vls->width <= MAX_SHORT_STRING);
318   assert (ip != NULL);
319   assert (*ip != NULL);
320
321   i = *ip;
322   ivl = *i->lp++;
323   if (ivl != NULL) 
324     {
325       i->vl.value = ivl->value;
326       i->vl.label = atom_to_string (ivl->label);
327       return &i->vl;
328     }
329   else 
330     {
331       free (i->labels);
332       free (i);
333       *ip = NULL;
334       return NULL;
335     }
336 }
337
338 /* Discards the state for an incomplete iteration begun by
339    val_labs_first() or val_labs_first_sorted(). */
340 void 
341 val_labs_done (struct val_labs_iterator **ip) 
342 {
343   struct val_labs_iterator *i;
344
345   assert (ip != NULL);
346   assert (*ip != NULL);
347   
348   i = *ip;
349   free (i->labels);
350   free (i);
351   *ip = NULL;
352 }
353 \f
354 /* Compares two value labels and returns a strcmp()-type result. */
355 int
356 compare_int_val_lab (const void *a_, const void *b_, void *vls_)
357 {
358   const struct int_val_lab *a = a_;
359   const struct int_val_lab *b = b_;
360   const struct val_labs *vls = vls_;
361
362   if (vls->width == 0) 
363     return a->value.f < b->value.f ? -1 : a->value.f > b->value.f;
364   else
365     return memcmp (a->value.s, b->value.s, vls->width);
366 }
367
368 /* Hash a value label. */
369 unsigned
370 hash_int_val_lab (const void *vl_, void *vls_)
371 {
372   const struct int_val_lab *vl = vl_;
373   const struct val_labs *vls = vls_;
374
375   if (vls->width == 0)
376     return hsh_hash_double (vl->value.f);
377   else
378     return hsh_hash_bytes (vl->value.s, sizeof vl->value.s);
379 }
380
381 /* Free a value label. */
382 void
383 free_int_val_lab (void *vl_, void *vls_ unused) 
384 {
385   struct int_val_lab *vl = vl_;
386
387   atom_destroy (vl->label);
388   free (vl);
389 }
390 \f
391 /* Atoms. */
392
393 /* An atom. */
394 struct atom 
395   {
396     char *string;               /* String value. */
397     unsigned ref_count;         /* Number of references. */
398   };
399
400 static hsh_compare_func compare_atoms;
401 static hsh_hash_func hash_atom;
402 static hsh_free_func free_atom;
403
404 /* Hash table of atoms. */
405 static struct hsh_table *atoms;
406
407 /* Creates and returns an atom for STRING. */
408 static struct atom *
409 atom_create (const char *string) 
410 {
411   struct atom a;
412   void **app;
413   
414   assert (string != NULL);
415           
416   if (atoms == NULL) 
417     atoms = hsh_create (8, compare_atoms, hash_atom, free_atom, NULL);
418
419   a.string = (char *) string;
420   app = hsh_probe (atoms, &a);
421   if (*app != NULL) 
422     {
423       struct atom *ap = *app;
424       ap->ref_count++;
425       return ap;
426     }
427   else
428     {
429       struct atom *ap = xmalloc (sizeof *ap);
430       ap->string = xstrdup (string);
431       ap->ref_count = 1;
432       *app = ap;
433       return ap;
434     }
435 }
436
437 /* Destroys ATOM. */
438 static void 
439 atom_destroy (struct atom *atom)
440 {
441   if (atom != NULL) 
442     {
443       assert (atom->ref_count > 0);
444       atom->ref_count--;
445       if (atom->ref_count == 0) 
446         hsh_force_delete (atoms, atom);
447     }
448 }
449
450 /* Returns the string associated with ATOM. */
451 static const char *
452 atom_to_string (const struct atom *atom) 
453 {
454   assert (atom != NULL);
455   
456   return atom->string;
457 }
458
459 /* A hsh_compare_func that compares A and B. */
460 static int
461 compare_atoms (const void *a_, const void *b_, void *aux unused) 
462 {
463   const struct atom *a = a_;
464   const struct atom *b = b_;
465
466   return strcmp (a->string, b->string);
467 }
468
469 /* A hsh_hash_func that hashes ATOM. */
470 static unsigned
471 hash_atom (const void *atom_, void *aux unused) 
472 {
473   const struct atom *atom = atom_;
474
475   return hsh_hash_string (atom->string);
476 }
477
478 /* A hsh_free_func that destroys ATOM. */
479 static void
480 free_atom (void *atom_, void *aux unused) 
481 {
482   struct atom *atom = atom_;
483
484   free (atom->string);
485   free (atom);
486 }