2 * go-data-cache.h : The definition of a content for a data slicer
4 * Copyright (C) 2008 Jody Goldberg (jody@gnome.org)
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) version 3.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
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 St, Fifth Floor, Boston, MA 02110-1301
22 #include <gnumeric-config.h>
23 #include <go-data-cache-impl.h>
24 #include <go-data-cache-source.h>
25 #include <go-data-cache-field-impl.h>
27 #include <gsf/gsf-impl-utils.h>
28 #include <glib/gi18n-lib.h>
31 #define GO_DATA_CACHE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_CACHE_TYPE, GODataCacheClass))
32 #define IS_GO_DATA_CACHE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_CACHE_TYPE))
33 #define GO_DATA_CACHE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_CACHE_TYPE, GODataCacheClass))
37 PROP_REFRESHED_BY
, /* char * */
38 PROP_REFRESHED_ON
, /* GOVal * */
39 PROP_REFRESH_UPGRADES
, /* bool */
40 PROP_XL_REFRESH_VER
, /* unsigned int */
41 PROP_XL_CREATED_VER
/* unsigned int */
44 /*****************************************************************/
47 go_data_cache_records_set_size (GODataCache
*cache
, unsigned int n
)
51 g_return_if_fail (cache
->record_size
> 0);
52 g_return_if_fail (n
< G_MAXUINT
/ cache
->record_size
);
54 expand
= n
- cache
->records_allocated
;
58 cache
->records
= g_realloc (cache
->records
, n
* cache
->record_size
);
60 memset (cache
->records
+ cache
->records_allocated
* cache
->record_size
, 0,
61 expand
* cache
->record_size
);
62 cache
->records_allocated
= n
;
66 go_data_cache_records_fetch_index (GODataCache
*cache
, unsigned i
)
68 if (cache
->records_allocated
<= i
) {
69 go_data_cache_records_set_size (cache
, i
+128);
70 if (cache
->records_allocated
<= i
)
74 if (cache
->records_len
<= i
)
75 cache
->records_len
= i
+ 1;
77 return go_data_cache_records_index (cache
, i
);
81 go_data_cache_records_init (GODataCache
*cache
, unsigned int n
, unsigned int record_size
)
83 cache
->record_size
= record_size
;
84 cache
->records_len
= 0;
85 go_data_cache_records_set_size (cache
, n
);
88 /*****************************************************************/
90 static GObjectClass
*parent_klass
;
92 go_data_cache_init (GODataCache
*cache
)
94 cache
->fields
= g_ptr_array_new ();
95 cache
->data_source
= NULL
;
96 cache
->records
= NULL
;
97 cache
->records_len
= cache
->records_allocated
= 0;
99 cache
->refreshed_by
= NULL
;
100 cache
->refreshed_on
= NULL
;
101 cache
->refresh_upgrades
= TRUE
;
103 cache
->XL_created_ver
= 1;
104 cache
->XL_refresh_ver
= 1;
108 go_data_cache_finalize (GObject
*obj
)
110 GODataCache
*cache
= (GODataCache
*)obj
;
113 if (NULL
!= cache
->records
) {
114 for (i
= cache
->fields
->len
; i
-- > 0 ; ) {
115 GODataCacheField
const *f
= g_ptr_array_index (cache
->fields
, i
);
116 if (GO_DATA_CACHE_FIELD_TYPE_INLINE
== f
->ref_type
) {
118 for (j
= cache
->records_len
; j
-- > 0 ; ) {
120 gpointer p
= go_data_cache_records_index (cache
, j
) + f
->offset
;
121 memcpy (&v
, p
, sizeof (v
));
126 g_free (cache
->records
);
127 cache
->records
= NULL
;
128 cache
->records_len
= cache
->records_allocated
= 0;
131 for (i
= cache
->fields
->len
; i
-- > 0 ; )
132 g_object_unref (g_ptr_array_index (cache
->fields
, i
));
133 g_ptr_array_free (cache
->fields
, TRUE
);
134 cache
->fields
= NULL
;
136 if (NULL
!= cache
->data_source
) {
137 g_object_unref (cache
->data_source
);
138 cache
->data_source
= NULL
;
141 g_free (cache
->refreshed_by
);
142 go_val_free (cache
->refreshed_on
);
144 (parent_klass
->finalize
) (obj
);
148 go_data_cache_set_property (GObject
*obj
, guint property_id
,
149 GValue
const *value
, GParamSpec
*pspec
)
151 GODataCache
*cache
= (GODataCache
*)obj
;
153 switch (property_id
) {
154 case PROP_REFRESHED_BY
:
155 g_free (cache
->refreshed_by
);
156 cache
->refreshed_by
= g_value_dup_string (value
);
158 case PROP_REFRESHED_ON
:
159 go_val_free (cache
->refreshed_on
);
160 cache
->refreshed_on
= g_value_dup_boxed (value
);
162 case PROP_REFRESH_UPGRADES
: cache
->refresh_upgrades
= g_value_get_boolean (value
); break;
163 case PROP_XL_REFRESH_VER
: cache
->XL_refresh_ver
= g_value_get_uint (value
); break;
164 case PROP_XL_CREATED_VER
: cache
->XL_created_ver
= g_value_get_uint (value
); break;
167 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
172 go_data_cache_get_property (GObject
*obj
, guint property_id
,
173 GValue
*value
, GParamSpec
*pspec
)
175 GODataCache
const *cache
= (GODataCache
const *)obj
;
176 switch (property_id
) {
177 case PROP_REFRESHED_BY
: g_value_set_string (value
, cache
->refreshed_by
); break;
178 case PROP_REFRESHED_ON
: g_value_set_boxed (value
, cache
->refreshed_on
); break;
179 case PROP_REFRESH_UPGRADES
: g_value_set_boolean (value
, cache
->refresh_upgrades
); break;
180 case PROP_XL_REFRESH_VER
: g_value_set_uint (value
, cache
->XL_refresh_ver
); break;
181 case PROP_XL_CREATED_VER
: g_value_set_uint (value
, cache
->XL_created_ver
); break;
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
188 go_data_cache_class_init (GODataCacheClass
*klass
)
190 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
191 gobject_class
->set_property
= go_data_cache_set_property
;
192 gobject_class
->get_property
= go_data_cache_get_property
;
193 gobject_class
->finalize
= go_data_cache_finalize
;
195 g_object_class_install_property (gobject_class
, PROP_REFRESHED_BY
,
196 g_param_spec_string ("refreshed-by", NULL
, NULL
, NULL
,
197 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
198 g_object_class_install_property (gobject_class
, PROP_REFRESHED_ON
,
199 g_param_spec_boxed ("refreshed-on", NULL
, NULL
,
200 GO_VAL_TYPE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
201 g_object_class_install_property (gobject_class
, PROP_REFRESH_UPGRADES
,
202 g_param_spec_boolean ("refresh-upgrades", NULL
, NULL
,
203 TRUE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
204 g_object_class_install_property (gobject_class
, PROP_XL_REFRESH_VER
,
205 g_param_spec_uint ("refresh-version", NULL
, NULL
,
206 0, G_MAXUINT
, 1, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
207 g_object_class_install_property (gobject_class
, PROP_XL_CREATED_VER
,
208 g_param_spec_uint ("created-version", NULL
, NULL
,
209 0, G_MAXUINT
, 1, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
211 parent_klass
= g_type_class_peek_parent (klass
);
214 GSF_CLASS (GODataCache
, go_data_cache
,
215 go_data_cache_class_init
, go_data_cache_init
,
219 go_data_cache_get_source (GODataCache
const *cache
)
221 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), NULL
);
222 return cache
->data_source
;
226 * go_data_cache_set_source:
227 * @cache: #GODataCache
228 * @src: #GODataCacheSource
230 * Absorbs the reference to @src.
233 go_data_cache_set_source (GODataCache
*cache
, GODataCacheSource
*src
)
235 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
236 g_return_if_fail (NULL
== src
|| IS_GO_DATA_CACHE_SOURCE (src
));
238 if (cache
->data_source
)
239 g_object_unref (cache
->data_source
);
240 cache
->data_source
= src
;
244 go_data_cache_add_field (GODataCache
*cache
, GODataCacheField
*field
)
246 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
247 g_return_if_fail (IS_GO_DATA_CACHE_FIELD (field
));
248 g_return_if_fail (field
->indx
< 0);
249 g_return_if_fail (field
->cache
== NULL
);
250 g_return_if_fail (NULL
== cache
->records
);
252 field
->indx
= cache
->fields
->len
;
253 field
->cache
= cache
;
254 g_ptr_array_add (cache
->fields
, field
);
258 * go_data_cache_import_start:
259 * @cache:#GODataCache
262 * Validate the field setup and initialize the storage.
265 go_data_cache_import_start (GODataCache
*cache
, unsigned int n
)
268 unsigned int i
, offset
= 0;
270 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
271 g_return_if_fail (NULL
== cache
->records
);
273 for (i
= 0 ; i
< cache
->fields
->len
; i
++) {
274 f
= g_ptr_array_index (cache
->fields
, i
);
276 if (NULL
== f
->indexed
|| 0 == f
->indexed
->len
) {
277 if (NULL
!= f
->grouped
&&
278 f
->group_parent
>= 0 && f
->group_parent
!= f
->indx
)
279 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_NONE
;
281 offset
+= sizeof (GOVal
*);
282 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_INLINE
;
284 } else if (f
->indexed
->len
< ((1<<8) - 1)) {
285 offset
+= sizeof (guint8
);
286 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
;
287 } else if (f
->indexed
->len
< ((1<<16) - 1)) {
288 offset
+= sizeof (guint16
);
289 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
;
291 offset
+= sizeof (guint32
);
292 f
->ref_type
= GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
;
296 for (i
= 0 ; i
< cache
->fields
->len
; i
++) {
297 f
= g_ptr_array_index (cache
->fields
, i
);
298 if (f
->group_parent
>= 0) {
299 GODataCacheField
*base
= g_ptr_array_index (cache
->fields
, f
->group_parent
);
300 g_return_if_fail (base
->ref_type
!= GO_DATA_CACHE_FIELD_TYPE_NONE
);
301 f
->offset
= base
->offset
;
304 go_data_cache_records_init (cache
, n
, offset
);
308 go_data_cache_dump_value (GOVal
const *v
)
311 g_print ("<MISSING>");
313 GOFormat
const *fmt
= go_val_get_fmt (v
);
316 char *str
= format_value (fmt
, v
, -1, NULL
);
317 g_print ("'%s'", str
);
320 g_print ("'%s'", value_peek_string (v
));
325 go_data_cache_set_val (GODataCache
*cache
,
326 int field
, unsigned int record_num
, GOVal
*v
)
331 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
332 g_return_if_fail (NULL
!= cache
->records
);
333 g_return_if_fail (0 <= field
&& (unsigned int )field
< cache
->fields
->len
);
335 f
= g_ptr_array_index (cache
->fields
, field
);
337 #ifdef GO_DEBUG_SLICERS
338 g_print ("\t[%d] ", field
);
339 go_data_cache_dump_value (v
);
342 p
= go_data_cache_records_fetch_index (cache
, record_num
) + f
->offset
;
343 switch (f
->ref_type
) {
344 case GO_DATA_CACHE_FIELD_TYPE_NONE
:
345 g_warning ("attempt to set a value for grouped/calculated field #%d : '%s'",
346 f
->indx
, f
->name
->str
);
349 case GO_DATA_CACHE_FIELD_TYPE_INLINE
:
350 memcpy (p
, &v
, sizeof (v
));
353 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
: *((guint8
*)p
) = 0; break;
354 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
: *((guint16
*)p
) = 0; break;
355 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
: *((guint32
*)p
) = 0; break;
358 g_warning ("unknown field type %d", f
->ref_type
);
361 g_warning ("Attempt to store a value in an indexed field");
365 go_data_cache_set_index (GODataCache
*cache
,
366 int field
, unsigned int record_num
, unsigned int idx
)
371 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
372 g_return_if_fail (NULL
!= cache
->records
);
373 g_return_if_fail (0 <= field
&& (unsigned int )field
< cache
->fields
->len
);
375 f
= g_ptr_array_index (cache
->fields
, field
);
377 g_return_if_fail (NULL
!= f
->indexed
);
378 g_return_if_fail (idx
< f
->indexed
->len
);
380 #ifdef GO_DEBUG_SLICERS
381 g_print ("\t(%d) %d=", field
, idx
);
382 go_data_cache_dump_value (cache
, g_ptr_array_index (f
->indexed
, idx
));
385 p
= go_data_cache_records_fetch_index (cache
, record_num
) + f
->offset
;
386 switch (f
->ref_type
) {
387 case GO_DATA_CACHE_FIELD_TYPE_NONE
:
388 g_warning ("attempt to get value from grouped/calculated field #%d : '%s'",
389 f
->indx
, f
->name
->str
);
391 case GO_DATA_CACHE_FIELD_TYPE_INLINE
: {
392 GOVal
*v
= go_val_new_empty ();
393 memcpy (p
, &v
, sizeof (v
));
396 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
: *((guint8
*)p
) = idx
+1; break;
397 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
: *((guint16
*)p
) = idx
+1; break;
398 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
: *((guint32
*)p
) = idx
+1; break;
401 g_warning ("unknown field type %d", f
->ref_type
);
406 * go_data_cache_import_done:
407 * @cache: #GODataCache
408 * @actual_records: count
410 * Tidy up after an import, and tighten up the amount of memory used to store
414 go_data_cache_import_done (GODataCache
*cache
, unsigned int actual_records
)
416 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
418 if (actual_records
< cache
->records_allocated
)
419 go_data_cache_records_set_size (cache
, actual_records
);
423 go_data_cache_num_items (GODataCache
const *cache
)
425 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), 0);
426 return cache
->records_allocated
;
430 go_data_cache_num_fields (GODataCache
const *cache
)
432 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), 0);
433 return cache
->fields
->len
;
436 go_data_cache_get_field (GODataCache
const *cache
, int i
)
438 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), NULL
);
439 g_return_val_if_fail (0 <= i
&& (unsigned int)i
< cache
->fields
->len
, NULL
);
440 return g_ptr_array_index (cache
->fields
, i
);
444 go_data_cache_get_index (GODataCache
const *cache
,
445 GODataCacheField
const *field
, unsigned int record_num
)
449 g_return_val_if_fail (IS_GO_DATA_CACHE (cache
), -1);
451 p
= go_data_cache_records_index (cache
, record_num
) + field
->offset
;
452 switch (field
->ref_type
) {
453 case GO_DATA_CACHE_FIELD_TYPE_NONE
: break;
454 case GO_DATA_CACHE_FIELD_TYPE_INLINE
: break;
455 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
: return *(guint8
*)p
- 1;
456 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
: return *(guint16
*)p
- 1;
457 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
: return *(guint32
*)p
- 1;
459 g_warning ("unknown field type %d", field
->ref_type
);
465 GODataCache
const *cache
;
466 GArray
const *field_order
;
467 } GODataCacheCompare
;
469 cb_go_data_cache_cmp (int const *a
, int const * b
,
470 GODataCacheCompare
const *info
)
472 GODataCacheField
const *f
, *base
;
473 GOVal
const *va
, *vb
;
475 unsigned int idxa
, idxb
, i
;
476 unsigned int const n
= info
->field_order
->len
;
479 for (i
= 0 ; i
< n
; i
++) {
480 f
= g_ptr_array_index (info
->cache
->fields
, g_array_index (info
->field_order
, unsigned int, i
));
481 base
= (f
->group_parent
< 0) ? f
: g_ptr_array_index (info
->cache
->fields
, f
->group_parent
);
482 pa
= go_data_cache_records_index (info
->cache
, *a
) + base
->offset
;
483 pb
= go_data_cache_records_index (info
->cache
, *b
) + base
->offset
;
484 if (base
->ref_type
!= GO_DATA_CACHE_FIELD_TYPE_INLINE
) {
485 switch (base
->ref_type
) {
486 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
:
487 idxa
= *(guint8
*)pa
;
488 idxb
= *(guint8
*)pb
;
490 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
:
491 idxa
= *(guint16
*)pa
;
492 idxb
= *(guint16
*)pb
;
494 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
:
495 idxa
= *(guint32
*)pa
;
496 idxb
= *(guint32
*)pb
;
499 g_assert_not_reached ();
501 #warning TODO : compare indicies directly, and pre-order the indexed values
502 va
= (idxa
> 0) ? g_ptr_array_index (base
->indexed
, idxa
-1) : NULL
;
503 vb
= (idxb
> 0) ? g_ptr_array_index (base
->indexed
, idxb
-1) : NULL
;
505 va
= *((GOVal
**)pa
);
506 vb
= *((GOVal
**)pb
);
509 if (f
->bucketer
.type
!= GO_VAL_BUCKET_NONE
)
510 res
= go_val_bucketer_apply (&f
->bucketer
, va
) - go_val_bucketer_apply (&f
->bucketer
, vb
);
512 res
= go_val_cmp (&va
, &vb
);
520 * go_data_cache_permute:
521 * @cache: #GODataCache
522 * @field_order: #GArray of unsigned int
523 * @permutation: #GArray of unsigned int that will be re-ordered according to the fields.
527 go_data_cache_permute (GODataCache
const *cache
,
528 GArray
const *field_order
,
531 GODataCacheCompare info
;
533 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
534 g_return_if_fail (field_order
);
535 g_return_if_fail (permutation
);
538 info
.field_order
= field_order
;
539 g_array_sort_with_data (permutation
,
540 (GCompareDataFunc
) cb_go_data_cache_cmp
, &info
);
544 go_data_cache_dump (GODataCache
*cache
,
545 GArray
const *field_order
,
546 GArray
const *permutation
)
548 GODataCacheField
const *f
, *base
;
549 unsigned int iter
, i
, j
, idx
, num_fields
;
554 g_return_if_fail (IS_GO_DATA_CACHE (cache
));
556 num_fields
= field_order
? field_order
->len
: cache
->fields
->len
;
557 for (iter
= 0 ; iter
< cache
->records_len
; iter
++) {
559 if (NULL
== permutation
)
561 else if ((i
= g_array_index (permutation
, unsigned int, iter
)) >= 0)
565 g_print ("%d)", iter
+ 1);
567 for (j
= 0 ; j
< num_fields
; j
++) {
568 f
= g_ptr_array_index (cache
->fields
, field_order
? g_array_index (field_order
, unsigned int, j
) :j
);
569 base
= (f
->group_parent
< 0) ? f
: g_ptr_array_index (cache
->fields
, f
->group_parent
);
570 p
= go_data_cache_records_index (cache
, i
) + base
->offset
;
572 switch (base
->ref_type
) {
573 case GO_DATA_CACHE_FIELD_TYPE_NONE
:
575 case GO_DATA_CACHE_FIELD_TYPE_INLINE
:
576 memcpy (&v
, p
, sizeof (v
));
579 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I8
: idx
= *(guint8
*)p
; break;
580 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I16
: idx
= *(guint16
*)p
; break;
581 case GO_DATA_CACHE_FIELD_TYPE_INDEXED_I32
: idx
= *(guint32
*)p
; break;
584 g_warning ("unknown field type %d", base
->ref_type
);
591 g_return_if_fail (base
->indexed
!= NULL
&& idx
< base
->indexed
->len
);
593 v
= g_ptr_array_index (base
->indexed
, idx
);
594 g_print ("\t(%d) %d=", j
, idx
);
596 g_print ("\t[%d] ", j
);
598 if (f
->bucketer
.type
!= GO_VAL_BUCKET_NONE
) {
599 int res
= go_val_bucketer_apply (&f
->bucketer
, v
);
600 go_data_cache_dump_value (g_ptr_array_index (f
->grouped
, res
));
602 go_data_cache_dump_value (v
);