1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2010, 2013 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "libpspp/pxd.h"
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"
32 #include "gl/minmax.h"
34 #include "gl/xalloc.h"
35 #include "gl/xmemdup0.h"
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,
45 const struct pxd_id *);
48 make_datum (const void *data, size_t size)
52 d.dptr = CONST_CAST (void *, data);
59 string_datum (const char *s)
61 return make_datum (s, strlen (s));
65 id_datum (const struct pxd_id *id)
67 return make_datum (id, sizeof *id);
73 return string_datum ("root");
77 pxd_get_root (const struct pxd *pxd, struct pxd_id *id)
81 content = gdbm_fetch (pxd->dbf, root_key ());
82 if (content.dptr != NULL && content.dsize == sizeof *id)
83 memcpy (id, content.dptr, sizeof *id);
85 memset (id, 0, sizeof *id);
90 pxd_open (const char *name, bool may_create)
95 dbf = gdbm_open (CONST_CAST (char *, name), 4096,
96 may_create ? GDBM_WRCREAT : GDBM_WRITER, 0777, NULL);
99 error (0, 0, "%s: open failed (%s)", name, gdbm_strerror (gdbm_errno));
103 pxd = xmalloc (sizeof *pxd);
109 pxd_close (struct pxd *pxd)
113 gdbm_close (pxd->dbf);
119 pxd_fetch (const struct pxd *pxd, const struct pxd_id *id)
121 struct pxd_object *obj;
126 obj = pxd_object_lookup__ (id);
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));
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);
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);
145 return pxd_object_create_raw__ (content.dptr, n_links,
146 content.dsize - header_size, id);
150 pxd_store (struct pxd *pxd, const struct pxd_object *object)
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));
162 pxd_swap_root (struct pxd *pxd, const struct pxd_id *old_root,
163 struct pxd_object *new_root)
165 struct pxd_id cur_root;
167 pxd_get_root (pxd, &cur_root);
168 if (pxd_id_equals (&cur_root, old_root))
170 if (gdbm_store (pxd->dbf, root_key (), id_datum (&new_root->id),
172 error (1, 0, "storing root object failed");
173 pxd_object_unref (new_root);
180 static struct hmap object_table = HMAP_INITIALIZER (object_table);
182 static struct pxd_object *
183 pxd_object_lookup__ (const struct pxd_id *id)
185 const struct pxd_object *obj;
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);
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)
199 struct pxd_object *obj;
201 obj = xmalloc (sizeof *obj);
202 hmap_insert (&object_table, &obj->hmap_node, pxd_id_hash (id));
205 obj->n_links = n_links;
207 obj->links = (struct pxd_id *) (raw + sizeof (uint32_t));
208 obj->data = (uint8_t *) (obj->links + obj->n_links);
214 pxd_object_create (const struct pxd_id links[], size_t n_links,
215 const void *data, size_t size)
217 struct pxd_object *obj;
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);
231 /* Reuse an existing copy if there is one. */
232 obj = pxd_object_lookup__ (&id);
236 /* Build a new object. */
237 raw = xmalloc (sizeof le_n_links + n_links * sizeof *links +
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);
243 return pxd_object_create_raw__ (raw, n_links, size, &id);
247 pxd_object_ref (const struct pxd_object *const_obj)
249 struct pxd_object *obj = CONST_CAST (struct pxd_object *, const_obj);
255 pxd_object_unref (struct pxd_object *obj)
257 if (obj != NULL && --obj->n_refs == 0)
259 hmap_delete (&object_table, &obj->hmap_node);
260 free (pxd_object_raw_data__ (obj));
266 pxd_object_raw_data__ (const struct pxd_object *obj)
268 return ((uint32_t *) obj->links) - 1;
272 pxd_object_raw_size__ (const struct pxd_object *obj)
274 return sizeof (uint32_t *) + obj->n_links * sizeof *obj->links + obj->size;
278 pxd_builder_init (struct pxd_builder *b, struct pxd *pxd)
283 b->n_links = b->links_allocated = 0;
286 b->size = b->data_allocated = 0;
290 pxd_builder_destroy (struct pxd_builder *b)
303 pxd_builder_commit (struct pxd_builder *b)
305 struct pxd_object *obj;
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);
315 pxd_builder_put_uninit (struct pxd_builder *b, size_t n)
319 if (b->size + n > b->data_allocated)
321 b->data_allocated = MAX (b->size + n + 64, b->data_allocated * 2);
322 b->data = xrealloc (b->data, b->data_allocated);
325 p = &b->data[b->size];
331 pxd_builder_put (struct pxd_builder *b, const void *p, size_t n)
333 memcpy (pxd_builder_put_uninit (b, n), p, n);
337 pxd_builder_put_value (struct pxd_builder *b,
338 const union value *value, int width)
341 pxd_builder_put_double (b, value->f);
344 const uint8_t *s = value_str (value, width);
345 if (width < sizeof (struct pxd_id))
346 pxd_builder_put (b, s, width);
348 pxd_builder_put_link (b, pxd_object_create (NULL, 0, s, width));
353 pxd_builder_put_string__ (struct pxd_builder *b, const char *s, size_t length)
355 pxd_builder_put_u32 (b, length);
356 if (length < sizeof (struct pxd_id))
357 pxd_builder_put (b, s, length);
359 pxd_builder_put_link (b, pxd_object_create (NULL, 0, s, length));
363 pxd_builder_put_string (struct pxd_builder *b, const char *s)
365 return pxd_builder_put_string__ (b, s, strlen (s));
369 pxd_builder_put_interned_string (struct pxd_builder *b, const char *s)
371 return pxd_builder_put_string__ (b, s, intern_strlen (s));
375 pxd_builder_put_bool (struct pxd_builder *b, bool x)
377 pxd_builder_put_u8 (b, x != 0);
381 pxd_builder_put_u8 (struct pxd_builder *b, unsigned char x)
383 pxd_builder_put (b, &x, 1);
387 pxd_builder_put_u16 (struct pxd_builder *b, unsigned short int x)
389 uint16_t le_x = cpu_to_le16 (x);
390 pxd_builder_put (b, &le_x, sizeof (le_x));
393 void pxd_builder_put_u32 (struct pxd_builder *b, unsigned int x)
395 uint32_t le_x = cpu_to_le32 (x);
396 pxd_builder_put (b, &le_x, sizeof (le_x));
400 pxd_builder_put_u64 (struct pxd_builder *b, unsigned long long int x)
402 uint64_t le_x = cpu_to_le64 (x);
403 pxd_builder_put (b, &le_x, sizeof (le_x));
407 pxd_builder_put_s8 (struct pxd_builder *b, signed char x)
409 pxd_builder_put (b, &x, 1);
413 pxd_builder_put_s16 (struct pxd_builder *b, short int x)
415 uint16_t le_x = cpu_to_le16 (x);
416 pxd_builder_put (b, &le_x, sizeof (le_x));
419 void pxd_builder_put_s32 (struct pxd_builder *b, int x)
421 uint32_t le_x = cpu_to_le32 (x);
422 pxd_builder_put (b, &le_x, sizeof (le_x));
426 pxd_builder_put_s64 (struct pxd_builder *b, long long int x)
428 uint64_t le_x = cpu_to_le64 (x);
429 pxd_builder_put (b, &le_x, sizeof (le_x));
433 pxd_builder_put_size_t (struct pxd_builder *b, size_t x)
435 pxd_builder_put_u64 (b, x);
439 pxd_builder_put_casenumber (struct pxd_builder *b, casenumber x)
441 pxd_builder_put_u64 (b, x);
445 pxd_builder_put_double (struct pxd_builder *b, double x)
447 pxd_builder_put_u64 (b, double_to_ieee64le (x));
451 pxd_builder_put_link (struct pxd_builder *b, struct pxd_object *obj)
453 if (b->n_links >= b->links_allocated)
454 b->links = x2nrealloc (b->links, &b->links_allocated, sizeof *b->links);
456 b->links[b->n_links++] = obj->id;
460 pxd_parser_init (struct pxd_parser *p,
461 struct pxd_object *obj, const struct pxd *pxd)
470 pxd_parser_destroy (struct pxd_parser *p)
473 pxd_object_unref (p->obj);
478 pxd_parser_get (struct pxd_parser *p, size_t n)
482 assert (p->offset + n <= p->obj->size);
484 data = &p->obj->data[p->offset];
490 pxd_parser_get_copy (struct pxd_parser *p, void *dst, size_t n)
492 memcpy (dst, pxd_parser_get (p, n), n);
496 pxd_parser_get_value (struct pxd_parser *p, union value *value, int width)
498 value_init (value, width);
500 value->f = ieee64le_to_double (pxd_parser_get_u64 (p));
503 uint8_t *s = value_str_rw (value, width);
504 if (width < sizeof (struct pxd_id))
505 pxd_parser_get_copy (p, s, width);
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);
517 pxd_parser_get_string (struct pxd_parser *p)
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);
524 struct pxd_object *link;
527 link = pxd_parser_get_link (p);
528 assert (link->size == length);
529 s = xmemdup0 (link->data, link->size);
530 pxd_object_unref (link);
537 pxd_parser_get_interned_string (struct pxd_parser *p)
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);
544 struct pxd_object *link;
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);
557 pxd_parser_get_bool (struct pxd_parser *p)
559 return pxd_parser_get_u8 (p) != 0;
563 pxd_parser_get_u8 (struct pxd_parser *p)
566 pxd_parser_get_copy (p, &x, 1);
571 pxd_parser_get_u16 (struct pxd_parser *p)
574 pxd_parser_get_copy (p, &x, sizeof x);
575 return le16_to_cpu (x);
579 pxd_parser_get_u32 (struct pxd_parser *p)
582 pxd_parser_get_copy (p, &x, sizeof x);
583 return le32_to_cpu (x);
586 unsigned long long int
587 pxd_parser_get_u64 (struct pxd_parser *p)
590 pxd_parser_get_copy (p, &x, sizeof x);
591 return le64_to_cpu (x);
595 pxd_parser_get_s8 (struct pxd_parser *p)
598 pxd_parser_get_copy (p, &x, 1);
603 pxd_parser_get_s16 (struct pxd_parser *p)
606 pxd_parser_get_copy (p, &x, sizeof x);
607 return le16_to_cpu (x);
611 pxd_parser_get_s32 (struct pxd_parser *p)
614 pxd_parser_get_copy (p, &x, sizeof x);
615 return le32_to_cpu (x);
619 pxd_parser_get_s64 (struct pxd_parser *p)
622 pxd_parser_get_copy (p, &x, sizeof x);
623 return le64_to_cpu (x);
627 pxd_parser_get_size_t (struct pxd_parser *p)
629 uint64_t x = pxd_parser_get_u64 (p);
630 assert (x <= SIZE_MAX);
635 pxd_parser_get_casenumber (struct pxd_parser *p)
637 uint64_t x = pxd_parser_get_u64 (p);
638 assert (x <= CASENUMBER_MAX);
643 pxd_parser_get_double (struct pxd_parser *p)
645 return ieee64le_to_double (pxd_parser_get_u64 (p));
649 pxd_parser_get_link (struct pxd_parser *p)
651 assert (p->link < p->obj->n_links);
653 return pxd_fetch (p->pxd, &p->obj->links[p->link++]);
657 pxd_array_builder_init (struct pxd_array_builder *ab, struct pxd *pxd)
659 pxd_builder_init (&ab->b, pxd);
663 pxd_array_builder_destroy (struct pxd_array_builder *ab)
665 pxd_builder_destroy (&ab->b);
669 pxd_array_builder_commit (struct pxd_array_builder *ab)
671 return pxd_builder_commit (&ab->b);
675 pxd_array_builder_add (struct pxd_array_builder *ab, struct pxd_object *obj)
677 pxd_builder_put_link (&ab->b, obj);
681 pxd_array_init (struct pxd_array *array, struct pxd_object *obj,
682 const struct pxd *pxd)
689 pxd_array_destroy (struct pxd_array *array)
691 pxd_object_unref (array->obj);