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