1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * go-data-cache.h : The definition of a content for a data slicer
5 * Copyright (C) 2008 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gnumeric-config.h>
24 #include "go-data-cache-impl.h"
25 #include "go-data-cache-source.h"
26 #include "go-data-cache-field-impl.h"
28 #include <gsf/gsf-impl-utils.h>
29 #include <glib/gi18n-lib.h>
32 #define GO_DATA_CACHE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_CACHE_TYPE, GODataCacheClass))
33 #define IS_GO_DATA_CACHE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_CACHE_TYPE))
34 #define GO_DATA_CACHE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_CACHE_TYPE, GODataCacheClass))
38 PROP_REFRESHED_BY
, /* char * */
39 PROP_REFRESHED_ON
, /* GOVal * */
40 PROP_REFRESH_UPGRADES
, /* bool */
41 PROP_XL_REFRESH_VER
, /* unsigned int */
42 PROP_XL_CREATED_VER
/* unsigned int */
45 /*****************************************************************/
48 go_data_cache_records_set_size (GODataCache
*cache
, unsigned int n
)
52 g_return_if_fail (cache
->record_size
> 0);
53 g_return_if_fail (n
< G_MAXUINT
/ cache
->record_size
);
55 expand
= n
- cache
->records_allocated
;
59 cache
->records
= g_realloc (cache
->records
, n
* cache
->record_size
);
61 memset (cache
->records
+ cache
->records_allocated
* cache
->record_size
, 0,
62 expand
* cache
->record_size
);
63 cache
->records_allocated
= n
;
67 go_data_cache_records_fetch_index (GODataCache
*cache
, unsigned i
)
69 if (cache
->records_allocated
<= i
) {
70 go_data_cache_records_set_size (cache
, i
+128);
71 if (cache
->records_allocated
<= i
)
75 if (cache
->records_len
<= i
)
76 cache
->records_len
= i
+ 1;
78 return go_data_cache_records_index (cache
, i
);
82 go_data_cache_records_init (GODataCache
*cache
, unsigned int n
, unsigned int record_size
)
84 cache
->record_size
= record_size
;
85 cache
->records_len
= 0;
86 go_data_cache_records_set_size (cache
, n
);
89 /*****************************************************************/
91 static GObjectClass
*parent_klass
;
93 go_data_cache_init (GODataCache
*cache
)
95 cache
->fields
= g_ptr_array_new ();
96 cache
->data_source
= NULL
;
97 cache
->records
= NULL
;
98 cache
->records_len
= cache
->records_allocated
= 0;
100 cache
->refreshed_by
= NULL
;
101 cache
->refreshed_on
= NULL
;
102 cache
->refresh_upgrades
= TRUE
;
104 cache
->XL_created_ver
= 1;
105 cache
->XL_refresh_ver
= 1;
109 go_data_cache_finalize (GObject
*obj
)
111 GODataCache
*cache
= (GODataCache
*)obj
;
114 if (NULL
!= cache
->records
) {
115 for (i
= cache
->fields
->len
; i
-- > 0 ; ) {
116 GODataCacheField
const *f
= g_ptr_array_index (cache
->fields
, i
);
117 if (GO_DATA_CACHE_FIELD_TYPE_INLINE
== f
->ref_type
) {
119 for (j
= cache
->records_len
; j
-- > 0 ; ) {
121 gpointer p
= go_data_cache_records_index (cache
, j
) + f
->offset
;
122 memcpy (&v
, p
, sizeof (v
));
127 g_free (cache
->records
);
128 cache
->records
= NULL
;
129 cache
->records_len
= cache
->records_allocated
= 0;
132 for (i
= cache
->fields
->len
; i
-- > 0 ; )
133 g_object_unref (g_ptr_array_index (cache
->fields
, i
));
134 g_ptr_array_free (cache
->fields
, TRUE
);
135 cache
->fields
= NULL
;
137 if (NULL
!= cache
->data_source
) {
138 g_object_unref (cache
->data_source
);
139 cache
->data_source
= NULL
;
142 g_free (cache
->refreshed_by
);
143 go_val_free (cache
->refreshed_on
);
145 (parent_klass
->finalize
) (obj
);
149 go_data_cache_set_property (GObject
*obj
, guint property_id
,
150 GValue
const *value
, GParamSpec
*pspec
)
152 GODataCache
*cache
= (GODataCache
*)obj
;
154 switch (property_id
) {
155 case PROP_REFRESHED_BY
:
156 g_free (cache
->refreshed_by
);
157 cache
->refreshed_by
= g_value_dup_string (value
);
159 case PROP_REFRESHED_ON
:
160 go_val_free (cache
->refreshed_on
);
161 cache
->refreshed_on
= g_value_dup_boxed (value
);
163 case PROP_REFRESH_UPGRADES
: cache
->refresh_upgrades
= g_value_get_boolean (value
); break;
164 case PROP_XL_REFRESH_VER
: cache
->XL_refresh_ver
= g_value_get_uint (value
); break;
165 case PROP_XL_CREATED_VER
: cache
->XL_created_ver
= g_value_get_uint (value
); break;
168 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
173 go_data_cache_get_property (GObject
*obj
, guint property_id
,
174 GValue
*value
, GParamSpec
*pspec
)
176 GODataCache
const *cache
= (GODataCache
const *)obj
;
177 switch (property_id
) {
178 case PROP_REFRESHED_BY
: g_value_set_string (value
, cache
->refreshed_by
); break;
179 case PROP_REFRESHED_ON
: g_value_set_boxed (value
, cache
->refreshed_on
); break;
180 case PROP_REFRESH_UPGRADES
: g_value_set_boolean (value
, cache
->refresh_upgrades
); break;
181 case PROP_XL_REFRESH_VER
: g_value_set_uint (value
, cache
->XL_refresh_ver
); break;
182 case PROP_XL_CREATED_VER
: g_value_set_uint (value
, cache
->XL_created_ver
); break;
184 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
189 go_data_cache_class_init (GODataCacheClass
*klass
)
191 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
192 gobject_class
->set_property
= go_data_cache_set_property
;
193 gobject_class
->get_property
= go_data_cache_get_property
;
194 gobject_class
->finalize
= go_data_cache_finalize
;
196 g_object_class_install_property (gobject_class
, PROP_REFRESHED_BY
,
197 g_param_spec_string ("refreshed-by", NULL
, NULL
, NULL
,
198 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
199 g_object_class_install_property (gobject_class
, PROP_REFRESHED_ON
,
200 g_param_spec_boxed ("refreshed-on", NULL
, NULL
,
201 GO_VAL_TYPE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
202 g_object_class_install_property (gobject_class
, PROP_REFRESH_UPGRADES
,
203 g_param_spec_boolean ("refresh-upgrades", NULL
, NULL
,
204 TRUE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
205 g_object_class_install_property (gobject_class
, PROP_XL_REFRESH_VER
,
206 g_param_spec_uint ("refresh-version", NULL
, NULL
,
207 0, G_MAXUINT
, 1, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
208 g_object_class_install_property (gobject_class
, PROP_XL_CREATED_VER
,
209 g_param_spec_uint ("created-version", NULL
, NULL
,
210 0, G_MAXUINT
, 1, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
212 parent_klass
= g_type_class_peek_parent (klass
);
215 GSF_CLASS (GODataCache
, go_data_cache
,
216 go_data_cache_class_init
, go_data_cache_init
,
220 go_data_cache_get_source (GODataCache
const *cache
)
222 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), NULL
);
223 return cache
->data_source
;
227 * go_data_cache_set_source :
228 * @cache: #GODataCache
229 * @src: #GODataCacheSource
231 * Absorbs the reference to @src.
234 go_data_cache_set_source (GODataCache
*cache
, GODataCacheSource
*src
)
236 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
237 g_return_if_fail (NULL
== src
|| IS_GO_DATA_CACHE_SOURCE (src
));
239 if (cache
->data_source
)
240 g_object_unref (cache
->data_source
);
241 cache
->data_source
= src
;
245 go_data_cache_add_field (GODataCache
*cache
, GODataCacheField
*field
)
247 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
248 g_return_if_fail (IS_GO_DATA_CACHE_FIELD (field
));
249 g_return_if_fail (field
->indx
< 0);
250 g_return_if_fail (field
->cache
== NULL
);
251 g_return_if_fail (NULL
== cache
->records
);
253 field
->indx
= cache
->fields
->len
;
254 field
->cache
= cache
;
255 g_ptr_array_add (cache
->fields
, field
);
259 * go_data_cache_import_start :
260 * @cache:#GODataCache
263 * Validate the field setup and initialize the storage.
266 go_data_cache_import_start (GODataCache
*cache
, unsigned int n
)
269 unsigned int i
, offset
= 0;
271 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
272 g_return_if_fail (NULL
== cache
->records
);
274 for (i
= 0 ; i
< cache
->fields
->len
; i
++) {
275 f
= g_ptr_array_index (cache
->fields
, i
);
277 if (NULL
== f
->indexed
|| 0 == f
->indexed
->len
) {
278 if (NULL
!= f
->grouped
&&
279 f
->group_parent
>= 0 && f
->group_parent
!= f
->indx
)
280 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_NONE
;
282 offset
+= sizeof (GOVal
*);
283 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_INLINE
;
285 } else if (f
->indexed
->len
< ((1<<8) - 1)) {
286 offset
+= sizeof (guint8
);
287 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
;
288 } else if (f
->indexed
->len
< ((1<<16) - 1)) {
289 offset
+= sizeof (guint16
);
290 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
;
292 offset
+= sizeof (guint32
);
293 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
;
297 for (i
= 0 ; i
< cache
->fields
->len
; i
++) {
298 f
= g_ptr_array_index (cache
->fields
, i
);
299 if (f
->group_parent
>= 0) {
300 GODataCacheField
*base
= g_ptr_array_index (cache
->fields
, f
->group_parent
);
301 g_return_if_fail (base
->ref_type
!= GO_DATA_CACHE_FIELD_TYPE_NONE
);
302 f
->offset
= base
->offset
;
305 go_data_cache_records_init (cache
, n
, offset
);
309 go_data_cache_dump_value (GOVal
const *v
)
312 g_print ("<MISSING>");
314 GOFormat
const *fmt
= go_val_get_fmt (v
);
317 char *str
= format_value (fmt
, v
, -1, NULL
);
318 g_print ("'%s'", str
);
321 g_print ("'%s'", value_peek_string (v
));
326 go_data_cache_set_val (GODataCache
*cache
,
327 int field
, unsigned int record_num
, GOVal
*v
)
332 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
333 g_return_if_fail (NULL
!= cache
->records
);
334 g_return_if_fail (0 <= field
&& (unsigned int )field
< cache
->fields
->len
);
336 f
= g_ptr_array_index (cache
->fields
, field
);
338 #ifdef GO_DEBUG_SLICERS
339 g_print ("\t[%d] ", field
);
340 go_data_cache_dump_value (v
);
343 p
= go_data_cache_records_fetch_index (cache
, record_num
) + f
->offset
;
344 switch (f
->ref_type
) {
345 case GO_DATA_CACHE_FIELD_TYPE_NONE
:
346 g_warning ("attempt to set a value for grouped/calculated field #%d : '%s'",
347 f
->indx
, f
->name
->str
);
350 case GO_DATA_CACHE_FIELD_TYPE_INLINE
:
351 memcpy (p
, &v
, sizeof (v
));
354 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
: *((guint8
*)p
) = 0; break;
355 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
: *((guint16
*)p
) = 0; break;
356 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
: *((guint32
*)p
) = 0; break;
359 g_warning ("unknown field type %d", f
->ref_type
);
362 g_warning ("Attempt to store a value in an indexed field");
366 go_data_cache_set_index (GODataCache
*cache
,
367 int field
, unsigned int record_num
, unsigned int idx
)
372 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
373 g_return_if_fail (NULL
!= cache
->records
);
374 g_return_if_fail (0 <= field
&& (unsigned int )field
< cache
->fields
->len
);
376 f
= g_ptr_array_index (cache
->fields
, field
);
378 g_return_if_fail (NULL
!= f
->indexed
);
379 g_return_if_fail (idx
< f
->indexed
->len
);
381 #ifdef GO_DEBUG_SLICERS
382 g_print ("\t(%d) %d=", field
, idx
);
383 go_data_cache_dump_value (cache
, g_ptr_array_index (f
->indexed
, idx
));
386 p
= go_data_cache_records_fetch_index (cache
, record_num
) + f
->offset
;
387 switch (f
->ref_type
) {
388 case GO_DATA_CACHE_FIELD_TYPE_NONE
:
389 g_warning ("attempt to get value from grouped/calculated field #%d : '%s'",
390 f
->indx
, f
->name
->str
);
392 case GO_DATA_CACHE_FIELD_TYPE_INLINE
: {
393 GOVal
*v
= go_val_new_empty ();
394 memcpy (p
, &v
, sizeof (v
));
397 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
: *((guint8
*)p
) = idx
+1; break;
398 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
: *((guint16
*)p
) = idx
+1; break;
399 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
: *((guint32
*)p
) = idx
+1; break;
402 g_warning ("unknown field type %d", f
->ref_type
);
407 * go_data_cache_import_done :
408 * @cache: #GODataCache
409 * @actual_records: count
411 * Tidy up after an import, and tighten up the amount of memory used to store
415 go_data_cache_import_done (GODataCache
*cache
, unsigned int actual_records
)
417 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
419 if (actual_records
< cache
->records_allocated
)
420 go_data_cache_records_set_size (cache
, actual_records
);
424 go_data_cache_num_items (GODataCache
const *cache
)
426 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), 0);
427 return cache
->records_allocated
;
431 go_data_cache_num_fields (GODataCache
const *cache
)
433 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), 0);
434 return cache
->fields
->len
;
437 go_data_cache_get_field (GODataCache
const *cache
, int i
)
439 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), NULL
);
440 g_return_val_if_fail (0 <= i
&& (unsigned int)i
< cache
->fields
->len
, NULL
);
441 return g_ptr_array_index (cache
->fields
, i
);
445 go_data_cache_get_index (GODataCache
const *cache
,
446 GODataCacheField
const *field
, unsigned int record_num
)
450 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), -1);
452 p
= go_data_cache_records_index (cache
, record_num
) + field
->offset
;
453 switch (field
->ref_type
) {
454 case GO_DATA_CACHE_FIELD_TYPE_NONE
: break;
455 case GO_DATA_CACHE_FIELD_TYPE_INLINE
: break;
456 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
: return *(guint8
*)p
- 1;
457 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
: return *(guint16
*)p
- 1;
458 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
: return *(guint32
*)p
- 1;
460 g_warning ("unknown field type %d", field
->ref_type
);
466 GODataCache
const *cache
;
467 GArray
const *field_order
;
468 } GODataCacheCompare
;
470 cb_go_data_cache_cmp (int const *a
, int const * b
,
471 GODataCacheCompare
const *info
)
473 GODataCacheField
const *f
, *base
;
474 GOVal
const *va
, *vb
;
476 unsigned int idxa
, idxb
, i
;
477 unsigned int const n
= info
->field_order
->len
;
480 for (i
= 0 ; i
< n
; i
++) {
481 f
= g_ptr_array_index (info
->cache
->fields
, g_array_index (info
->field_order
, unsigned int, i
));
482 base
= (f
->group_parent
< 0) ? f
: g_ptr_array_index (info
->cache
->fields
, f
->group_parent
);
483 pa
= go_data_cache_records_index (info
->cache
, *a
) + base
->offset
;
484 pb
= go_data_cache_records_index (info
->cache
, *b
) + base
->offset
;
485 if (base
->ref_type
!= GO_DATA_CACHE_FIELD_TYPE_INLINE
) {
486 switch (base
->ref_type
) {
487 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
:
488 idxa
= *(guint8
*)pa
;
489 idxb
= *(guint8
*)pb
;
491 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
:
492 idxa
= *(guint16
*)pa
;
493 idxb
= *(guint16
*)pb
;
495 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
:
496 idxa
= *(guint32
*)pa
;
497 idxb
= *(guint32
*)pb
;
500 g_assert_not_reached ();
502 #warning TODO : compare indicies directly, and pre-order the indexed values
503 va
= (idxa
> 0) ? g_ptr_array_index (base
->indexed
, idxa
-1) : NULL
;
504 vb
= (idxb
> 0) ? g_ptr_array_index (base
->indexed
, idxb
-1) : NULL
;
506 va
= *((GOVal
**)pa
);
507 vb
= *((GOVal
**)pb
);
510 if (f
->bucketer
.type
!= GO_VAL_BUCKET_NONE
)
511 res
= go_val_bucketer_apply (&f
->bucketer
, va
) - go_val_bucketer_apply (&f
->bucketer
, vb
);
513 res
= go_val_cmp (&va
, &vb
);
521 * go_data_cache_permute:
522 * @cache: #GODataCache
523 * @field_order: #GArray of unsigned int
524 * @permutation: #GArray of unsigned int that will be re-ordered according to the fields.
528 go_data_cache_permute (GODataCache
const *cache
,
529 GArray
const *field_order
,
532 GODataCacheCompare info
;
534 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
535 g_return_if_fail (field_order
);
536 g_return_if_fail (permutation
);
539 info
.field_order
= field_order
;
540 g_array_sort_with_data (permutation
,
541 (GCompareDataFunc
) cb_go_data_cache_cmp
, &info
);
545 go_data_cache_dump (GODataCache
*cache
,
546 GArray
const *field_order
,
547 GArray
const *permutation
)
549 GODataCacheField
const *f
, *base
;
550 unsigned int iter
, i
, j
, idx
, num_fields
;
555 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
557 num_fields
= field_order
? field_order
->len
: cache
->fields
->len
;
558 for (iter
= 0 ; iter
< cache
->records_len
; iter
++) {
560 if (NULL
== permutation
)
562 else if ((i
= g_array_index (permutation
, unsigned int, iter
)) >= 0)
566 g_print ("%d)", iter
+ 1);
568 for (j
= 0 ; j
< num_fields
; j
++) {
569 f
= g_ptr_array_index (cache
->fields
, field_order
? g_array_index (field_order
, unsigned int, j
) :j
);
570 base
= (f
->group_parent
< 0) ? f
: g_ptr_array_index (cache
->fields
, f
->group_parent
);
571 p
= go_data_cache_records_index (cache
, i
) + base
->offset
;
573 switch (base
->ref_type
) {
574 case GO_DATA_CACHE_FIELD_TYPE_NONE
:
576 case GO_DATA_CACHE_FIELD_TYPE_INLINE
:
577 memcpy (&v
, p
, sizeof (v
));
580 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
: idx
= *(guint8
*)p
; break;
581 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
: idx
= *(guint16
*)p
; break;
582 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
: idx
= *(guint32
*)p
; break;
585 g_warning ("unknown field type %d", base
->ref_type
);
592 g_return_if_fail (base
->indexed
!= NULL
&& idx
< base
->indexed
->len
);
594 v
= g_ptr_array_index (base
->indexed
, idx
);
595 g_print ("\t(%d) %d=", j
, idx
);
597 g_print ("\t[%d] ", j
);
599 if (f
->bucketer
.type
!= GO_VAL_BUCKET_NONE
) {
600 int res
= go_val_bucketer_apply (&f
->bucketer
, v
);
601 go_data_cache_dump_value (g_ptr_array_index (f
->grouped
, res
));
603 go_data_cache_dump_value (v
);