Add datasheet.
[pspp-builds.git] / src / ui / flexifile.c
1 /* PSPP - computes sample statistics.
2
3    Copyright (C) 2006, 2007 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 static unsigned long
186 flexifilereader_cnum (const struct casereader *cr)
187 {
188   struct flexifilereader *ffr = FLEXIFILEREADER(cr);
189
190   return ffr->case_idx;
191 }
192
193 static struct ccase *
194 flexifilereader_get_next_case (struct casereader *cr)
195 {
196   struct flexifilereader *ffr = FLEXIFILEREADER(cr);
197   struct flexifile *ff = FLEXIFILE(casereader_get_casefile(cr));
198
199   if ( ffr->case_idx >= ff->case_cnt)
200     return NULL;
201
202   return &ff->cases[ffr->case_idx++];
203 }
204
205 static void
206 flexifilereader_destroy(struct casereader *r)
207 {
208   free(r);
209 }
210
211 static struct casereader *
212 flexifile_get_reader (const struct casefile *cf_)
213 {
214   struct casefile *cf = (struct casefile *) cf_;
215   struct flexifilereader *ffr = xzalloc (sizeof *ffr);
216   struct casereader *reader = (struct casereader *) ffr;
217
218   casereader_register (cf, reader, CLASS_CASEREADER(&class_reader));
219
220   return reader;
221 }
222
223
224 static struct casereader *
225 flexifilereader_clone (const struct casereader *cr)
226 {
227   const struct flexifilereader *ffr = (const struct flexifilereader *) cr;
228   struct flexifilereader *new_ffr = xzalloc (sizeof *new_ffr);
229   struct casereader *new_reader = (struct casereader *) new_ffr;
230   struct casefile *cf = casereader_get_casefile (cr);
231
232   casereader_register (cf, new_reader, CLASS_CASEREADER(&class_reader));
233
234   new_ffr->case_idx = ffr->case_idx ;
235   new_ffr->destructive = ffr->destructive ;
236
237   return new_reader;
238 }
239
240
241 static bool
242 flexifile_in_core(const struct casefile *cf UNUSED)
243 {
244   /* Always in memory */
245   return true;
246 }
247
248 static bool
249 flexifile_error (const struct casefile *cf UNUSED )
250 {
251   return false;
252 }
253
254
255 struct casefile *
256 flexifile_create (size_t value_cnt)
257 {
258   struct flexifile *ff = xzalloc (sizeof *ff);
259   struct casefile *cf = (struct casefile *) ff;
260
261   casefile_register (cf, (struct class_casefile *) &class);
262
263   ff->value_cnt = value_cnt;
264
265   ff->cases = xzalloc(sizeof (struct ccase *) * CHUNK_SIZE);
266   ff->capacity = CHUNK_SIZE;
267
268   return cf;
269 }
270
271 static const struct class_flexifile class = {
272   {
273     flexifile_destroy,
274     flexifile_error,
275     flexifile_get_value_cnt,
276     flexifile_get_case_cnt,
277     flexifile_get_reader,
278     flexifile_append,
279
280     flexifile_in_core,
281     0, /* to_disk */
282     0 /* sleep */
283   },
284
285   impl_get_case ,
286   impl_insert_case ,
287   impl_delete_cases,
288   impl_resize,
289 };
290
291
292 static const struct class_flexifilereader class_reader =
293   {
294     {
295       flexifilereader_get_next_case,
296       flexifilereader_cnum,
297       flexifilereader_destroy,
298       flexifilereader_clone
299     }
300   };
301
302
303 /* Implementations of class methods */
304
305 static bool
306 impl_get_case(const struct flexifile *ff, unsigned long casenum,
307               struct ccase *c)
308 {
309   if ( casenum >= ff->case_cnt)
310     return false;
311
312   case_clone (c, &ff->cases[casenum]);
313
314   return true;
315 }
316
317 #if DEBUGGING
318 #include <stdio.h>
319
320 static void 
321 dumpcasedata(struct ccase *c)
322 {
323   size_t value_cnt = case_get_value_cnt (c);
324   int i;
325   for ( i = 0 ; i < value_cnt * MAX_SHORT_STRING; ++i )
326     putchar (case_str (c, 0)[i]);
327   putchar('\n');
328 }
329 #endif
330
331 static bool
332 impl_resize (struct flexifile *ff, int n_values, int posn)
333 {
334   int i;
335
336   for( i = 0 ; i < ff->case_cnt ; ++i )
337     {
338       struct ccase c;
339       case_create (&c, ff->value_cnt + n_values);
340
341       case_copy (&c, 0, &ff->cases[i], 0, posn);
342       if ( n_values > 0 )
343         memset (case_data_rw_idx(&c, posn), ' ', n_values * MAX_SHORT_STRING) ;
344       case_copy (&c, posn + n_values,
345                  &ff->cases[i], posn, ff->value_cnt - posn);
346
347       case_destroy (&ff->cases[i]);
348       ff->cases[i] = c;
349     }
350
351   ff->value_cnt += n_values;
352
353   return true;
354 }
355
356 static bool
357 impl_insert_case (struct flexifile *ff, struct ccase *c, int posn)
358 {
359   int i;
360   struct ccase blank;
361
362   assert (ff);
363
364   if ( posn > ff->case_cnt )
365     return false;
366
367   if ( posn >= ff->capacity )
368     grow(ff);
369
370   case_create(&blank, ff->value_cnt);
371
372   flexifile_append(CASEFILE(ff), &blank);
373
374   case_destroy(&blank);
375
376   /* Shift the existing cases down one */
377   for ( i = ff->case_cnt ; i > posn; --i)
378       case_move(&ff->cases[i], &ff->cases[i-1]);
379
380   case_clone (&ff->cases[posn], c);
381
382   return true;
383 }
384
385
386 static bool
387 impl_delete_cases (struct flexifile *ff, int n_cases, int first)
388 {
389   int i;
390
391   if ( ff->case_cnt < first + n_cases )
392     return false;
393
394   for ( i = first ; i < first + n_cases; ++i )
395     case_destroy (&ff->cases[i]);
396
397   /* Shift the cases up by N_CASES */
398   for ( i = first; i < ff->case_cnt - n_cases; ++i )
399     {
400       case_move (&ff->cases[i], &ff->cases[i+ n_cases]);
401     }
402
403   ff->case_cnt -= n_cases;
404
405   return true;
406 }
407
408
409