X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fvalue.c;h=6dbecb11abfca8137bc8fdc763ffc1c0a8568bc2;hb=8830c95bb9e8d72621787866141a27fc22e8c786;hp=5e94b68c2ed81b5abf656e29edca9c4df9d26e2f;hpb=b5f633cb2139ad17ce66cb4433b3a96276ccb1a9;p=pspp-builds.git diff --git a/src/data/value.c b/src/data/value.c index 5e94b68c..6dbecb11 100644 --- a/src/data/value.c +++ b/src/data/value.c @@ -1,77 +1,236 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2009 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 2 of the - License, or (at your option) any later version. + 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. + 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include -#include "value.h" +#include +#include +#include #include +#include #include +#include +#include "minmax.h" #include "xalloc.h" -/* Duplicate a value. - The caller is responsible for freeing the returned value. */ -union value * -value_dup (const union value *val, int width) +/* 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. + + 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) +{ + u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width, + value_str (src, src_width), 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. + + DST must be a string value; that is, DST_WIDTH must be + positive. + + 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) +{ + 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. + + 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_buf_rpad (union value *dst, int dst_width, + const uint8_t *src, size_t src_len, char pad) { - return xmemdup (val, MAX (width, sizeof *val)); + u8_buf_copy_rpad (value_str_rw (dst, dst_width), dst_width, src, src_len, pad); +} + +/* Sets V to the system-missing value for data of the given + WIDTH. */ +void +value_set_missing (union value *v, int width) +{ + if (width != -1) + { + if (width == 0) + v->f = SYSMIS; + else + memset (value_str_rw (v, width), ' ', width); + } } /* 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. */ + a strcmp()-type result. */ int -compare_values (const union value *a, const union value *b, int width) +value_compare_3way (const union value *a, const union value *b, int width) { - return (width == 0 - ? (a->f < b->f ? -1 : a->f > b->f) - : memcmp (a->s, b->s, MIN (MAX_SHORT_STRING, width))); + return (width == -1 ? 0 + : width == 0 ? (a->f < b->f ? -1 : a->f > b->f) + : memcmp (value_str (a, width), value_str (b, width), width)); } -/* 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 union value *v, int 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 == 0 - ? hsh_hash_double (v->f) - : hsh_hash_bytes (v->s, MIN (MAX_SHORT_STRING, width))); + return (width == -1 ? true + : width == 0 ? a->f == b->f + : !memcmp (value_str (a, width), value_str (b, width), width)); } -/* Copies SRC to DST, given that they both contain data of the - given WIDTH. */ -void -value_copy (union value *dst, const union value *src, int 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) { - if (width == 0) - dst->f = src->f; + return (width == -1 ? basis + : width == 0 ? hash_double (value->f, basis) + : hash_bytes (value_str (value, width), width, basis)); +} + +/* Tests whether VALUE may be resized from OLD_WIDTH to + NEW_WIDTH, using the following rules that match those for + resizing missing values and value labels. First, OLD_WIDTH + and NEW_WIDTH must be both numeric or both string. Second, if + NEW_WIDTH is less than OLD_WIDTH, then the bytes that would be + 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 - memcpy (dst->s, src->s, width); + { + const uint8_t *str = value_str (value, old_width); + int i; + + for (i = new_width; i < old_width; i++) + if (str[i] != ' ') + return false; + return true; + } } -/* Sets V to the system-missing value for data of the given - WIDTH. */ +/* Resizes VALUE from OLD_WIDTH to NEW_WIDTH. The arguments must + satisfy the rules specified above for value_is_resizable. */ void -value_set_missing (union value *v, int width) +value_resize (union value *value, int old_width, int new_width) { - if (width == 0) - v->f = SYSMIS; - else - memset (v->s, ' ', 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 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)); + + /* We need to call value_resize if either the new width is + longer than the old width (in which case the new characters + must be set to spaces) or if either width is a long string. + (We could omit resizing if both the old and new widths were + long and the new width was shorter, but we choose to do so + anyway in hopes of saving memory.) */ + return (old_width != new_width + && (new_width > old_width + || old_width > MAX_SHORT_STRING + || new_width > MAX_SHORT_STRING)); +} + +/* 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_init_pool (struct pool *pool, union value *value, int width) +{ + if (width > MAX_SHORT_STRING) + value->long_string = pool_alloc_unaligned (pool, width); +} + +/* 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) + { + if (new_width > MAX_SHORT_STRING) + { + uint8_t *new_long_string = pool_alloc_unaligned (pool, new_width); + memcpy (new_long_string, value_str (value, old_width), old_width); + value->long_string = new_long_string; + } + memset (value_str_rw (value, new_width) + old_width, ' ', + new_width - old_width); + } }