65d10aea2477640a826077a7da01c17f78fbf665
[pspp-builds.git] / src / data / lazy-casereader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <data/lazy-casereader.h>
20
21 #include <stdlib.h>
22
23 #include <data/case.h>
24 #include <data/casereader.h>
25 #include <data/casereader-provider.h>
26 #include <libpspp/assertion.h>
27
28 #include "xalloc.h"
29
30 /* A lazy casereader's auxiliary data. */
31 struct lazy_casereader
32   {
33     unsigned long int serial;
34     struct casereader *(*callback) (void *aux);
35     void *aux;
36   };
37
38 static const struct casereader_class lazy_casereader_class;
39
40 /* Creates and returns a new lazy casereader that will
41    instantiate its underlying casereader, if necessary, by
42    calling CALLBACK, passing AUX as its argument.  *SERIAL is set
43    to a "serial number" that uniquely identifies the new lazy
44    casereader, for use with lazy_casereader_destroy.
45
46    VALUE_CNT must be the number of struct values per case read
47    from the casereader.
48
49    CASE_CNT is an upper limit on the number of cases that
50    casereader_read will return from the casereader in successive
51    calls.  Ordinarily, this is the actual number of cases in the
52    data source or CASENUMBER_MAX if the number of cases cannot be
53    predicted in advance. */
54 struct casereader *
55 lazy_casereader_create (size_t value_cnt, casenumber case_cnt,
56                         struct casereader *(*callback) (void *aux), void *aux,
57                         unsigned long int *serial)
58 {
59   static unsigned long int next_serial = 0;
60   struct lazy_casereader *lc;
61   assert (callback != NULL);
62   lc = xmalloc (sizeof *lc);
63   *serial = lc->serial = next_serial++;
64   lc->callback = callback;
65   lc->aux = aux;
66   return casereader_create_sequential (NULL, value_cnt, case_cnt,
67                                        &lazy_casereader_class, lc);
68 }
69
70 /* If READER is the lazy casereader that was returned by
71    lazy_casereader_create along with SERIAL, and READER was never
72    instantiated by any use of a casereader function, then this
73    function destroys READER without instantiating it, and returns
74    true.  Returns false in any other case; that is, if READER is
75    not a lazy casereader, or if READER is a lazy casereader with
76    a serial number different from SERIAL, or if READER is a lazy
77    casereader that was instantiated.
78
79    When this function returns true, it necessarily indicates
80    that the lazy casereader was never cloned and never
81    destroyed. */
82 bool
83 lazy_casereader_destroy (struct casereader *reader, unsigned long int serial)
84 {
85   struct lazy_casereader *lc;
86
87   if (reader == NULL)
88     return false;
89
90   lc = casereader_dynamic_cast (reader, &lazy_casereader_class);
91   if (lc == NULL || lc->serial != serial)
92     return false;
93
94   lc->callback = NULL;
95   casereader_destroy (reader);
96   return true;
97 }
98
99 /* Instantiates lazy casereader READER, which is associated with
100    LC. */
101 static void
102 instantiate_lazy_casereader (struct casereader *reader,
103                              struct lazy_casereader *lc)
104 {
105   struct casereader *subreader;
106
107   /* Call the client-provided callback to obtain the real
108      casereader, then swap READER with that casereader. */
109   subreader = lc->callback (lc->aux);
110   casereader_swap (reader, subreader);
111
112   /* Now destroy the lazy casereader, which is no longer needed
113      since we already swapped it out.  Set the callback to null
114      to prevent lazy_casereader_do_destroy from trying to
115      instantiate it again.  */
116   lc->callback = NULL;
117   casereader_destroy (subreader);
118 }
119
120 static struct ccase *
121 lazy_casereader_read (struct casereader *reader, void *lc_)
122 {
123   struct lazy_casereader *lc = lc_;
124   instantiate_lazy_casereader (reader, lc);
125   return casereader_read (reader);
126 }
127
128 static void
129 lazy_casereader_do_destroy (struct casereader *reader UNUSED, void *lc_)
130 {
131   struct lazy_casereader *lc = lc_;
132   if (lc->callback != NULL)
133     casereader_destroy (lc->callback (lc->aux));
134   free (lc);
135 }
136
137 static struct casereader *
138 lazy_casereader_clone (struct casereader *reader, void *lc_)
139 {
140   struct lazy_casereader *lc = lc_;
141   instantiate_lazy_casereader (reader, lc);
142   return casereader_clone (reader);
143 }
144
145 static struct ccase *
146 lazy_casereader_peek (struct casereader *reader, void *lc_, casenumber idx)
147 {
148   struct lazy_casereader *lc = lc_;
149   instantiate_lazy_casereader (reader, lc);
150   return casereader_peek (reader, idx);
151 }
152
153 static const struct casereader_class lazy_casereader_class =
154   {
155     lazy_casereader_read,
156     lazy_casereader_do_destroy,
157     lazy_casereader_clone,
158     lazy_casereader_peek,
159   };