X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flibpspp%2Fpxd.c;fp=src%2Flibpspp%2Fpxd.c;h=00bc8882d83b379628bedc46fc86abdadef5086c;hb=52c54183e360053b1845e46cb96cd44a0cf96040;hp=0000000000000000000000000000000000000000;hpb=2ca3267c1110bbff675c560b19d02defb96ee2f9;p=pspp diff --git a/src/libpspp/pxd.c b/src/libpspp/pxd.c new file mode 100644 index 0000000000..00bc8882d8 --- /dev/null +++ b/src/libpspp/pxd.c @@ -0,0 +1,692 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2010, 2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "libpspp/pxd.h" + +#include +#include + +#include "data/value.h" +#include "libpspp/cast.h" +#include "libpspp/compiler.h" +#include "libpspp/float-format.h" +#include "libpspp/integer-format.h" +#include "libpspp/intern.h" + +#include "gl/error.h" +#include "gl/minmax.h" +#include "gl/sha1.h" +#include "gl/xalloc.h" +#include "gl/xmemdup0.h" + +struct pxd + { + GDBM_FILE dbf; + }; + +static struct pxd_object *pxd_object_lookup__ (const struct pxd_id *id); +static struct pxd_object *pxd_object_create_raw__ (char *raw, size_t n_links, + size_t size, + const struct pxd_id *); + +static datum +make_datum (const void *data, size_t size) +{ + datum d; + + d.dptr = CONST_CAST (void *, data); + d.dsize = size; + + return d; +} + +static datum +string_datum (const char *s) +{ + return make_datum (s, strlen (s)); +} + +static datum +id_datum (const struct pxd_id *id) +{ + return make_datum (id, sizeof *id); +} + +static datum +root_key (void) +{ + return string_datum ("root"); +} + +void +pxd_get_root (const struct pxd *pxd, struct pxd_id *id) +{ + datum content; + + content = gdbm_fetch (pxd->dbf, root_key ()); + if (content.dptr != NULL && content.dsize == sizeof *id) + memcpy (id, content.dptr, sizeof *id); + else + memset (id, 0, sizeof *id); + free (content.dptr); +} + +struct pxd * +pxd_open (const char *name, bool may_create) +{ + struct pxd *pxd; + GDBM_FILE dbf; + + dbf = gdbm_open (CONST_CAST (char *, name), 4096, + may_create ? GDBM_WRCREAT : GDBM_WRITER, 0777, NULL); + if (dbf == NULL) + { + error (0, 0, "%s: open failed (%s)", name, gdbm_strerror (gdbm_errno)); + return NULL; + } + + pxd = xmalloc (sizeof *pxd); + pxd->dbf = dbf; + return pxd; +} + +void +pxd_close (struct pxd *pxd) +{ + if (pxd != NULL) + { + gdbm_close (pxd->dbf); + free (pxd); + } +} + +struct pxd_object * +pxd_fetch (const struct pxd *pxd, const struct pxd_id *id) +{ + struct pxd_object *obj; + datum content; + uint32_t n_links; + size_t header_size; + + obj = pxd_object_lookup__ (id); + if (obj != NULL) + return obj; + + content = gdbm_fetch (pxd->dbf, id_datum (id)); + if (content.dptr == NULL) + error (1, 0, PXD_ID_FMT": not found in database", PXD_ID_ARGS (id)); + + if (content.dsize < sizeof n_links) + error (1, 0, PXD_ID_FMT": size %d is less than minimum size 4", + PXD_ID_ARGS(id), content.dsize); + + n_links = le32_to_cpu (*(uint32_t *) content.dptr); + header_size = 4 + n_links * sizeof (struct pxd_id); + if (header_size > content.dsize) + error (1, 0, PXD_ID_FMT": size %d is less than minimum size %zu for " + "object with %"PRIu32" links", + PXD_ID_ARGS (id), content.dsize, header_size, n_links); + + return pxd_object_create_raw__ (content.dptr, n_links, + content.dsize - header_size, id); +} + +void +pxd_store (struct pxd *pxd, const struct pxd_object *object) +{ + datum content; + + content = make_datum (pxd_object_raw_data__ (object), + pxd_object_raw_size__ (object)); + if (gdbm_store (pxd->dbf, id_datum (&object->id), content, GDBM_INSERT) < 0) + error (1, 0, PXD_ID_FMT": storing object failed", + PXD_ID_ARGS (&object->id)); +} + +bool +pxd_swap_root (struct pxd *pxd, const struct pxd_id *old_root, + struct pxd_object *new_root) +{ + struct pxd_id cur_root; + + pxd_get_root (pxd, &cur_root); + if (pxd_id_equals (&cur_root, old_root)) + { + if (gdbm_store (pxd->dbf, root_key (), id_datum (&new_root->id), + GDBM_INSERT) < 0) + error (1, 0, "storing root object failed"); + pxd_object_unref (new_root); + return true; + } + else + return false; +} + +static struct hmap object_table = HMAP_INITIALIZER (object_table); + +static struct pxd_object * +pxd_object_lookup__ (const struct pxd_id *id) +{ + const struct pxd_object *obj; + + HMAP_FOR_EACH_IN_BUCKET (obj, struct pxd_object, hmap_node, + pxd_id_hash (id), &object_table) + if (pxd_id_equals (id, &obj->id)) + return pxd_object_ref (obj); + + return NULL; +} + +static struct pxd_object * +pxd_object_create_raw__ (char *raw, size_t n_links, size_t size, + const struct pxd_id *id) +{ + struct pxd_object *obj; + + obj = xmalloc (sizeof *obj); + hmap_insert (&object_table, &obj->hmap_node, pxd_id_hash (id)); + obj->n_refs = 1; + obj->id = *id; + obj->n_links = n_links; + obj->size = size; + obj->links = (struct pxd_id *) (raw + sizeof (uint32_t)); + obj->data = (uint8_t *) (obj->links + obj->n_links); + + return obj; +} + +struct pxd_object * +pxd_object_create (const struct pxd_id links[], size_t n_links, + const void *data, size_t size) +{ + struct pxd_object *obj; + struct sha1_ctx sha; + uint32_t le_n_links; + struct pxd_id id; + char *raw; + + /* Hash the raw form of the object. */ + sha1_init_ctx (&sha); + le_n_links = cpu_to_le32 (n_links); + sha1_process_bytes (&le_n_links, sizeof le_n_links, &sha); + sha1_process_bytes (links, n_links * sizeof *links, &sha); + sha1_process_bytes (data, size, &sha); + sha1_finish_ctx (&sha, &id); + + /* Reuse an existing copy if there is one. */ + obj = pxd_object_lookup__ (&id); + if (obj != NULL) + return obj; + + /* Build a new object. */ + raw = xmalloc (sizeof le_n_links + n_links * sizeof *links + + size); + memcpy (raw, &le_n_links, sizeof le_n_links); + memcpy (raw + sizeof le_n_links, links, n_links * sizeof *links); + memcpy (raw + sizeof le_n_links + n_links * sizeof *links, data, size); + + return pxd_object_create_raw__ (raw, n_links, size, &id); +} + +struct pxd_object * +pxd_object_ref (const struct pxd_object *const_obj) +{ + struct pxd_object *obj = CONST_CAST (struct pxd_object *, const_obj); + obj->n_refs++; + return obj; +} + +void +pxd_object_unref (struct pxd_object *obj) +{ + if (obj != NULL && --obj->n_refs == 0) + { + hmap_delete (&object_table, &obj->hmap_node); + free (pxd_object_raw_data__ (obj)); + free (obj); + } +} + +void * +pxd_object_raw_data__ (const struct pxd_object *obj) +{ + return ((uint32_t *) obj->links) - 1; +} + +size_t +pxd_object_raw_size__ (const struct pxd_object *obj) +{ + return sizeof (uint32_t *) + obj->n_links * sizeof *obj->links + obj->size; +} + +void +pxd_builder_init (struct pxd_builder *b, struct pxd *pxd) +{ + b->pxd = pxd; + + b->links = NULL; + b->n_links = b->links_allocated = 0; + + b->data = NULL; + b->size = b->data_allocated = 0; +} + +void +pxd_builder_destroy (struct pxd_builder *b) +{ + if (b != NULL) + { + free (b->links); + b->links = NULL; + + free (b->data); + b->data = NULL; + } +} + +struct pxd_object * +pxd_builder_commit (struct pxd_builder *b) +{ + struct pxd_object *obj; + + obj = pxd_object_create (b->links, b->n_links, b->data, b->size); + pxd_store (b->pxd, obj); + pxd_builder_destroy (b); + + return obj; +} + +void * +pxd_builder_put_uninit (struct pxd_builder *b, size_t n) +{ + void *p; + + if (b->size + n > b->data_allocated) + { + b->data_allocated = MAX (b->size + n + 64, b->data_allocated * 2); + b->data = xrealloc (b->data, b->data_allocated); + } + + p = &b->data[b->size]; + b->size += n; + return p; +} + +void +pxd_builder_put (struct pxd_builder *b, const void *p, size_t n) +{ + memcpy (pxd_builder_put_uninit (b, n), p, n); +} + +void +pxd_builder_put_value (struct pxd_builder *b, + const union value *value, int width) +{ + if (width == 0) + pxd_builder_put_double (b, value->f); + else + { + const uint8_t *s = value_str (value, width); + if (width < sizeof (struct pxd_id)) + pxd_builder_put (b, s, width); + else + pxd_builder_put_link (b, pxd_object_create (NULL, 0, s, width)); + } +} + +static void +pxd_builder_put_string__ (struct pxd_builder *b, const char *s, size_t length) +{ + pxd_builder_put_u32 (b, length); + if (length < sizeof (struct pxd_id)) + pxd_builder_put (b, s, length); + else + pxd_builder_put_link (b, pxd_object_create (NULL, 0, s, length)); +} + +void +pxd_builder_put_string (struct pxd_builder *b, const char *s) +{ + return pxd_builder_put_string__ (b, s, strlen (s)); +} + +void +pxd_builder_put_interned_string (struct pxd_builder *b, const char *s) +{ + return pxd_builder_put_string__ (b, s, intern_strlen (s)); +} + +void +pxd_builder_put_bool (struct pxd_builder *b, bool x) +{ + pxd_builder_put_u8 (b, x != 0); +} + +void +pxd_builder_put_u8 (struct pxd_builder *b, unsigned char x) +{ + pxd_builder_put (b, &x, 1); +} + +void +pxd_builder_put_u16 (struct pxd_builder *b, unsigned short int x) +{ + uint16_t le_x = cpu_to_le16 (x); + pxd_builder_put (b, &le_x, sizeof (le_x)); +} + +void pxd_builder_put_u32 (struct pxd_builder *b, unsigned int x) +{ + uint32_t le_x = cpu_to_le32 (x); + pxd_builder_put (b, &le_x, sizeof (le_x)); +} + +void +pxd_builder_put_u64 (struct pxd_builder *b, unsigned long long int x) +{ + uint64_t le_x = cpu_to_le64 (x); + pxd_builder_put (b, &le_x, sizeof (le_x)); +} + +void +pxd_builder_put_s8 (struct pxd_builder *b, signed char x) +{ + pxd_builder_put (b, &x, 1); +} + +void +pxd_builder_put_s16 (struct pxd_builder *b, short int x) +{ + uint16_t le_x = cpu_to_le16 (x); + pxd_builder_put (b, &le_x, sizeof (le_x)); +} + +void pxd_builder_put_s32 (struct pxd_builder *b, int x) +{ + uint32_t le_x = cpu_to_le32 (x); + pxd_builder_put (b, &le_x, sizeof (le_x)); +} + +void +pxd_builder_put_s64 (struct pxd_builder *b, long long int x) +{ + uint64_t le_x = cpu_to_le64 (x); + pxd_builder_put (b, &le_x, sizeof (le_x)); +} + +void +pxd_builder_put_size_t (struct pxd_builder *b, size_t x) +{ + pxd_builder_put_u64 (b, x); +} + +void +pxd_builder_put_casenumber (struct pxd_builder *b, casenumber x) +{ + pxd_builder_put_u64 (b, x); +} + +void +pxd_builder_put_double (struct pxd_builder *b, double x) +{ + pxd_builder_put_u64 (b, double_to_ieee64le (x)); +} + +void +pxd_builder_put_link (struct pxd_builder *b, struct pxd_object *obj) +{ + if (b->n_links >= b->links_allocated) + b->links = x2nrealloc (b->links, &b->links_allocated, sizeof *b->links); + + b->links[b->n_links++] = obj->id; +} + +void +pxd_parser_init (struct pxd_parser *p, + struct pxd_object *obj, const struct pxd *pxd) +{ + p->obj = obj; + p->pxd = pxd; + p->offset = 0; + p->link = 0; +} + +void +pxd_parser_destroy (struct pxd_parser *p) +{ + if (p != NULL) + pxd_object_unref (p->obj); +} + + +const void * +pxd_parser_get (struct pxd_parser *p, size_t n) +{ + const void *data; + + assert (p->offset + n <= p->obj->size); + + data = &p->obj->data[p->offset]; + p->offset += n; + return data; +} + +void +pxd_parser_get_copy (struct pxd_parser *p, void *dst, size_t n) +{ + memcpy (dst, pxd_parser_get (p, n), n); +} + +void +pxd_parser_get_value (struct pxd_parser *p, union value *value, int width) +{ + value_init (value, width); + if (width == 0) + value->f = ieee64le_to_double (pxd_parser_get_u64 (p)); + else + { + uint8_t *s = value_str_rw (value, width); + if (width < sizeof (struct pxd_id)) + pxd_parser_get_copy (p, s, width); + else + { + struct pxd_object *link = pxd_parser_get_link (p); + assert (link->size == width); + memcpy (s, link->data, width); + pxd_object_unref (link); + } + } +} + +char * +pxd_parser_get_string (struct pxd_parser *p) +{ + uint32_t length = pxd_parser_get_u32 (p); + if (length < sizeof (struct pxd_id)) + return xmemdup0 (pxd_parser_get (p, length), length); + else + { + struct pxd_object *link; + char *s; + + link = pxd_parser_get_link (p); + assert (link->size == length); + s = xmemdup0 (link->data, link->size); + pxd_object_unref (link); + + return s; + } +} + +const char * +pxd_parser_get_interned_string (struct pxd_parser *p) +{ + uint32_t length = pxd_parser_get_u32 (p); + if (length < sizeof (struct pxd_id)) + return intern_buffer (pxd_parser_get (p, length), length); + else + { + struct pxd_object *link; + const char *s; + + link = pxd_parser_get_link (p); + assert (link->size == length); + s = intern_buffer ((const void *) link->data, link->size); + pxd_object_unref (link); + + return s; + } +} + +bool +pxd_parser_get_bool (struct pxd_parser *p) +{ + return pxd_parser_get_u8 (p) != 0; +} + +unsigned char +pxd_parser_get_u8 (struct pxd_parser *p) +{ + uint8_t x; + pxd_parser_get_copy (p, &x, 1); + return x; +} + +unsigned short int +pxd_parser_get_u16 (struct pxd_parser *p) +{ + uint16_t x; + pxd_parser_get_copy (p, &x, sizeof x); + return le16_to_cpu (x); +} + +unsigned int +pxd_parser_get_u32 (struct pxd_parser *p) +{ + uint32_t x; + pxd_parser_get_copy (p, &x, sizeof x); + return le32_to_cpu (x); +} + +unsigned long long int +pxd_parser_get_u64 (struct pxd_parser *p) +{ + uint64_t x; + pxd_parser_get_copy (p, &x, sizeof x); + return le64_to_cpu (x); +} + +signed char +pxd_parser_get_s8 (struct pxd_parser *p) +{ + int8_t x; + pxd_parser_get_copy (p, &x, 1); + return x; +} + +short int +pxd_parser_get_s16 (struct pxd_parser *p) +{ + uint16_t x; + pxd_parser_get_copy (p, &x, sizeof x); + return le16_to_cpu (x); +} + +int +pxd_parser_get_s32 (struct pxd_parser *p) +{ + uint32_t x; + pxd_parser_get_copy (p, &x, sizeof x); + return le32_to_cpu (x); +} + +long long int +pxd_parser_get_s64 (struct pxd_parser *p) +{ + uint64_t x; + pxd_parser_get_copy (p, &x, sizeof x); + return le64_to_cpu (x); +} + +size_t +pxd_parser_get_size_t (struct pxd_parser *p) +{ + uint64_t x = pxd_parser_get_u64 (p); + assert (x <= SIZE_MAX); + return x; +} + +casenumber +pxd_parser_get_casenumber (struct pxd_parser *p) +{ + uint64_t x = pxd_parser_get_u64 (p); + assert (x <= CASENUMBER_MAX); + return x; +} + +double +pxd_parser_get_double (struct pxd_parser *p) +{ + return ieee64le_to_double (pxd_parser_get_u64 (p)); +} + +struct pxd_object * +pxd_parser_get_link (struct pxd_parser *p) +{ + assert (p->link < p->obj->n_links); + + return pxd_fetch (p->pxd, &p->obj->links[p->link++]); +} + +void +pxd_array_builder_init (struct pxd_array_builder *ab, struct pxd *pxd) +{ + pxd_builder_init (&ab->b, pxd); +} + +void +pxd_array_builder_destroy (struct pxd_array_builder *ab) +{ + pxd_builder_destroy (&ab->b); +} + +struct pxd_object * +pxd_array_builder_commit (struct pxd_array_builder *ab) +{ + return pxd_builder_commit (&ab->b); +} + +void +pxd_array_builder_add (struct pxd_array_builder *ab, struct pxd_object *obj) +{ + pxd_builder_put_link (&ab->b, obj); +} + +void +pxd_array_init (struct pxd_array *array, struct pxd_object *obj, + const struct pxd *pxd) +{ + array->pxd = pxd; + array->obj = obj; +} + +void +pxd_array_destroy (struct pxd_array *array) +{ + pxd_object_unref (array->obj); +}