X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fcasereader.c;h=3d27a9192a79bd63466e25e2059ce91824e94241;hb=a5f512f378fbab43da8496d366dedcf59f29f580;hp=3c062d9068b6355ace4f8e3caf005b89cd7c25ad;hpb=d908f52b818f4fdf2ab23797756812ec2986bd2b;p=pspp-builds.git diff --git a/src/data/casereader.c b/src/data/casereader.c index 3c062d90..3d27a919 100644 --- a/src/data/casereader.c +++ b/src/data/casereader.c @@ -1,20 +1,18 @@ -/* PSPP - computes sample statistics. - Copyright (C) 2007 Free Software Foundation, Inc. +/* PSPP - a program for statistical analysis. + Copyright (C) 2007, 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 @@ -45,32 +43,43 @@ struct casereader static void insert_shim (struct casereader *); -/* Creates a new case in C and reads the next case from READER - into it. The caller owns C and must destroy C when its data - is no longer needed. Return true if successful, false when - cases have been exhausted or upon detection of an I/O error. - In the latter case, C is set to the null case. +/* Reads and returns the next case from READER. The caller owns + the returned case and must call case_unref on it when its data + is no longer needed. Returns a null pointer if cases have + been exhausted or upon detection of an I/O error. The case returned is effectively consumed: it can never be read again through READER. If this is inconvenient, READER may be cloned in advance with casereader_clone, or casereader_peek may be used instead. */ -bool -casereader_read (struct casereader *reader, struct ccase *c) +struct ccase * +casereader_read (struct casereader *reader) { - if (reader->case_cnt != 0 && reader->class->read (reader, reader->aux, c)) + if (reader->case_cnt != 0) { - assert (case_get_value_cnt (c) == reader->value_cnt); + /* ->read may use casereader_swap to replace itself by + another reader and then delegate to that reader by + recursively calling casereader_read. Currently only + lazy_casereader does this and, with luck, nothing else + ever will. + + To allow this to work, however, we must decrement + case_cnt before calling ->read. If we decremented + case_cnt after calling ->read, then this would actually + drop two cases from case_cnt instead of one, and we'd + lose the last case in the casereader. */ + struct ccase *c; if (reader->case_cnt != CASENUMBER_MAX) reader->case_cnt--; - return true; - } - else - { - reader->case_cnt = 0; - case_nullify (c); - return false; + c = reader->class->read (reader, reader->aux); + if (c != NULL) + { + assert (case_get_value_cnt (c) >= reader->value_cnt); + return c; + } } + reader->case_cnt = 0; + return NULL; } /* Destroys READER. @@ -151,27 +160,50 @@ casereader_swap (struct casereader *a, struct casereader *b) } } -/* Creates a new case in C and reads the (IDX + 1)'th case from - READER into it. The caller owns C and must destroy C when its - data is no longer needed. Return true if successful, false - when cases have been exhausted or upon detection of an I/O - error. In the latter case, C is set to the null case. */ -bool -casereader_peek (struct casereader *reader, casenumber idx, struct ccase *c) +/* Reads and returns the (IDX + 1)'th case from READER. The + caller owns the returned case and must call case_unref on it + when it is no longer needed. Returns a null pointer if cases + have been exhausted or upon detection of an I/O error. */ +struct ccase * +casereader_peek (struct casereader *reader, casenumber idx) { if (idx < reader->case_cnt) { + struct ccase *c; if (reader->class->peek == NULL) insert_shim (reader); - if (reader->class->peek (reader, reader->aux, idx, c)) - return true; + c = reader->class->peek (reader, reader->aux, idx); + if (c != NULL) + return c; else if (casereader_error (reader)) reader->case_cnt = 0; } if (reader->case_cnt > idx) reader->case_cnt = idx; - case_nullify (c); - return false; + return NULL; +} + +/* Returns true if no cases remain to be read from READER, or if + an error has occurred on READER. (A return value of false + does *not* mean that the next call to casereader_peek or + casereader_read will return true, because an error can occur + in the meantime.) */ +bool +casereader_is_empty (struct casereader *reader) +{ + if (reader->case_cnt == 0) + return true; + else + { + struct ccase *c = casereader_peek (reader, 0); + if (c == NULL) + return true; + else + { + case_unref (c); + return false; + } + } } /* Returns true if an I/O error or another hard error has @@ -236,11 +268,11 @@ casereader_count_cases (struct casereader *reader) if (reader->case_cnt == CASENUMBER_MAX) { casenumber n_cases = 0; - struct ccase c; + struct ccase *c; struct casereader *clone = casereader_clone (reader); - for (; casereader_read (clone, &c); case_destroy (&c)) + for (; (c = casereader_read (clone)) != NULL; case_unref (c)) n_cases++; casereader_destroy (clone); @@ -262,12 +294,12 @@ casereader_get_value_cnt (struct casereader *reader) void casereader_transfer (struct casereader *reader, struct casewriter *writer) { - struct ccase c; + struct ccase *c; taint_propagate (casereader_get_taint (reader), casewriter_get_taint (writer)); - while (casereader_read (reader, &c)) - casewriter_write (writer, &c); + while ((c = casereader_read (reader)) != NULL) + casewriter_write (writer, c); casereader_destroy (reader); } @@ -313,6 +345,26 @@ casereader_create_sequential (const struct taint *taint, reader->aux = aux; return reader; } + +/* If READER is a casereader of the given CLASS, returns its + associated auxiliary data; otherwise, returns a null pointer. + + This function is intended for use from casereader + implementations, not by casereader users. Even within + casereader implementations, its usefulness is quite limited, + for at least two reasons. First, every casereader member + function already receives a pointer to the casereader's + auxiliary data. Second, a casereader's class can change + (through a call to casereader_swap) and this is in practice + quite common (e.g. any call to casereader_clone on a + casereader that does not directly support clone will cause the + casereader to be replaced by a shim caseader). */ +void * +casereader_dynamic_cast (struct casereader *reader, + const struct casereader_class *class) +{ + return reader->class == class ? reader->aux : NULL; +} /* Random-access casereader implementation. @@ -346,7 +398,7 @@ struct random_reader_shared void *aux; }; -static struct casereader_class random_reader_casereader_class; +static const struct casereader_class random_reader_casereader_class; /* Creates and returns a new random_reader with the given SHARED data and OFFSET. Inserts the new random reader into the @@ -428,22 +480,20 @@ advance_random_reader (struct casereader *reader, } /* struct casereader_class "read" function for random reader. */ -static bool -random_reader_read (struct casereader *reader, void *br_, struct ccase *c) +static struct ccase * +random_reader_read (struct casereader *reader, void *br_) { struct random_reader *br = br_; struct random_reader_shared *shared = br->shared; - - if (shared->class->read (reader, shared->aux, - br->offset - shared->min_offset, c)) + struct ccase *c = shared->class->read (reader, shared->aux, + br->offset - shared->min_offset); + if (c != NULL) { br->offset++; heap_changed (shared->readers, &br->heap_node); advance_random_reader (reader, shared); - return true; } - else - return false; + return c; } /* struct casereader_class "destroy" function for random @@ -482,19 +532,18 @@ random_reader_clone (struct casereader *reader, void *br_) } /* struct casereader_class "peek" function for random reader. */ -static bool -random_reader_peek (struct casereader *reader, void *br_, - casenumber idx, struct ccase *c) +static struct ccase * +random_reader_peek (struct casereader *reader, void *br_, casenumber idx) { struct random_reader *br = br_; struct random_reader_shared *shared = br->shared; return shared->class->read (reader, shared->aux, - br->offset - shared->min_offset + idx, c); + br->offset - shared->min_offset + idx); } /* Casereader class for random reader. */ -static struct casereader_class random_reader_casereader_class = +static const struct casereader_class random_reader_casereader_class = { random_reader_read, random_reader_destroy, @@ -533,7 +582,7 @@ struct shim struct casereader *subreader; /* Subordinate casereader. */ }; -static struct casereader_random_class shim_class; +static const struct casereader_random_class shim_class; /* Interposes a buffering shim atop READER. */ static void @@ -542,7 +591,7 @@ insert_shim (struct casereader *reader) size_t value_cnt = casereader_get_value_cnt (reader); casenumber case_cnt = casereader_get_case_cnt (reader); struct shim *b = xmalloc (sizeof *b); - b->window = casewindow_create (value_cnt, get_workspace_cases (value_cnt)); + b->window = casewindow_create (value_cnt, settings_get_workspace_cases (value_cnt)); b->subreader = casereader_create_random (value_cnt, case_cnt, &shim_class, b); casereader_swap (reader, b->subreader); @@ -560,24 +609,27 @@ prime_buffer (struct shim *b, casenumber case_cnt) { while (casewindow_get_case_cnt (b->window) < case_cnt) { - struct ccase tmp; - if (!casereader_read (b->subreader, &tmp)) + struct ccase *tmp = casereader_read (b->subreader); + if (tmp == NULL) return false; - casewindow_push_head (b->window, &tmp); + casewindow_push_head (b->window, tmp); } return true; } /* Reads the case at the given 0-based OFFSET from the front of - the window into C. Returns true if successful, false if - OFFSET is beyond the end of file or upon I/O error. */ -static bool + the window into C. Returns the case if successful, or a null + pointer if OFFSET is beyond the end of file or upon I/O error. + The caller must call case_unref() on the returned case when it + is no longer needed. */ +static struct ccase * shim_read (struct casereader *reader UNUSED, void *b_, - casenumber offset, struct ccase *c) + casenumber offset) { struct shim *b = b_; - return (prime_buffer (b, offset + 1) - && casewindow_get_case (b->window, offset, c)); + if (!prime_buffer (b, offset + 1)) + return NULL; + return casewindow_get_case (b->window, offset); } /* Destroys B. */ @@ -599,7 +651,7 @@ shim_advance (struct casereader *reader UNUSED, void *b_, casenumber case_cnt) } /* Class for the buffered reader. */ -static struct casereader_random_class shim_class = +static const struct casereader_random_class shim_class = { shim_read, shim_destroy,