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