pxd: initial work
[pspp] / src / libpspp / pxd.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2010, 2013 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 "libpspp/pxd.h"
20
21 #include <gdbm.h>
22 #include <stdlib.h>
23
24 #include "data/value.h"
25 #include "libpspp/cast.h"
26 #include "libpspp/compiler.h"
27 #include "libpspp/float-format.h"
28 #include "libpspp/integer-format.h"
29 #include "libpspp/intern.h"
30
31 #include "gl/error.h"
32 #include "gl/minmax.h"
33 #include "gl/sha1.h"
34 #include "gl/xalloc.h"
35 #include "gl/xmemdup0.h"
36
37 struct pxd
38   {
39     GDBM_FILE dbf;
40   };
41
42 static struct pxd_object *pxd_object_lookup__ (const struct pxd_id *id);
43 static struct pxd_object *pxd_object_create_raw__ (char *raw, size_t n_links,
44                                                    size_t size,
45                                                    const struct pxd_id *);
46
47 static datum
48 make_datum (const void *data, size_t size)
49 {
50   datum d;
51
52   d.dptr = CONST_CAST (void *, data);
53   d.dsize = size;
54
55   return d;
56 }
57
58 static datum
59 string_datum (const char *s)
60 {
61   return make_datum (s, strlen (s));
62 }
63
64 static datum
65 id_datum (const struct pxd_id *id)
66 {
67   return make_datum (id, sizeof *id);
68 }
69
70 static datum
71 root_key (void)
72 {
73   return string_datum ("root");
74 }
75
76 void
77 pxd_get_root (const struct pxd *pxd, struct pxd_id *id)
78 {
79   datum content;
80
81   content = gdbm_fetch (pxd->dbf, root_key ());
82   if (content.dptr != NULL && content.dsize == sizeof *id)
83     memcpy (id, content.dptr, sizeof *id);
84   else
85     memset (id, 0, sizeof *id);
86   free (content.dptr);
87 }
88
89 struct pxd *
90 pxd_open (const char *name, bool may_create)
91 {
92   struct pxd *pxd;
93   GDBM_FILE dbf;
94
95   dbf = gdbm_open (CONST_CAST (char *, name), 4096,
96                    may_create ? GDBM_WRCREAT : GDBM_WRITER, 0777, NULL);
97   if (dbf == NULL)
98     {
99       error (0, 0, "%s: open failed (%s)", name, gdbm_strerror (gdbm_errno));
100       return NULL;
101     }
102
103   pxd = xmalloc (sizeof *pxd);
104   pxd->dbf = dbf;
105   return pxd;
106 }
107
108 void
109 pxd_close (struct pxd *pxd)
110 {
111   if (pxd != NULL)
112     {
113       gdbm_close (pxd->dbf);
114       free (pxd);
115     }
116 }
117
118 struct pxd_object *
119 pxd_fetch (const struct pxd *pxd, const struct pxd_id *id)
120 {
121   struct pxd_object *obj;
122   datum content;
123   uint32_t n_links;
124   size_t header_size;
125
126   obj = pxd_object_lookup__ (id);
127   if (obj != NULL)
128     return obj;
129
130   content = gdbm_fetch (pxd->dbf, id_datum (id));
131   if (content.dptr == NULL)
132     error (1, 0, PXD_ID_FMT": not found in database", PXD_ID_ARGS (id));
133
134   if (content.dsize < sizeof n_links)
135     error (1, 0, PXD_ID_FMT": size %d is less than minimum size 4",
136            PXD_ID_ARGS(id), content.dsize);
137
138   n_links = le32_to_cpu (*(uint32_t *) content.dptr);
139   header_size = 4 + n_links * sizeof (struct pxd_id);
140   if (header_size > content.dsize)
141     error (1, 0, PXD_ID_FMT": size %d is less than minimum size %zu for "
142            "object with %"PRIu32" links",
143            PXD_ID_ARGS (id), content.dsize, header_size, n_links);
144
145   return pxd_object_create_raw__ (content.dptr, n_links,
146                                   content.dsize - header_size, id);
147 }
148
149 void
150 pxd_store (struct pxd *pxd, const struct pxd_object *object)
151 {
152   datum content;
153
154   content = make_datum (pxd_object_raw_data__ (object),
155                         pxd_object_raw_size__ (object));
156   if (gdbm_store (pxd->dbf, id_datum (&object->id), content, GDBM_INSERT) < 0)
157     error (1, 0, PXD_ID_FMT": storing object failed",
158            PXD_ID_ARGS (&object->id));
159 }
160
161 bool
162 pxd_swap_root (struct pxd *pxd, const struct pxd_id *old_root,
163                struct pxd_object *new_root)
164 {
165   struct pxd_id cur_root;
166
167   pxd_get_root (pxd, &cur_root);
168   if (pxd_id_equals (&cur_root, old_root))
169     {
170       if (gdbm_store (pxd->dbf, root_key (), id_datum (&new_root->id),
171                       GDBM_INSERT) < 0)
172         error (1, 0, "storing root object failed");
173       pxd_object_unref (new_root);
174       return true;
175     }
176   else
177     return false;
178 }
179 \f
180 static struct hmap object_table = HMAP_INITIALIZER (object_table);
181
182 static struct pxd_object *
183 pxd_object_lookup__ (const struct pxd_id *id)
184 {
185   const struct pxd_object *obj;
186
187   HMAP_FOR_EACH_IN_BUCKET (obj, struct pxd_object, hmap_node,
188                            pxd_id_hash (id), &object_table)
189     if (pxd_id_equals (id, &obj->id))
190       return pxd_object_ref (obj);
191
192   return NULL;
193 }
194
195 static struct pxd_object *
196 pxd_object_create_raw__ (char *raw, size_t n_links, size_t size,
197                          const struct pxd_id *id)
198 {
199   struct pxd_object *obj;
200
201   obj = xmalloc (sizeof *obj);
202   hmap_insert (&object_table, &obj->hmap_node, pxd_id_hash (id));
203   obj->n_refs = 1;
204   obj->id = *id;
205   obj->n_links = n_links;
206   obj->size = size;
207   obj->links = (struct pxd_id *) (raw + sizeof (uint32_t));
208   obj->data = (uint8_t *) (obj->links + obj->n_links);
209
210   return obj;
211 }
212
213 struct pxd_object *
214 pxd_object_create (const struct pxd_id links[], size_t n_links,
215                    const void *data, size_t size)
216 {
217   struct pxd_object *obj;
218   struct sha1_ctx sha;
219   uint32_t le_n_links;
220   struct pxd_id id;
221   char *raw;
222
223   /* Hash the raw form of the object. */
224   sha1_init_ctx (&sha);
225   le_n_links = cpu_to_le32 (n_links);
226   sha1_process_bytes (&le_n_links, sizeof le_n_links, &sha);
227   sha1_process_bytes (links, n_links * sizeof *links, &sha);
228   sha1_process_bytes (data, size, &sha);
229   sha1_finish_ctx (&sha, &id);
230
231   /* Reuse an existing copy if there is one. */
232   obj = pxd_object_lookup__ (&id);
233   if (obj != NULL)
234     return obj;
235
236   /* Build a new object. */
237   raw = xmalloc (sizeof le_n_links + n_links * sizeof *links +
238                  size);
239   memcpy (raw, &le_n_links, sizeof le_n_links);
240   memcpy (raw + sizeof le_n_links, links, n_links * sizeof *links);
241   memcpy (raw + sizeof le_n_links + n_links * sizeof *links, data, size);
242
243   return pxd_object_create_raw__ (raw, n_links, size, &id);
244 }
245
246 struct pxd_object *
247 pxd_object_ref (const struct pxd_object *const_obj)
248 {
249   struct pxd_object *obj = CONST_CAST (struct pxd_object *, const_obj);
250   obj->n_refs++;
251   return obj;
252 }
253
254 void
255 pxd_object_unref (struct pxd_object *obj)
256 {
257   if (obj != NULL && --obj->n_refs == 0)
258     {
259       hmap_delete (&object_table, &obj->hmap_node);
260       free (pxd_object_raw_data__ (obj));
261       free (obj);
262     }
263 }
264
265 void *
266 pxd_object_raw_data__ (const struct pxd_object *obj)
267 {
268   return ((uint32_t *) obj->links) - 1;
269 }
270
271 size_t
272 pxd_object_raw_size__ (const struct pxd_object *obj)
273 {
274   return sizeof (uint32_t *) + obj->n_links * sizeof *obj->links + obj->size;
275 }
276 \f
277 void
278 pxd_builder_init (struct pxd_builder *b, struct pxd *pxd)
279 {
280   b->pxd = pxd;
281
282   b->links = NULL;
283   b->n_links = b->links_allocated = 0;
284
285   b->data = NULL;
286   b->size = b->data_allocated = 0;
287 }
288
289 void
290 pxd_builder_destroy (struct pxd_builder *b)
291 {
292   if (b != NULL)
293     {
294       free (b->links);
295       b->links = NULL;
296
297       free (b->data);
298       b->data = NULL;
299     }
300 }
301
302 struct pxd_object *
303 pxd_builder_commit (struct pxd_builder *b)
304 {
305   struct pxd_object *obj;
306
307   obj = pxd_object_create (b->links, b->n_links, b->data, b->size);
308   pxd_store (b->pxd, obj);
309   pxd_builder_destroy (b);
310
311   return obj;
312 }
313
314 void *
315 pxd_builder_put_uninit (struct pxd_builder *b, size_t n)
316 {
317   void *p;
318
319   if (b->size + n > b->data_allocated)
320     {
321       b->data_allocated = MAX (b->size + n + 64, b->data_allocated * 2);
322       b->data = xrealloc (b->data, b->data_allocated);
323     }
324
325   p = &b->data[b->size];
326   b->size += n;
327   return p;
328 }
329
330 void
331 pxd_builder_put (struct pxd_builder *b, const void *p, size_t n)
332 {
333   memcpy (pxd_builder_put_uninit (b, n), p, n);
334 }
335
336 void
337 pxd_builder_put_value (struct pxd_builder *b,
338                        const union value *value, int width)
339 {
340   if (width == 0)
341     pxd_builder_put_double (b, value->f);
342   else
343     {
344       const uint8_t *s = value_str (value, width);
345       if (width < sizeof (struct pxd_id))
346         pxd_builder_put (b, s, width);
347       else
348         pxd_builder_put_link (b, pxd_object_create (NULL, 0, s, width));
349     }
350 }
351
352 static void
353 pxd_builder_put_string__ (struct pxd_builder *b, const char *s, size_t length)
354 {
355   pxd_builder_put_u32 (b, length);
356   if (length < sizeof (struct pxd_id))
357     pxd_builder_put (b, s, length);
358   else
359     pxd_builder_put_link (b, pxd_object_create (NULL, 0, s, length));
360 }
361
362 void
363 pxd_builder_put_string (struct pxd_builder *b, const char *s)
364 {
365   return pxd_builder_put_string__ (b, s, strlen (s));
366 }
367
368 void
369 pxd_builder_put_interned_string (struct pxd_builder *b, const char *s)
370 {
371   return pxd_builder_put_string__ (b, s, intern_strlen (s));
372 }
373
374 void
375 pxd_builder_put_bool (struct pxd_builder *b, bool x)
376 {
377   pxd_builder_put_u8 (b, x != 0);
378 }
379
380 void
381 pxd_builder_put_u8 (struct pxd_builder *b, unsigned char x)
382 {
383   pxd_builder_put (b, &x, 1);
384 }
385
386 void
387 pxd_builder_put_u16 (struct pxd_builder *b, unsigned short int x)
388 {
389   uint16_t le_x = cpu_to_le16 (x);
390   pxd_builder_put (b, &le_x, sizeof (le_x));
391 }
392
393 void pxd_builder_put_u32 (struct pxd_builder *b, unsigned int x)
394 {
395   uint32_t le_x = cpu_to_le32 (x);
396   pxd_builder_put (b, &le_x, sizeof (le_x));
397 }
398
399 void
400 pxd_builder_put_u64 (struct pxd_builder *b, unsigned long long int x)
401 {
402   uint64_t le_x = cpu_to_le64 (x);
403   pxd_builder_put (b, &le_x, sizeof (le_x));
404 }
405
406 void
407 pxd_builder_put_s8 (struct pxd_builder *b, signed char x)
408 {
409   pxd_builder_put (b, &x, 1);
410 }
411
412 void
413 pxd_builder_put_s16 (struct pxd_builder *b, short int x)
414 {
415   uint16_t le_x = cpu_to_le16 (x);
416   pxd_builder_put (b, &le_x, sizeof (le_x));
417 }
418
419 void pxd_builder_put_s32 (struct pxd_builder *b, int x)
420 {
421   uint32_t le_x = cpu_to_le32 (x);
422   pxd_builder_put (b, &le_x, sizeof (le_x));
423 }
424
425 void
426 pxd_builder_put_s64 (struct pxd_builder *b, long long int x)
427 {
428   uint64_t le_x = cpu_to_le64 (x);
429   pxd_builder_put (b, &le_x, sizeof (le_x));
430 }
431
432 void
433 pxd_builder_put_size_t (struct pxd_builder *b, size_t x)
434 {
435   pxd_builder_put_u64 (b, x);
436 }
437
438 void
439 pxd_builder_put_casenumber (struct pxd_builder *b, casenumber x)
440 {
441   pxd_builder_put_u64 (b, x);
442 }
443
444 void
445 pxd_builder_put_double (struct pxd_builder *b, double x)
446 {
447   pxd_builder_put_u64 (b, double_to_ieee64le (x));
448 }
449
450 void
451 pxd_builder_put_link (struct pxd_builder *b, struct pxd_object *obj)
452 {
453   if (b->n_links >= b->links_allocated)
454     b->links = x2nrealloc (b->links, &b->links_allocated, sizeof *b->links);
455
456   b->links[b->n_links++] = obj->id;
457 }
458 \f
459 void
460 pxd_parser_init (struct pxd_parser *p,
461                  struct pxd_object *obj, const struct pxd *pxd)
462 {
463   p->obj = obj;
464   p->pxd = pxd;
465   p->offset = 0;
466   p->link = 0;
467 }
468
469 void
470 pxd_parser_destroy (struct pxd_parser *p)
471 {
472   if (p != NULL)
473     pxd_object_unref (p->obj);
474 }
475
476
477 const void *
478 pxd_parser_get (struct pxd_parser *p, size_t n)
479 {
480   const void *data;
481
482   assert (p->offset + n <= p->obj->size);
483
484   data = &p->obj->data[p->offset];
485   p->offset += n;
486   return data;
487 }
488
489 void
490 pxd_parser_get_copy (struct pxd_parser *p, void *dst, size_t n)
491 {
492   memcpy (dst, pxd_parser_get (p, n), n);
493 }
494
495 void
496 pxd_parser_get_value (struct pxd_parser *p, union value *value, int width)
497 {
498   value_init (value, width);
499   if (width == 0)
500     value->f = ieee64le_to_double (pxd_parser_get_u64 (p));
501   else
502     {
503       uint8_t *s = value_str_rw (value, width);
504       if (width < sizeof (struct pxd_id))
505         pxd_parser_get_copy (p, s, width);
506       else
507         {
508           struct pxd_object *link = pxd_parser_get_link (p);
509           assert (link->size == width);
510           memcpy (s, link->data, width);
511           pxd_object_unref (link);
512         }
513     }
514 }
515
516 char *
517 pxd_parser_get_string (struct pxd_parser *p)
518 {
519   uint32_t length = pxd_parser_get_u32 (p);
520   if (length < sizeof (struct pxd_id))
521     return xmemdup0 (pxd_parser_get (p, length), length);
522   else
523     {
524       struct pxd_object *link;
525       char *s;
526
527       link = pxd_parser_get_link (p);
528       assert (link->size == length);
529       s = xmemdup0 (link->data, link->size);
530       pxd_object_unref (link);
531
532       return s;
533     }
534 }
535
536 const char *
537 pxd_parser_get_interned_string (struct pxd_parser *p)
538 {
539   uint32_t length = pxd_parser_get_u32 (p);
540   if (length < sizeof (struct pxd_id))
541     return intern_buffer (pxd_parser_get (p, length), length);
542   else
543     {
544       struct pxd_object *link;
545       const char *s;
546
547       link = pxd_parser_get_link (p);
548       assert (link->size == length);
549       s = intern_buffer ((const void *) link->data, link->size);
550       pxd_object_unref (link);
551
552       return s;
553     }
554 }
555
556 bool
557 pxd_parser_get_bool (struct pxd_parser *p)
558 {
559   return pxd_parser_get_u8 (p) != 0;
560 }
561
562 unsigned char
563 pxd_parser_get_u8 (struct pxd_parser *p)
564 {
565   uint8_t x;
566   pxd_parser_get_copy (p, &x, 1);
567   return x;
568 }
569
570 unsigned short int
571 pxd_parser_get_u16 (struct pxd_parser *p)
572 {
573   uint16_t x;
574   pxd_parser_get_copy (p, &x, sizeof x);
575   return le16_to_cpu (x);
576 }
577
578 unsigned int
579 pxd_parser_get_u32 (struct pxd_parser *p)
580 {
581   uint32_t x;
582   pxd_parser_get_copy (p, &x, sizeof x);
583   return le32_to_cpu (x);
584 }
585
586 unsigned long long int
587 pxd_parser_get_u64 (struct pxd_parser *p)
588 {
589   uint64_t x;
590   pxd_parser_get_copy (p, &x, sizeof x);
591   return le64_to_cpu (x);
592 }
593
594 signed char
595 pxd_parser_get_s8 (struct pxd_parser *p)
596 {
597   int8_t x;
598   pxd_parser_get_copy (p, &x, 1);
599   return x;
600 }
601
602 short int
603 pxd_parser_get_s16 (struct pxd_parser *p)
604 {
605   uint16_t x;
606   pxd_parser_get_copy (p, &x, sizeof x);
607   return le16_to_cpu (x);
608 }
609
610 int
611 pxd_parser_get_s32 (struct pxd_parser *p)
612 {
613   uint32_t x;
614   pxd_parser_get_copy (p, &x, sizeof x);
615   return le32_to_cpu (x);
616 }
617
618 long long int
619 pxd_parser_get_s64 (struct pxd_parser *p)
620 {
621   uint64_t x;
622   pxd_parser_get_copy (p, &x, sizeof x);
623   return le64_to_cpu (x);
624 }
625
626 size_t
627 pxd_parser_get_size_t (struct pxd_parser *p)
628 {
629   uint64_t x = pxd_parser_get_u64 (p);
630   assert (x <= SIZE_MAX);
631   return x;
632 }
633
634 casenumber
635 pxd_parser_get_casenumber (struct pxd_parser *p)
636 {
637   uint64_t x = pxd_parser_get_u64 (p);
638   assert (x <= CASENUMBER_MAX);
639   return x;
640 }
641
642 double
643 pxd_parser_get_double (struct pxd_parser *p)
644 {
645   return ieee64le_to_double (pxd_parser_get_u64 (p));
646 }
647
648 struct pxd_object *
649 pxd_parser_get_link (struct pxd_parser *p)
650 {
651   assert (p->link < p->obj->n_links);
652
653   return pxd_fetch (p->pxd, &p->obj->links[p->link++]);
654 }
655 \f
656 void
657 pxd_array_builder_init (struct pxd_array_builder *ab, struct pxd *pxd)
658 {
659   pxd_builder_init (&ab->b, pxd);
660 }
661
662 void
663 pxd_array_builder_destroy (struct pxd_array_builder *ab)
664 {
665   pxd_builder_destroy (&ab->b);
666 }
667
668 struct pxd_object *
669 pxd_array_builder_commit (struct pxd_array_builder *ab)
670 {
671   return pxd_builder_commit (&ab->b);
672 }
673
674 void
675 pxd_array_builder_add (struct pxd_array_builder *ab, struct pxd_object *obj)
676 {
677   pxd_builder_put_link (&ab->b, obj);
678 }
679 \f
680 void
681 pxd_array_init (struct pxd_array *array, struct pxd_object *obj,
682                 const struct pxd *pxd)
683 {
684   array->pxd = pxd;
685   array->obj = obj;
686 }
687
688 void
689 pxd_array_destroy (struct pxd_array *array)
690 {
691   pxd_object_unref (array->obj);
692 }