/* PSPP - a program for statistical analysis. Copyright (C) 2007, 2009, 2010, 2011 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 "data/case-tmpfile.h" #include #include #include #include "libpspp/assertion.h" #include "libpspp/taint.h" #include "libpspp/ext-array.h" #include "gl/xalloc.h" /* A temporary file that stores an array of cases. */ struct case_tmpfile { struct taint *taint; /* Taint. */ struct caseproto *proto; /* Format of cases in the tmpfile. */ size_t case_size; /* Number of bytes per case. */ size_t *offsets; /* Offset to each value. */ struct ext_array *ext_array; /* Temporary file. */ }; /* Returns the number of bytes needed to store a value with the given WIDTH on disk. */ static size_t width_to_n_bytes (int width) { return width == 0 ? sizeof (double) : width; } /* Returns the address of the data in VALUE (for reading or writing to/from disk). VALUE must have the given WIDTH. */ static void * value_to_data (const union value *value_, int width) { union value *value = CONST_CAST (union value *, value_); assert (sizeof value->f == sizeof (double)); if (width == 0) return &value->f; else return value->s; } /* Creates and returns a new case_tmpfile that will store cases that match case prototype PROTO. The caller retains ownership of PROTO. */ struct case_tmpfile * case_tmpfile_create (const struct caseproto *proto) { struct case_tmpfile *ctf; size_t n_values; size_t i; ctf = xmalloc (sizeof *ctf); ctf->taint = taint_create (); ctf->ext_array = ext_array_create (); ctf->proto = caseproto_ref (proto); ctf->case_size = 0; n_values = caseproto_get_n_widths (proto); ctf->offsets = xmalloc (n_values * sizeof *ctf->offsets); for (i = 0; i < n_values; i++) { size_t width = caseproto_get_width (proto, i); ctf->offsets[i] = ctf->case_size; ctf->case_size += width == -1 ? 0 : width == 0 ? sizeof (double) : width; } return ctf; } /* Destroys case_tmpfile CTF. Returns true if CTF was tainted, which is caused by an I/O error on case_tmpfile access or by taint propagation to the case_tmpfile. */ bool case_tmpfile_destroy (struct case_tmpfile *ctf) { bool ok = true; if (ctf != NULL) { struct taint *taint = ctf->taint; ext_array_destroy (ctf->ext_array); caseproto_unref (ctf->proto); free (ctf->offsets); free (ctf); ok = taint_destroy (taint); } return ok; } /* Returns true if CTF is tainted, which is caused by an I/O error on case_tmpfile access or by taint propagation to the case_tmpfile. */ bool case_tmpfile_error (const struct case_tmpfile *ctf) { return taint_is_tainted (ctf->taint); } /* Marks CTF as tainted. */ void case_tmpfile_force_error (struct case_tmpfile *ctf) { taint_set_taint (ctf->taint); } /* Returns CTF's taint object. */ const struct taint * case_tmpfile_get_taint (const struct case_tmpfile *ctf) { return ctf->taint; } /* Reads N_VALUES values into VALUES, from the case numbered CASE_IDX starting START_VALUE values into that case. Returns true if successful, false if CTF is tainted or an I/O error occurs during the operation. The results of this function are undefined if any of the values read have not been previously written to CTF. */ bool case_tmpfile_get_values (const struct case_tmpfile *ctf, casenumber case_idx, size_t start_value, union value values[], size_t n_values) { off_t case_offset = (off_t) ctf->case_size * case_idx; size_t i; assert (caseproto_range_is_valid (ctf->proto, start_value, n_values)); for (i = start_value; i < start_value + n_values; i++) { int width = caseproto_get_width (ctf->proto, i); if (width != -1 && !ext_array_read (ctf->ext_array, case_offset + ctf->offsets[i], width_to_n_bytes (width), value_to_data (&values[i], width))) return false; } return true; } /* Reads the case numbered CASE_IDX from CTF. Returns the case if successful or a null pointer if CTF is tainted or an I/O error occurs during the operation. The results of this function are undefined if the case read from CTF had not previously been written. */ struct ccase * case_tmpfile_get_case (const struct case_tmpfile *ctf, casenumber case_idx) { struct ccase *c = case_create (ctf->proto); if (case_tmpfile_get_values (ctf, case_idx, 0, case_data_all_rw (c), caseproto_get_n_widths (ctf->proto))) return c; else { case_unref (c); return NULL; } } /* Writes N_VALUES values from VALUES, into the case numbered CASE_IDX starting START_VALUE values into that case. Returns true if successful, false if CTF is tainted or an I/O error occurs during the operation. */ bool case_tmpfile_put_values (struct case_tmpfile *ctf, casenumber case_idx, size_t start_value, const union value values[], size_t n_values) { off_t case_offset = (off_t) ctf->case_size * case_idx; size_t i; assert (caseproto_range_is_valid (ctf->proto, start_value, n_values)); for (i = start_value; i < start_value + n_values; i++) { int width = caseproto_get_width (ctf->proto, i); if (width != -1 && !ext_array_write (ctf->ext_array, case_offset + ctf->offsets[i], width_to_n_bytes (width), value_to_data (values++, width))) return false; } return true; } /* Writes C to CTF as the case numbered CASE_IDX. Returns true if successful, false if CTF is tainted or an I/O error occurs during the operation. */ bool case_tmpfile_put_case (struct case_tmpfile *ctf, casenumber case_idx, struct ccase *c) { bool ok = case_tmpfile_put_values (ctf, case_idx, 0, case_data_all (c), caseproto_get_n_widths (ctf->proto)); case_unref (c); return ok; }

AltStyle によって変換されたページ (->オリジナル) /