X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fvalue.c;h=bbe96b19805a7060945e26bc6eb1ec4d60810583;hb=a68ebf8f11b6d584de4188ec5284d12b23f69708;hp=9fcf1cbb9b8fa0a97cb4e75544a9a6892879ab68;hpb=46dfa3ec7417bbb7452f152a6b62435006259633;p=pspp diff --git a/src/data/value.c b/src/data/value.c index 9fcf1cbb9b..bbe96b1980 100644 --- a/src/data/value.c +++ b/src/data/value.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 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 @@ -15,72 +15,75 @@ along with this program. If not, see . */ #include -#include -#include -#include -#include -#include "variable.h" +#include "data/value.h" -#include "xalloc.h" +#include "data/val-type.h" +#include "data/variable.h" +#include "libpspp/hash-functions.h" +#include "libpspp/pool.h" +#include "libpspp/str.h" +#include "gl/unistr.h" -/* Duplicate a value. - The caller is responsible for freeing the returned value. */ -union value * -value_dup (const union value *val, int width) -{ - return xmemdup (val, MAX (width, sizeof *val)); -} +#include "gl/minmax.h" +#include "gl/xalloc.h" +/* Copies the contents of string value SRC with width SRC_WIDTH + to string value DST with width DST_WIDTH. If SRC_WIDTH is + greater than DST_WIDTH, then only the first DST_WIDTH bytes + are copied; if DST_WIDTH is greater than SRC_WIDTH, then DST + is padded on the right with PAD bytes. -/* Create a value of specified width. - The caller is responsible for freeing the returned value. */ -union value * -value_create (int width) + SRC and DST must be string values; that is, SRC_WIDTH and + DST_WIDTH must both be positive. + + It is important that SRC_WIDTH and DST_WIDTH be the actual + widths with which SRC and DST were initialized. Passing, + e.g., smaller values in order to copy only a prefix of SRC or + modify only a prefix of DST will not work in every case. */ +void +value_copy_rpad (union value *dst, int dst_width, + const union value *src, int src_width, + char pad) { - return xnmalloc (value_cnt_from_width (width), sizeof (union value)); + u8_buf_copy_rpad (dst->s, dst_width, src->s, src_width, pad); } +/* Copies the contents of null-terminated string SRC to string + value DST with width DST_WIDTH. If SRC is more than DST_WIDTH + bytes long, then only the first DST_WIDTH bytes are copied; if + DST_WIDTH is greater than the length of SRC, then DST is + padded on the right with PAD bytes. -/* Compares A and B, which both have the given WIDTH, and returns - a strcmp()-type result. - Only the short string portion of longer strings are - compared. */ -int -compare_values (const void *a_, const void *b_, const void *var_) -{ - const union value *a = a_; - const union value *b = b_; - const struct variable *var = var_; - int width = var_get_width (var); - return (width == 0 - ? (a->f < b->f ? -1 : a->f > b->f) - : memcmp (a->s, b->s, MIN (MAX_SHORT_STRING, width))); -} + DST must be a string value; that is, DST_WIDTH must be + positive. -/* Create a hash of V, which has the given WIDTH. - Only the short string portion of a longer string is hashed. */ -unsigned -hash_value (const void *v_, const void *var_) + It is important that DST_WIDTH be the actual width with which + DST was initialized. Passing, e.g., a smaller value in order + to modify only a prefix of DST will not work in every case. */ +void +value_copy_str_rpad (union value *dst, int dst_width, const uint8_t *src, + char pad) { - const union value *v = v_; - const struct variable *var = var_; - int width = var_get_width (var); - return (width == 0 - ? hsh_hash_double (v->f) - : hsh_hash_bytes (v->s, width)); + value_copy_buf_rpad (dst, dst_width, src, u8_strlen (src), pad); } +/* Copies the SRC_LEN bytes at SRC to string value DST with width + DST_WIDTH. If SRC_LEN is greater than DST_WIDTH, then only + the first DST_WIDTH bytes are copied; if DST_WIDTH is greater + than SRC_LEN, then DST is padded on the right with PAD bytes. + + DST must be a string value; that is, DST_WIDTH must be + positive. -/* Copies SRC to DST, given that they both contain data of the - given WIDTH. */ + It is important that DST_WIDTH be the actual width with which + DST was initialized. Passing, e.g., a smaller value in order + to modify only a prefix of DST will not work in every case. */ void -value_copy (union value *dst, const union value *src, int width) +value_copy_buf_rpad (union value *dst, int dst_width, + const uint8_t *src, size_t src_len, char pad) { - if (width == 0) - dst->f = src->f; - else - memcpy (dst->s, src->s, width); + u8_buf_copy_rpad (dst->s, dst_width, src, src_len, pad); } /* Sets V to the system-missing value for data of the given @@ -88,10 +91,43 @@ value_copy (union value *dst, const union value *src, int width) void value_set_missing (union value *v, int width) { - if (width == 0) - v->f = SYSMIS; - else - memset (v->s, ' ', width); + if (width != -1) + { + if (width == 0) + v->f = SYSMIS; + else + memset (v->s, ' ', width); + } +} + +/* Compares A and B, which both have the given WIDTH, and returns + a strcmp()-type result. */ +int +value_compare_3way (const union value *a, const union value *b, int width) +{ + return (width == -1 ? 0 + : width == 0 ? (a->f < b->f ? -1 : a->f > b->f) + : memcmp (a->s, b->s, width)); +} + +/* Returns true if A and B, which must both have the given WIDTH, + have equal contents, false if their contents differ. */ +bool +value_equal (const union value *a, const union value *b, int width) +{ + return (width == -1 ? true + : width == 0 ? a->f == b->f + : !memcmp (a->s, b->s, width)); +} + +/* Returns a hash of the data in VALUE, which must have the given + WIDTH, folding BASIS into the hash value calculation. */ +unsigned int +value_hash (const union value *value, int width, unsigned int basis) +{ + return (width == -1 ? basis + : width == 0 ? hash_double (value->f, basis) + : hash_bytes (value->s, width, basis)); } /* Tests whether VALUE may be resized from OLD_WIDTH to @@ -102,23 +138,116 @@ value_set_missing (union value *v, int width) trimmed off the right end of VALUE must be all spaces. */ bool value_is_resizable (const union value *value, int old_width, int new_width) +{ + if (old_width == new_width) + return true; + else if (val_type_from_width (old_width) != val_type_from_width (new_width)) + return false; + else + { + const uint8_t *str = value->s; + int i; + + for (i = new_width; i < old_width; i++) + if (str[i] != ' ') + return false; + return true; + } +} + +/* Resizes VALUE from OLD_WIDTH to NEW_WIDTH. The arguments must + satisfy the rules specified above for value_is_resizable. */ +void +value_resize (union value *value, int old_width, int new_width) +{ + assert (value_is_resizable (value, old_width, new_width)); + if (new_width != old_width) + { + union value tmp; + value_init (&tmp, new_width); + value_copy_rpad (&tmp, new_width, value, old_width, ' '); + value_destroy (value, old_width); + *value = tmp; + } +} + +/* Returns true if VALUE, with the given WIDTH, is all spaces, false otherwise. + Returns false if VALUE is numeric. */ +bool +value_is_spaces (const union value *value, int width) { int i; - if (val_type_from_width (old_width) != val_type_from_width (new_width)) - return false; - for (i = new_width; i < old_width; i++) + for (i = 0; i < width; i++) if (value->s[i] != ' ') return false; + return true; } -/* Resizes VALUE from OLD_WIDTH to NEW_WIDTH. The arguments must - satisfy the rules specified above for value_is_resizable. */ +/* Returns true if resizing a value from OLD_WIDTH to NEW_WIDTH + actually changes anything, false otherwise. If false is + returned, calls to value_resize() with the specified + parameters may be omitted without any ill effects. + + This is generally useful only if many values can skip being + resized from OLD_WIDTH to NEW_WIDTH. Otherwise you might as + well just call value_resize directly. */ +bool +value_needs_resize (int old_width, int new_width) +{ + assert (val_type_from_width (old_width) == val_type_from_width (new_width)); + + return old_width != new_width; +} + +/* Same as value_init, except that memory for VALUE (if + necessary) is allocated from POOL and will be freed + automatically when POOL is destroyed. + + VALUE must not be freed manually by calling value_destroy. If + it needs to be resized, it must be done using + value_resize_pool instead of value_resize. */ void -value_resize (union value *value, int old_width, int new_width) +value_init_pool (struct pool *pool, union value *value, int width) +{ + if (width > 0) + value->s = pool_alloc_unaligned (pool, width); +} + +/* Same as value_clone(), except that memory for VALUE (if necessary) is + allocated from POOL and will be freed automatically when POOL is destroyed. + + VALUE must not be freed manually by calling value_destroy(). If it needs to + be resized, it must be done using value_resize_pool() instead of + value_resize(). */ +void +value_clone_pool (struct pool *pool, + union value *value, const union value *src, int width) +{ + if (width > 0) + value->s = pool_clone_unaligned (pool, src->s, width); + else + value->f = src->f; +} + +/* Same as value_resize, except that VALUE must have been + allocated from POOL using value_init_pool. + + This function causes some memory in POOL to be wasted in some + cases (until the pool is freed), so it should only be done if + this is acceptable. */ +void +value_resize_pool (struct pool *pool, union value *value, + int old_width, int new_width) { assert (value_is_resizable (value, old_width, new_width)); if (new_width > old_width) - memset (&value->s[old_width], ' ', new_width - old_width); + { + uint8_t *new_string = pool_alloc_unaligned (pool, new_width); + memcpy (new_string, value->s, old_width); + value->s = new_string; + + memset (value->s + old_width, ' ', new_width - old_width); + } }