Removed my authorship lines.
[pspp] / src / ui / flexifile.c
1 /* PSPP - computes sample statistics.
2
3    Copyright (C) 2006 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include <xalloc.h>
22 #include <assert.h>
23 #include "flexifile.h"
24 #include <string.h>
25 #include <data/casefile.h>
26 #include <data/casefile-private.h>
27 #include <data/case.h>
28 #include <libpspp/compiler.h>
29
30
31 struct class_flexifile
32 {
33   struct class_casefile parent;
34
35   bool (*get_case) (const struct flexifile *, unsigned long, struct ccase *);
36
37   bool (*insert_case) (struct flexifile *, struct ccase *, int );
38   bool (*delete_cases) (struct flexifile *, int, int );
39
40   bool (*resize) (struct flexifile *, int, int );
41 };
42
43 static const struct class_flexifile class;
44
45 #define CLASS_FLEXIFILE(K)  ((struct class_flexifile *) K)
46 #define CONST_CLASS_FLEXIFILE(K) ((const struct class_flexifile *) K)
47
48
49 /* A flexifile. */
50 struct flexifile
51 {
52   struct casefile cf;           /* Parent */
53
54   size_t value_cnt;             /* Case size in `union value's. */
55   unsigned long case_cnt;       /* Number of cases stored. */
56
57
58   /* Memory storage. */
59   struct ccase *cases;          /* Pointer to array of cases. */
60   unsigned long capacity;       /* size of array in cases */
61 };
62
63 struct class_flexifilereader 
64 {
65   struct class_casereader parent ;
66 };
67
68 static const struct class_flexifilereader class_reader;
69
70 /* For reading out the cases in a flexifile. */
71 struct flexifilereader
72 {
73   struct casereader cr;         /* Parent */
74
75   unsigned long case_idx;       /* Case number of current case. */
76   bool destructive;             /* Is this a destructive reader? */
77 };
78
79
80
81 #define CHUNK_SIZE 10
82
83 static bool 
84 impl_get_case(const struct flexifile *ff, unsigned long casenum, 
85               struct ccase *);
86 static bool
87 impl_insert_case (struct flexifile *ff, struct ccase *c, int posn);
88
89 static bool 
90 impl_delete_cases (struct flexifile *ff, int n_cases, int first);
91
92 static bool 
93 impl_resize (struct flexifile *ff, int n_values, int posn);
94
95
96 /* Gets a case, for which writing may not be safe */
97 bool 
98 flexifile_get_case(const struct flexifile *ff, unsigned long casenum, 
99                    struct ccase *c)
100 {
101   const struct class_flexifile *class = 
102     CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
103
104   return class->get_case(ff, casenum, c);
105 }
106
107
108 /* Insert N_VALUES before POSN.
109    If N_VALUES is negative, then deleted -N_VALUES instead
110 */
111 bool
112 flexifile_resize (struct flexifile *ff, int n_values, int posn)
113 {
114   const struct class_flexifile *class = 
115     CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
116
117   return class->resize(ff, n_values, posn);
118 }
119
120
121
122 bool
123 flexifile_insert_case (struct flexifile *ff, struct ccase *c, int posn)
124 {
125   const struct class_flexifile *class = 
126     CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
127
128   return class->insert_case(ff, c, posn);
129 }
130
131
132 bool
133 flexifile_delete_cases (struct flexifile *ff, int n_cases, int first)
134 {
135   const struct class_flexifile *class = 
136     CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
137
138   return class->delete_cases (ff, n_cases, first);
139 }
140
141
142 static unsigned long 
143 flexifile_get_case_cnt (const struct casefile *cf)
144 {
145   return FLEXIFILE(cf)->case_cnt;
146 }
147
148 static size_t
149 flexifile_get_value_cnt (const struct casefile *cf)
150 {
151   return FLEXIFILE(cf)->value_cnt;
152 }
153
154
155 static void
156 flexifile_destroy (struct casefile *cf)
157 {
158   int i ; 
159   for ( i = 0 ; i < FLEXIFILE(cf)->case_cnt; ++i ) 
160     case_destroy( &FLEXIFILE(cf)->cases[i]);
161
162   free(FLEXIFILE(cf)->cases);
163 }
164
165 static void
166 grow(struct flexifile *ff) 
167 {
168   ff->capacity += CHUNK_SIZE;
169   ff->cases = xrealloc(ff->cases, ff->capacity * sizeof ( *ff->cases) );
170 }
171
172 static bool
173 flexifile_append (struct casefile *cf, const struct ccase *c)
174 {
175   struct flexifile *ff =  FLEXIFILE(cf);
176
177   if (ff->case_cnt >= ff->capacity)
178     grow(ff);
179
180   case_clone (&ff->cases[ff->case_cnt++], c);
181
182   return true;
183 }
184
185
186 static struct ccase *
187 flexifilereader_get_next_case (struct casereader *cr)
188 {
189   struct flexifilereader *ffr = FLEXIFILEREADER(cr);
190   struct flexifile *ff = FLEXIFILE(casereader_get_casefile(cr));
191
192   if ( ffr->case_idx >= ff->case_cnt) 
193     return NULL;
194
195   return &ff->cases[ffr->case_idx++];
196 }
197
198 static void
199 flexifilereader_destroy(struct casereader *r)
200 {
201   free(r);
202 }
203
204 static struct casereader * 
205 flexifile_get_reader (const struct casefile *cf_)
206 {
207   struct casefile *cf = (struct casefile *) cf_;
208   struct flexifilereader *ffr = xzalloc (sizeof *ffr);
209   struct casereader *reader = (struct casereader *) ffr;
210
211   casereader_register (cf, reader, CLASS_CASEREADER(&class_reader));
212
213   return reader;
214 }
215
216
217 static struct casereader *
218 flexifilereader_clone (const struct casereader *cr)
219 {
220   const struct flexifilereader *ffr = (const struct flexifilereader *) cr;
221   struct flexifilereader *new_ffr = xzalloc (sizeof *new_ffr);
222   struct casereader *new_reader = (struct casereader *) new_ffr;
223   struct casefile *cf = casereader_get_casefile (cr);
224
225   casereader_register (cf, new_reader, CLASS_CASEREADER(&class_reader));
226
227   new_ffr->case_idx = ffr->case_idx ;
228   new_ffr->destructive = ffr->destructive ;
229
230   return new_reader;
231 }
232
233
234 static bool
235 flexifile_in_core(const struct casefile *cf UNUSED)
236 {
237   /* Always in memory */
238   return true;
239 }
240
241 static bool
242 flexifile_error (const struct casefile *cf UNUSED )
243 {
244   return false;
245 }
246
247
248 struct casefile *
249 flexifile_create (size_t value_cnt)
250 {
251   struct flexifile *ff = xzalloc (sizeof *ff);
252   struct casefile *cf = (struct casefile *) ff;
253
254   casefile_register (cf, (struct class_casefile *) &class);
255
256   ff->value_cnt = value_cnt;
257  
258   ff->cases = xzalloc(sizeof (struct ccase *) * CHUNK_SIZE);
259   ff->capacity = CHUNK_SIZE;
260  
261   return cf;
262 }
263
264 static const struct class_flexifile class = {
265   {
266     flexifile_destroy,
267     flexifile_error,
268     flexifile_get_value_cnt,
269     flexifile_get_case_cnt,
270     flexifile_get_reader,
271     flexifile_append,
272
273     flexifile_in_core,
274     0, /* to_disk */
275     0 /* sleep */
276   },
277
278   impl_get_case ,
279   impl_insert_case ,
280   impl_delete_cases,
281   impl_resize,
282 };
283
284
285 static const struct class_flexifilereader class_reader = 
286   {
287     {
288       flexifilereader_get_next_case,
289       0,  /* cnum */
290       flexifilereader_destroy,
291       flexifilereader_clone
292     }
293   };
294
295
296 /* Implementations of class methods */
297
298 static bool 
299 impl_get_case(const struct flexifile *ff, unsigned long casenum, 
300               struct ccase *c)
301 {
302   if ( casenum >= ff->case_cnt) 
303     return false;
304
305   case_clone (c, &ff->cases[casenum]);
306   
307   return true;
308 }
309
310 #if DEBUGGING
311 static void
312 dumpcasedata(struct ccase *c)
313 {
314   int i;
315   for ( i = 0 ; i < c->case_data->value_cnt * MAX_SHORT_STRING; ++i ) 
316     putchar(c->case_data->values->s[i]);
317   putchar('\n');
318 }
319 #endif
320
321 static bool 
322 impl_resize (struct flexifile *ff, int n_values, int posn)
323 {
324   int i;
325
326   for( i = 0 ; i < ff->case_cnt ; ++i ) 
327     {
328       struct ccase c;
329       case_create (&c, ff->value_cnt + n_values);
330
331       case_copy (&c, 0, &ff->cases[i], 0, posn);
332       if ( n_values > 0 ) 
333         memset (case_data_rw_idx(&c, posn), ' ', n_values * MAX_SHORT_STRING) ;
334       case_copy (&c, posn + n_values, 
335                  &ff->cases[i], posn, ff->value_cnt - posn);
336
337       case_destroy (&ff->cases[i]);
338       ff->cases[i] = c;
339     }
340
341   ff->value_cnt += n_values;
342
343   return true;
344 }
345
346 static bool
347 impl_insert_case (struct flexifile *ff, struct ccase *c, int posn)
348 {
349   int i;
350   struct ccase blank;
351   
352   assert (ff);
353
354   if ( posn > ff->case_cnt )
355     return false;
356
357   if ( posn >= ff->capacity ) 
358     grow(ff);
359
360   case_create(&blank, ff->value_cnt);
361
362   flexifile_append(CASEFILE(ff), &blank);
363
364   case_destroy(&blank);
365
366   /* Shift the existing cases down one */
367   for ( i = ff->case_cnt ; i > posn; --i)
368       case_move(&ff->cases[i], &ff->cases[i-1]);
369
370   case_clone (&ff->cases[posn], c);
371
372   return true;
373 }
374
375
376 static bool 
377 impl_delete_cases (struct flexifile *ff, int n_cases, int first)
378 {
379   int i;
380
381   if ( ff->case_cnt < first + n_cases ) 
382     return false;
383
384   for ( i = first ; i < first + n_cases; ++i ) 
385     case_destroy (&ff->cases[i]);
386   
387   /* Shift the cases up by N_CASES */
388   for ( i = first; i < ff->case_cnt - n_cases; ++i ) 
389     {
390       case_move (&ff->cases[i], &ff->cases[i+ n_cases]);
391     }
392
393   ff->case_cnt -= n_cases;
394   
395   return true;
396 }
397
398
399