Coverage Report

Created: 2026-01-05 06:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/graph/attributes.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
4
   334 Harvard street, Cambridge, MA 02139 USA
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 2 of the License, or
9
   (at your option) any later version.
10
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.
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
*/
22
23
#include "igraph_attributes.h"
24
#include "igraph_memory.h"
25
26
#include "graph/attributes.h"
27
#include "internal/hacks.h" /* strdup */
28
29
#include <string.h>
30
#include <stdarg.h>
31
32
/**
33
 * \section about_attributes
34
 *
35
 * <para>Attributes are numbers, boolean values or strings associated with
36
 * the vertices or edges of a graph, or with the graph itself. E.g. you may
37
 * label vertices with symbolic names or attach numeric weights to the edges
38
 * of a graph. In addition to these three basic types, a custom object
39
 * type is supported as well.</para>
40
 *
41
 * <para>igraph attributes are designed to be flexible and extensible.
42
 * In igraph attributes are implemented via an interface abstraction:
43
 * any type implementing the functions in the interface can be used
44
 * for storing vertex, edge and graph attributes. This means that
45
 * different attribute implementations can be used together with
46
 * igraph. This is reasonable: if igraph is used from Python attributes can be
47
 * of any Python type, from R all R types are allowed. There is also an
48
 * experimental attribute implementation to be used when programming
49
 * in C, but by default it is currently turned off.</para>
50
 *
51
 * <para>First we briefly look over how attribute handlers can be
52
 * implemented. This is not something a user does every day. It is
53
 * rather typically the job of the high level interface writers. (But
54
 * it is possible to write an interface without implementing
55
 * attributes.) Then we show the experimental C attribute handler.</para>
56
 */
57
58
/**
59
 * \section about_attribute_table
60
 * <para>It is possible to attach an attribute handling
61
 * interface to \a igraph. This is simply a table of functions, of
62
 * type \ref igraph_attribute_table_t. These functions are invoked to
63
 * notify the attribute handling code about the structural changes in
64
 * a graph. See the documentation of this type for details.</para>
65
 *
66
 * <para>By default there is no attribute interface attached to \a igraph.
67
 * To attach one, call \ref igraph_set_attribute_table with your new
68
 * table. This is normally done on program startup, and is kept untouched
69
 * for the program's lifetime. It must be done before any graph object
70
 * is created, as graphs created with a given attribute handler
71
 * cannot be manipulated while a different attribute handler is
72
 * active.</para>
73
 */
74
75
/**
76
 * \section about_attribute_record
77
 *
78
 * <para>Functions in the attribute handler interface may refer to
79
 * \em "attribute records" or \em "attribute record lists". An attribute record
80
 * is simply a triplet consisting of an attribute name, an attribute type and
81
 * a vector containing the values of the attribute. Attribute record lists are
82
 * typed containers that contain a sequence of attribute records. Attribute
83
 * record lists own the attribute records that they contain, and similarly,
84
 * attribute records own the vectors contained in them. Destroying an attribute
85
 * record destroys the vector of values inside it, and destroying an attribute
86
 * record list destroys all attribute records in the list.</para>
87
 */
88
89
/**
90
 * \section about_attribute_combination
91
 *
92
 * <para>Several graph operations may collapse multiple vertices or edges into
93
 * a single one. Attribute combination lists are used to indicate to the attribute
94
 * handler how to combine the attributes of the original vertices or edges and
95
 * how to derive the final attribute value that is to be assigned to the collapsed
96
 * vertex or edge. For example, \ref igraph_simplify() removes loops and combines
97
 * multiple edges into a single one; in case of a graph with an edge attribute
98
 * named \c weight the attribute combination list can tell the attribute handler
99
 * whether the weight of a collapsed edge should be the sum, the mean or some other
100
 * function of the weights of the original edges that were collapsed into one.</para>
101
 *
102
 * <para>One attribute combination list may contain several attribute combination
103
 * records, one for each vertex or edge attribute that is to be handled during the
104
 * operation.</para>
105
 */
106
107
static void igraph_i_attribute_record_set_type(
108
    igraph_attribute_record_t *attr, igraph_attribute_type_t type, void *ptr
109
);
110
static void igraph_i_attribute_record_destroy_values(igraph_attribute_record_t *attr);
111
112
/**
113
 * \function igraph_attribute_record_init
114
 * \brief Initializes an attribute record with a given name and type.
115
 *
116
 * \param attr  the attribute record to initialize
117
 * \param name  name of the attribute
118
 * \param type  type of the attribute
119
 * \return Error code:
120
 *         \c IGRAPH_ENOMEM if there is not enough memory.
121
 *
122
 * Time complexity: O(1).
123
 */
124
igraph_error_t igraph_attribute_record_init(
125
    igraph_attribute_record_t *attr, const char *name, igraph_attribute_type_t type
126
0
) {
127
0
    attr->name = NULL;
128
0
    attr->type = IGRAPH_ATTRIBUTE_UNSPECIFIED;
129
0
    attr->value.as_raw = NULL;
130
0
    attr->default_value.string = NULL;
131
132
0
    IGRAPH_CHECK(igraph_attribute_record_set_name(attr, name));
133
0
    IGRAPH_FINALLY(igraph_free, attr->name);
134
0
    IGRAPH_CHECK(igraph_attribute_record_set_type(attr, type));
135
0
    IGRAPH_FINALLY_CLEAN(1);
136
137
0
    return IGRAPH_SUCCESS;
138
0
}
139
140
/**
141
 * \function igraph_attribute_record_init_copy
142
 * \brief Initializes an attribute record by copying another record.
143
 *
144
 * </para><para>
145
 * Copies made by this function are deep copies: a full copy of the value
146
 * vector contained in the record is placed in the new record so they become
147
 * independent of each other.
148
 *
149
 * \param to    the attribute record to initialize
150
 * \param from  the attribute record to copy data from
151
 * \return Error code:
152
 *         \c IGRAPH_ENOMEM if there is not enough memory.
153
 *
154
 * Time complexity: operating system dependent, usually O(n), where n is the
155
 * size of the value vector in the attribute record.
156
 */
157
igraph_error_t igraph_attribute_record_init_copy(
158
    igraph_attribute_record_t *to, const igraph_attribute_record_t *from
159
0
) {
160
0
    IGRAPH_CHECK(igraph_attribute_record_init(to, from->name, from->type));
161
162
0
    switch (from->type) {
163
0
        case IGRAPH_ATTRIBUTE_NUMERIC:
164
0
            IGRAPH_CHECK(igraph_vector_update(to->value.as_vector, from->value.as_vector));
165
0
            break;
166
167
0
        case IGRAPH_ATTRIBUTE_STRING:
168
0
            IGRAPH_CHECK(igraph_strvector_update(to->value.as_strvector, from->value.as_strvector));
169
0
            break;
170
171
0
        case IGRAPH_ATTRIBUTE_BOOLEAN:
172
0
            IGRAPH_CHECK(igraph_vector_bool_update(to->value.as_vector_bool, from->value.as_vector_bool));
173
0
            break;
174
175
0
        case IGRAPH_ATTRIBUTE_UNSPECIFIED:
176
0
            break;
177
178
0
        default:
179
0
            break;
180
0
    }
181
182
0
    return IGRAPH_SUCCESS;
183
0
}
184
185
0
static void igraph_i_attribute_record_destroy_values(igraph_attribute_record_t *attr) {
186
0
    IGRAPH_ASSERT(attr != NULL);
187
188
0
    if (attr->value.as_raw) {
189
0
        switch (attr->type) {
190
0
            case IGRAPH_ATTRIBUTE_NUMERIC:
191
0
                igraph_vector_destroy(attr->value.as_vector);
192
0
                break;
193
194
0
            case IGRAPH_ATTRIBUTE_STRING:
195
0
                igraph_strvector_destroy(attr->value.as_strvector);
196
0
                break;
197
198
0
            case IGRAPH_ATTRIBUTE_BOOLEAN:
199
0
                igraph_vector_bool_destroy(attr->value.as_vector_bool);
200
0
                break;
201
202
0
            default:
203
0
                break;
204
0
        }
205
206
0
        igraph_free(attr->value.as_raw);
207
0
        attr->value.as_raw = NULL;
208
0
    }
209
210
0
    switch (attr->type) {
211
0
        case IGRAPH_ATTRIBUTE_NUMERIC:
212
0
            attr->default_value.numeric = 0;
213
0
            break;
214
215
0
        case IGRAPH_ATTRIBUTE_STRING:
216
0
            if (attr->default_value.string) {
217
0
                igraph_free(attr->default_value.string);
218
0
                attr->default_value.string = NULL;
219
0
            }
220
0
            break;
221
222
0
        case IGRAPH_ATTRIBUTE_BOOLEAN:
223
0
            attr->default_value.boolean = 0;
224
0
            break;
225
226
0
        default:
227
0
            break;
228
0
    }
229
230
0
    attr->type = IGRAPH_ATTRIBUTE_UNSPECIFIED;
231
0
}
232
233
/**
234
 * \function igraph_attribute_record_destroy
235
 * \brief Destroys an attribute record.
236
 *
237
 * \param attr  the previously initialized attribute record to destroy.
238
 *
239
 * Time complexity: operating system dependent.
240
 */
241
0
void igraph_attribute_record_destroy(igraph_attribute_record_t *attr) {
242
0
    igraph_i_attribute_record_destroy_values(attr);
243
244
0
    if (attr->name) {
245
0
        igraph_free(attr->name);
246
0
        attr->name = NULL;
247
0
    }
248
0
}
249
250
/**
251
 * \function igraph_attribute_record_check_type
252
 * \brief Checks whether the type of the attribute record is equal to an expected type.
253
 *
254
 * \param attr  the attribute record to test
255
 * \param type  the expected type of the attribute record
256
 * \return Error code:
257
 *         \c IGRAPH_EINVAL if the type of the attribute record is not equal to
258
 *         the expected type
259
 *
260
 * Time complexity: O(1).
261
 */
262
igraph_error_t igraph_attribute_record_check_type(
263
    const igraph_attribute_record_t *attr, igraph_attribute_type_t type
264
0
) {
265
0
    if (type != attr->type) {
266
0
        switch (type) {
267
0
            case IGRAPH_ATTRIBUTE_STRING:
268
0
                IGRAPH_ERROR("String attribute expected.", IGRAPH_EINVAL);
269
0
                break;
270
0
            case IGRAPH_ATTRIBUTE_NUMERIC:
271
0
                IGRAPH_ERROR("Numeric attribute expected.", IGRAPH_EINVAL);
272
0
                break;
273
0
            case IGRAPH_ATTRIBUTE_BOOLEAN:
274
0
                IGRAPH_ERROR("Boolean attribute expected.", IGRAPH_EINVAL);
275
0
                break;
276
0
            case IGRAPH_ATTRIBUTE_OBJECT:
277
0
                IGRAPH_ERROR("Object attribute expected.", IGRAPH_EINVAL);
278
0
                break;
279
0
            default:
280
0
                IGRAPH_ERROR("Attribute with unknown type expected.", IGRAPH_EINVAL);
281
0
                break;
282
0
        }
283
0
    }
284
285
0
    return IGRAPH_SUCCESS;
286
0
}
287
288
/**
289
 * \function igraph_attribute_record_size
290
 * \brief Returns the size of the value vector in an attribute record.
291
 *
292
 * \param attr      the attribute record to query
293
 * \return the number of elements in the value vector of the attribute record
294
 */
295
0
igraph_int_t igraph_attribute_record_size(const igraph_attribute_record_t *attr) {
296
0
    IGRAPH_ASSERT(attr != NULL);
297
298
0
    switch (attr->type) {
299
0
        case IGRAPH_ATTRIBUTE_NUMERIC:
300
0
            return igraph_vector_size(attr->value.as_vector);
301
302
0
        case IGRAPH_ATTRIBUTE_STRING:
303
0
            return igraph_strvector_size(attr->value.as_strvector);
304
305
0
        case IGRAPH_ATTRIBUTE_BOOLEAN:
306
0
            return igraph_vector_bool_size(attr->value.as_vector_bool);
307
308
0
        case IGRAPH_ATTRIBUTE_UNSPECIFIED:
309
0
            return 0;
310
311
0
        default:
312
0
            IGRAPH_ERRORF("Unsupported attribute type: %d", IGRAPH_EINVAL, (int) attr->type);
313
0
    }
314
0
}
315
316
/**
317
 * \function igraph_attribute_record_resize
318
 * \brief Resizes the value vector in an attribute record.
319
 *
320
 * </para><para>When the value vector is shorter than the desired length, it
321
 * will be expanded with \c IGRAPH_NAN for numeric vectors, \c false for Boolean
322
 * vectors and empty strings for string vectors.
323
 *
324
 * \param attr      the attribute record to update
325
 * \param new_size  the new size of the value vector
326
 * \return Error code:
327
 *         \c IGRAPH_ENOMEM if there is not enough memory.
328
 *         \c IGRAPH_EINVAL if the type of the attribute record is not specified yet.
329
 */
330
igraph_error_t igraph_attribute_record_resize(
331
    igraph_attribute_record_t *attr, igraph_int_t new_size
332
0
) {
333
0
    igraph_int_t i;
334
0
    igraph_vector_t *vec;
335
0
    igraph_vector_bool_t *log;
336
0
    igraph_strvector_t *str;
337
338
0
    IGRAPH_ASSERT(attr != NULL);
339
340
0
    switch (attr->type) {
341
342
0
        case IGRAPH_ATTRIBUTE_NUMERIC:
343
0
            vec = attr->value.as_vector;
344
0
            i = igraph_vector_size(vec);
345
0
            IGRAPH_CHECK(igraph_vector_resize(vec, new_size));
346
0
            while (i < new_size) {
347
0
                VECTOR(*vec)[i++] = attr->default_value.numeric;
348
0
            }
349
0
            break;
350
351
0
        case IGRAPH_ATTRIBUTE_BOOLEAN:
352
0
            log = attr->value.as_vector_bool;
353
0
            i = igraph_vector_bool_size(log);
354
0
            IGRAPH_CHECK(igraph_vector_bool_resize(log, new_size));
355
0
            while (i < new_size) {
356
0
                VECTOR(*log)[i++] = attr->default_value.boolean;
357
0
            }
358
0
            break;
359
360
0
        case IGRAPH_ATTRIBUTE_STRING:
361
0
            str = attr->value.as_strvector;
362
0
            if (attr->default_value.string == 0 || (*attr->default_value.string == 0)) {
363
0
                IGRAPH_CHECK(igraph_strvector_resize(str, new_size));
364
0
            } else {
365
0
                i = igraph_strvector_size(str);
366
0
                IGRAPH_CHECK(igraph_strvector_resize(str, new_size));
367
0
                while (i < new_size) {
368
0
                    IGRAPH_CHECK(igraph_strvector_set(str, i++, attr->default_value.string));
369
0
                }
370
0
            }
371
0
            break;
372
373
0
        case IGRAPH_ATTRIBUTE_UNSPECIFIED:
374
0
            IGRAPH_ERROR("Attribute record has no type yet.", IGRAPH_EINVAL);
375
0
            break;
376
377
0
        default:
378
0
            IGRAPH_ERRORF("Unsupported attribute type: %d", IGRAPH_EINVAL, (int) attr->type);
379
0
    }
380
381
0
    return IGRAPH_SUCCESS;
382
0
}
383
384
/**
385
 * \function igraph_attribute_record_set_default_numeric
386
 * \brief Sets the default value of the attribute to the given number.
387
 *
388
 * </para><para>
389
 * This function must be called for numeric attribute records only. When not
390
 * specified, the default value of numeric attributes is NaN.
391
 *
392
 * \param attr   the attribute record to update
393
 * \param value  the new default value
394
 * \return Error code:
395
 *         \c IGRAPH_EINVAL if the attribute record has a non-numeric type
396
 */
397
igraph_error_t igraph_attribute_record_set_default_numeric(
398
    igraph_attribute_record_t *attr, igraph_real_t value
399
0
) {
400
0
    if (attr->type != IGRAPH_ATTRIBUTE_NUMERIC) {
401
0
        return IGRAPH_EINVAL;
402
0
    }
403
404
0
    attr->default_value.numeric = value;
405
0
    return IGRAPH_SUCCESS;
406
0
}
407
408
/**
409
 * \function igraph_attribute_record_set_default_boolean
410
 * \brief Sets the default value of the attribute to the given logical value.
411
 *
412
 * </para><para>
413
 * This function must be called for Boolean attribute records only. When not
414
 * specified, the default value of Boolean attributes is \c false.
415
 *
416
 * \param attr   the attribute record to update
417
 * \param value  the new default value
418
 * \return Error code:
419
 *         \c IGRAPH_EINVAL if the attribute record is not of Boolean type
420
 */
421
IGRAPH_EXPORT igraph_error_t igraph_attribute_record_set_default_boolean(
422
    igraph_attribute_record_t *attr, igraph_bool_t value
423
0
) {
424
0
    if (attr->type != IGRAPH_ATTRIBUTE_BOOLEAN) {
425
0
        return IGRAPH_EINVAL;
426
0
    }
427
428
0
    attr->default_value.boolean = value;
429
0
    return IGRAPH_SUCCESS;
430
0
}
431
432
/**
433
 * \function igraph_attribute_record_set_default_string
434
 * \brief Sets the default value of the attribute to the given string.
435
 *
436
 * </para><para>
437
 * This function must be called for string attribute records only. When not
438
 * specified, the default value of string attributes is an empty string.
439
 *
440
 * \param attr   the attribute record to update
441
 * \param value  the new default value. \c NULL means an empty string.
442
 *
443
 * \return Error code:
444
 *         \c IGRAPH_ENOMEM if there is not enough memory
445
 *         \c IGRAPH_EINVAL if the attribute record is not of string type
446
 */
447
IGRAPH_EXPORT igraph_error_t igraph_attribute_record_set_default_string(
448
    igraph_attribute_record_t *attr, const char* value
449
0
) {
450
0
    char* copy;
451
452
0
    if (attr->type != IGRAPH_ATTRIBUTE_STRING) {
453
0
        return IGRAPH_EINVAL;
454
0
    }
455
456
0
    if (value && (*value != 0)) {
457
0
        copy = strdup(value);
458
0
        IGRAPH_CHECK_OOM(copy, "Insufficient memory to duplicate default value.");
459
0
    } else {
460
0
        copy = NULL;
461
0
    }
462
463
0
    if (attr->default_value.string) {
464
0
        igraph_free(attr->default_value.string);
465
0
    }
466
0
    attr->default_value.string = copy;
467
468
0
    return IGRAPH_SUCCESS;
469
0
}
470
471
472
/**
473
 * \function igraph_attribute_record_set_name
474
 * \brief Sets the attribute name in an attribute record.
475
 *
476
 * \param attr  the attribute record to update
477
 * \param name  the new name
478
 * \return Error code:
479
 *         \c IGRAPH_ENOMEM if there is not enough memory.
480
 */
481
igraph_error_t igraph_attribute_record_set_name(
482
    igraph_attribute_record_t *attr, const char *name
483
0
) {
484
0
    char *new_name;
485
486
0
    IGRAPH_ASSERT(attr != NULL);
487
488
0
    if (name != NULL) {
489
0
        new_name = strdup(name);
490
0
        IGRAPH_CHECK_OOM(new_name, "Insufficient memory for allocating attribute name.");
491
0
    } else {
492
0
        new_name = NULL;
493
0
    }
494
495
0
    if (attr->name) {
496
0
        igraph_free(attr->name);
497
0
    }
498
499
0
    attr->name = new_name;
500
501
0
    return IGRAPH_SUCCESS;
502
0
}
503
504
static void igraph_i_attribute_record_set_type(
505
    igraph_attribute_record_t *attr, igraph_attribute_type_t type, void *ptr
506
0
) {
507
0
    bool type_changed = attr->type != type;
508
509
0
    if (type_changed || attr->value.as_raw != ptr) {
510
0
        igraph_i_attribute_record_destroy_values(attr);
511
0
        attr->type = type;
512
0
        attr->value.as_raw = ptr;
513
0
    }
514
515
0
    if (type_changed && type == IGRAPH_ATTRIBUTE_NUMERIC) {
516
0
        IGRAPH_ASSERT(
517
0
            igraph_attribute_record_set_default_numeric(attr, IGRAPH_NAN) == IGRAPH_SUCCESS
518
0
        );
519
0
    }
520
0
}
521
522
/**
523
 * \function igraph_attribute_record_set_type
524
 * \brief Sets the type of an attribute record.
525
 *
526
 * </para><para>
527
 * When the new type being set is different from the old type, any values already
528
 * stored in the attribute record will be destroyed and a new, empty attribute
529
 * value vector will be allocated. When the new type is the same as the old
530
 * type, this function is a no-op.
531
 *
532
 * \param attr  the attribute record to update
533
 * \param type  the new type
534
 * \return Error code:
535
 *         \c IGRAPH_ENOMEM if there is not enough memory.
536
 */
537
igraph_error_t igraph_attribute_record_set_type(
538
    igraph_attribute_record_t *attr, igraph_attribute_type_t type
539
0
) {
540
0
    void *ptr;
541
542
0
    IGRAPH_ASSERT(attr != NULL);
543
544
0
    if (attr->type != type) {
545
0
        switch (type) {
546
0
            case IGRAPH_ATTRIBUTE_NUMERIC: {
547
0
                igraph_vector_t *vec = IGRAPH_CALLOC(1, igraph_vector_t);
548
0
                IGRAPH_CHECK_OOM(vec, "Insufficient memory for attribute record.");
549
0
                IGRAPH_FINALLY(igraph_free, vec);
550
0
                IGRAPH_VECTOR_INIT_FINALLY(vec, 0);
551
0
                ptr = vec;
552
0
            }
553
0
            break;
554
555
0
            case IGRAPH_ATTRIBUTE_STRING: {
556
0
                igraph_strvector_t *strvec = IGRAPH_CALLOC(1, igraph_strvector_t);
557
0
                IGRAPH_CHECK_OOM(strvec, "Insufficient memory for attribute record.");
558
0
                IGRAPH_FINALLY(igraph_free, strvec);
559
0
                IGRAPH_STRVECTOR_INIT_FINALLY(strvec, 0);
560
0
                ptr = strvec;
561
0
            }
562
0
            break;
563
564
0
            case IGRAPH_ATTRIBUTE_BOOLEAN: {
565
0
                igraph_vector_bool_t *boolvec = IGRAPH_CALLOC(1, igraph_vector_bool_t);
566
0
                IGRAPH_CHECK_OOM(boolvec, "Insufficient memory for attribute record.");
567
0
                IGRAPH_FINALLY(igraph_free, boolvec);
568
0
                IGRAPH_VECTOR_BOOL_INIT_FINALLY(boolvec, 0);
569
0
                ptr = boolvec;
570
0
            }
571
0
            break;
572
573
0
            default:
574
0
                IGRAPH_FATALF("Unsupported attribute type: %d.", (int) type);
575
0
        }
576
577
0
        igraph_i_attribute_record_set_type(attr, type, ptr);
578
0
        IGRAPH_FINALLY_CLEAN(2);
579
0
    }
580
581
0
    return IGRAPH_SUCCESS;
582
0
}
583
584
#define ATTRIBUTE_RECORD_LIST
585
#define BASE_ATTRIBUTE_RECORD
586
#define CUSTOM_INIT_DESTROY
587
#include "igraph_pmt.h"
588
#include "core/typed_list.pmt"
589
#include "igraph_pmt_off.h"
590
#undef CUSTOM_INIT_DESTROY
591
#undef BASE_ATTRIBUTE_RECORD
592
#undef ATTRIBUTE_RECORD_LIST
593
594
static igraph_error_t igraph_i_attribute_record_list_init_item(
595
    const igraph_attribute_record_list_t* list, igraph_attribute_record_t* item
596
0
) {
597
0
    IGRAPH_UNUSED(list);
598
0
    return igraph_attribute_record_init(item, NULL, IGRAPH_ATTRIBUTE_UNSPECIFIED);
599
0
}
600
601
static igraph_error_t igraph_i_attribute_record_list_copy_item(
602
    igraph_attribute_record_t* dest, const igraph_attribute_record_t* source
603
0
) {
604
0
    return igraph_attribute_record_init_copy(dest, source);
605
0
}
606
607
0
static void igraph_i_attribute_record_list_destroy_item(igraph_attribute_record_t* item) {
608
0
    igraph_attribute_record_destroy(item);
609
0
}
610
611
/* Should you ever want to have a thread-local attribute handler table, prepend
612
 * IGRAPH_THREAD_LOCAL to the following declaration and #include "config.h". */
613
igraph_attribute_table_t *igraph_i_attribute_table = NULL;
614
615
igraph_error_t igraph_i_attribute_init(
616
    igraph_t *graph, const igraph_attribute_record_list_t *attr
617
178k
) {
618
178k
    graph->attr = NULL;
619
178k
    if (igraph_i_attribute_table) {
620
0
        IGRAPH_CHECK(igraph_i_attribute_table->init(graph, attr));
621
0
        if (graph->attr == NULL) {
622
0
            IGRAPH_ERROR("Attribute handler did not initialize attr pointer", IGRAPH_FAILURE);
623
0
        }
624
0
    }
625
178k
    return IGRAPH_SUCCESS;
626
178k
}
627
628
320k
void igraph_i_attribute_destroy(igraph_t *graph) {
629
320k
    if (graph->attr && igraph_i_attribute_table) {
630
0
        igraph_i_attribute_table->destroy(graph);
631
0
    }
632
320k
    graph->attr = NULL;
633
320k
}
634
635
igraph_error_t igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga,
636
104k
                            igraph_bool_t va, igraph_bool_t ea) {
637
104k
    igraph_i_attribute_destroy(to);
638
104k
    if (from->attr && igraph_i_attribute_table) {
639
0
        IGRAPH_CHECK(igraph_i_attribute_table->copy(to, from, ga, va, ea));
640
0
        if (to->attr == NULL) {
641
0
            IGRAPH_ERROR("Attribute handler did not initialize attr pointer", IGRAPH_FAILURE);
642
0
        }
643
0
    }
644
104k
    return IGRAPH_SUCCESS;
645
104k
}
646
647
igraph_error_t igraph_i_attribute_add_vertices(
648
    igraph_t *graph, igraph_int_t nv, const igraph_attribute_record_list_t *attr
649
0
) {
650
0
    if (igraph_i_attribute_table) {
651
0
        return igraph_i_attribute_table->add_vertices(graph, nv, attr);
652
0
    } else {
653
0
        return IGRAPH_SUCCESS;
654
0
    }
655
0
}
656
657
igraph_error_t igraph_i_attribute_permute_vertices(const igraph_t *graph,
658
                                        igraph_t *newgraph,
659
13.1k
                                        const igraph_vector_int_t *idx) {
660
    /* graph and newgraph may be the same, in which case we need to support
661
     * in-place operations. If they are _not_ the same, it is assumed that the
662
     * new graph has no vertex attributes yet */
663
13.1k
    if (igraph_i_attribute_table) {
664
0
        return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx);
665
13.1k
    } else {
666
13.1k
        return IGRAPH_SUCCESS;
667
13.1k
    }
668
13.1k
}
669
670
igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph,
671
                                        igraph_t *newgraph,
672
                                        const igraph_vector_int_list_t *merges,
673
0
                                        const igraph_attribute_combination_t *comb) {
674
    /* It is assumed that the two graphs are not the same and that the new
675
     * graph has no vertex attributes yet. We cannot assert the latter but we
676
     * can assert the former */
677
0
    IGRAPH_ASSERT(graph != newgraph);
678
0
    if (igraph_i_attribute_table) {
679
0
        return igraph_i_attribute_table->combine_vertices(graph, newgraph,
680
0
                merges,
681
0
                comb);
682
0
    } else {
683
0
        return IGRAPH_SUCCESS;
684
0
    }
685
0
}
686
687
igraph_error_t igraph_i_attribute_add_edges(
688
    igraph_t *graph, const igraph_vector_int_t *edges,
689
    const igraph_attribute_record_list_t *attr
690
0
) {
691
0
    if (igraph_i_attribute_table) {
692
0
        return igraph_i_attribute_table->add_edges(graph, edges, attr);
693
0
    } else {
694
0
        return IGRAPH_SUCCESS;
695
0
    }
696
0
}
697
698
igraph_error_t igraph_i_attribute_permute_edges(const igraph_t *graph,
699
                                     igraph_t *newgraph,
700
23.7k
                                     const igraph_vector_int_t *idx) {
701
    /* graph and newgraph may be the same, in which case we need to support
702
     * in-place operations. If they are _not_ the same, it is assumed that the
703
     * new graph has no edge attributes yet */
704
23.7k
    if (igraph_i_attribute_table) {
705
0
        return igraph_i_attribute_table->permute_edges(graph, newgraph, idx);
706
23.7k
    } else {
707
23.7k
        return IGRAPH_SUCCESS;
708
23.7k
    }
709
23.7k
}
710
711
igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph,
712
                                     igraph_t *newgraph,
713
                                     const igraph_vector_int_list_t *merges,
714
0
                                     const igraph_attribute_combination_t *comb) {
715
    /* It is assumed that the two graphs are not the same and that the new
716
     * graph has no eedge attributes yet. We cannot assert the latter but we
717
     * can assert the former */
718
0
    IGRAPH_ASSERT(graph != newgraph);
719
0
    if (igraph_i_attribute_table) {
720
0
        return igraph_i_attribute_table->combine_edges(graph, newgraph,
721
0
                merges,
722
0
                comb);
723
0
    } else {
724
0
        return IGRAPH_SUCCESS;
725
0
    }
726
0
}
727
728
igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph,
729
                                igraph_strvector_t *gnames,
730
                                igraph_vector_int_t *gtypes,
731
                                igraph_strvector_t *vnames,
732
                                igraph_vector_int_t *vtypes,
733
                                igraph_strvector_t *enames,
734
0
                                igraph_vector_int_t *etypes) {
735
0
    if (igraph_i_attribute_table) {
736
0
        return igraph_i_attribute_table->get_info(graph, gnames, gtypes,
737
0
                vnames, vtypes,
738
0
                enames, etypes);
739
0
    } else {
740
0
        return IGRAPH_SUCCESS;
741
0
    }
742
0
}
743
744
igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph,
745
        igraph_attribute_elemtype_t type,
746
0
        const char *name) {
747
0
    if (igraph_i_attribute_table) {
748
0
        return igraph_i_attribute_table->has_attr(graph, type, name);
749
0
    } else {
750
0
        return IGRAPH_SUCCESS;
751
0
    }
752
0
}
753
754
igraph_error_t igraph_i_attribute_get_type(const igraph_t *graph,
755
                               igraph_attribute_type_t *type,
756
                               igraph_attribute_elemtype_t elemtype,
757
0
                               const char *name) {
758
0
    if (igraph_i_attribute_table) {
759
0
        return igraph_i_attribute_table->get_type(graph, type, elemtype, name);
760
0
    } else {
761
0
        return IGRAPH_SUCCESS;
762
0
    }
763
764
0
}
765
766
igraph_error_t igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph,
767
        const char *name,
768
0
        igraph_vector_t *value) {
769
0
    igraph_vector_clear(value);
770
0
    if (igraph_i_attribute_table) {
771
0
        return igraph_i_attribute_table->get_numeric_graph_attr(graph, name, value);
772
0
    } else {
773
0
        return IGRAPH_SUCCESS;
774
0
    }
775
0
}
776
777
igraph_error_t igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph,
778
        const char *name,
779
        igraph_vs_t vs,
780
0
        igraph_vector_t *value) {
781
0
    igraph_vector_clear(value);
782
0
    if (igraph_i_attribute_table) {
783
0
        return igraph_i_attribute_table->get_numeric_vertex_attr(graph, name, vs, value);
784
0
    } else {
785
0
        return IGRAPH_SUCCESS;
786
0
    }
787
0
}
788
789
igraph_error_t igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph,
790
        const char *name,
791
        igraph_es_t es,
792
0
        igraph_vector_t *value) {
793
0
    igraph_vector_clear(value);
794
0
    if (igraph_i_attribute_table) {
795
0
        return igraph_i_attribute_table->get_numeric_edge_attr(graph, name, es, value);
796
0
    } else {
797
0
        return IGRAPH_SUCCESS;
798
0
    }
799
0
}
800
801
igraph_error_t igraph_i_attribute_get_string_graph_attr(const igraph_t *graph,
802
        const char *name,
803
0
        igraph_strvector_t *value) {
804
0
    igraph_strvector_clear(value);
805
0
    if (igraph_i_attribute_table) {
806
0
        return igraph_i_attribute_table->get_string_graph_attr(graph, name, value);
807
0
    } else {
808
0
        return IGRAPH_SUCCESS;
809
0
    }
810
0
}
811
812
igraph_error_t igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph,
813
        const char *name,
814
        igraph_vs_t vs,
815
0
        igraph_strvector_t *value) {
816
0
    igraph_strvector_clear(value);
817
0
    if (igraph_i_attribute_table) {
818
0
        return igraph_i_attribute_table->get_string_vertex_attr(graph, name, vs, value);
819
0
    } else {
820
0
        return IGRAPH_SUCCESS;
821
0
    }
822
0
}
823
824
igraph_error_t igraph_i_attribute_get_string_edge_attr(const igraph_t *graph,
825
        const char *name,
826
        igraph_es_t es,
827
0
        igraph_strvector_t *value) {
828
0
    igraph_strvector_clear(value);
829
0
    if (igraph_i_attribute_table) {
830
0
        return igraph_i_attribute_table->get_string_edge_attr(graph, name, es, value);
831
0
    } else {
832
0
        return IGRAPH_SUCCESS;
833
0
    }
834
0
}
835
836
igraph_error_t igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph,
837
        const char *name,
838
0
        igraph_vector_bool_t *value) {
839
0
    igraph_vector_bool_clear(value);
840
0
    if (igraph_i_attribute_table) {
841
0
        return igraph_i_attribute_table->get_bool_graph_attr(graph, name, value);
842
0
    } else {
843
0
        return IGRAPH_SUCCESS;
844
0
    }
845
0
}
846
847
igraph_error_t igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph,
848
        const char *name,
849
        igraph_vs_t vs,
850
0
        igraph_vector_bool_t *value) {
851
0
    igraph_vector_bool_clear(value);
852
0
    if (igraph_i_attribute_table) {
853
0
        return igraph_i_attribute_table->get_bool_vertex_attr(graph, name, vs, value);
854
0
    } else {
855
0
        return IGRAPH_SUCCESS;
856
0
    }
857
0
}
858
859
igraph_error_t igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph,
860
        const char *name,
861
        igraph_es_t es,
862
0
        igraph_vector_bool_t *value) {
863
0
    igraph_vector_bool_clear(value);
864
0
    if (igraph_i_attribute_table) {
865
0
        return igraph_i_attribute_table->get_bool_edge_attr(graph, name, es, value);
866
0
    } else {
867
0
        return IGRAPH_SUCCESS;
868
0
    }
869
0
}
870
871
/**
872
 * \function igraph_set_attribute_table
873
 * \brief Attach an attribute table.
874
 *
875
 * This function attaches attribute handling code to the igraph library.
876
 * Note that the attribute handler table is \em not thread-local even if
877
 * igraph is compiled in thread-local mode. In the vast majority of cases,
878
 * this is not a significant restriction.
879
 *
880
 * </para><para>
881
 * Attribute handlers are normally attached on program startup, and are
882
 * left active for the program's lifetime. This is because a graph object
883
 * created with a given attribute handler must not be manipulated while
884
 * a different attribute handler is active.
885
 *
886
 * \param table Pointer to an \ref igraph_attribute_table_t object
887
 *    containing the functions for attribute manipulation. Supply \c
888
 *    NULL here if you don't want attributes.
889
 * \return Pointer to the old attribute handling table.
890
 *
891
 * Time complexity: O(1).
892
 */
893
894
igraph_attribute_table_t *
895
0
igraph_set_attribute_table(const igraph_attribute_table_t * table) {
896
0
    igraph_attribute_table_t *old = igraph_i_attribute_table;
897
0
    igraph_i_attribute_table = (igraph_attribute_table_t*) table;
898
0
    return old;
899
0
}
900
901
0
igraph_bool_t igraph_has_attribute_table(void) {
902
0
    return igraph_i_attribute_table != NULL;
903
0
}
904
905
906
/**
907
 * \function igraph_attribute_combination_init
908
 * \brief Initialize attribute combination list.
909
 *
910
 * \param comb The uninitialized attribute combination list.
911
 * \return Error code.
912
 *
913
 * Time complexity: O(1)
914
 */
915
0
igraph_error_t igraph_attribute_combination_init(igraph_attribute_combination_t *comb) {
916
0
    IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0));
917
0
    return IGRAPH_SUCCESS;
918
0
}
919
920
/**
921
 * \function igraph_attribute_combination_destroy
922
 * \brief Destroy attribute combination list.
923
 *
924
 * \param comb The attribute combination list.
925
 *
926
 * Time complexity: O(n), where n is the number of records in the
927
                    attribute combination list.
928
 */
929
0
void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) {
930
0
    igraph_int_t i, n = igraph_vector_ptr_size(&comb->list);
931
0
    for (i = 0; i < n; i++) {
932
0
        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
933
0
        if (rec->name) {
934
0
            IGRAPH_FREE(rec->name);
935
0
        }
936
0
        IGRAPH_FREE(rec);
937
0
    }
938
0
    igraph_vector_ptr_destroy(&comb->list);
939
0
}
940
941
/**
942
 * \function igraph_attribute_combination_add
943
 * \brief Add combination record to attribute combination list.
944
 *
945
 * \param comb The attribute combination list.
946
 * \param name The name of the attribute. If the name already exists
947
 *             the attribute combination record will be replaced.
948
 *             Use NULL to add a default combination record for all
949
 *             atributes not in the list.
950
 * \param type The type of the attribute combination. See \ref
951
 *             igraph_attribute_combination_type_t for the options.
952
 * \param func Function to be used if \p type is
953
 *             \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION. This function is called
954
 *             by the concrete attribute handler attached to igraph, and its
955
 *             calling signature depends completely on the attribute handler.
956
 *             For instance, if you are using attributes from C and you have
957
 *             attached the C attribute handler, you need to follow the
958
 *             documentation of the <link linkend="c-attribute-combination-functions">C attribute handler</link>
959
 *             for more details.
960
 * \return Error code.
961
 *
962
 * Time complexity: O(n), where n is the number of current attribute
963
 *                  combinations.
964
 */
965
igraph_error_t igraph_attribute_combination_add(igraph_attribute_combination_t *comb,
966
                                     const char *name,
967
                                     igraph_attribute_combination_type_t type,
968
0
                                     igraph_function_pointer_t func) {
969
0
    igraph_int_t i, n = igraph_vector_ptr_size(&comb->list);
970
971
    /* Search, in case it is already there */
972
0
    for (i = 0; i < n; i++) {
973
0
        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
974
0
        const char *n = r->name;
975
0
        if ( (!name && !n) ||
976
0
             (name && n && !strcmp(n, name)) ) {
977
0
            r->type = type;
978
0
            r->func = func;
979
0
            break;
980
0
        }
981
0
    }
982
983
0
    if (i == n) {
984
        /* This is a new attribute name */
985
0
        igraph_attribute_combination_record_t *rec =
986
0
            IGRAPH_CALLOC(1, igraph_attribute_combination_record_t);
987
0
        if (! rec) {
988
0
            IGRAPH_ERROR("Cannot create attribute combination data.",
989
0
                         IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
990
0
        }
991
0
        IGRAPH_FINALLY(igraph_free, rec);
992
0
        if (! name) {
993
0
            rec->name = NULL;
994
0
        } else {
995
0
            rec->name = strdup(name);
996
0
            if (! rec->name) {
997
0
                IGRAPH_ERROR("Cannot create attribute combination data.",
998
0
                             IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
999
0
            }
1000
0
        }
1001
0
        IGRAPH_FINALLY(igraph_free, (char *) rec->name); /* free() is safe on NULL */
1002
0
        rec->type = type;
1003
0
        rec->func = func;
1004
1005
0
        IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec));
1006
0
        IGRAPH_FINALLY_CLEAN(2); /* ownership of 'rec' transferred to 'comb->list' */
1007
1008
0
    }
1009
1010
0
    return IGRAPH_SUCCESS;
1011
0
}
1012
1013
/**
1014
 * \function igraph_attribute_combination_remove
1015
 * \brief Remove a record from an attribute combination list.
1016
 *
1017
 * \param comb The attribute combination list.
1018
 * \param name The attribute name of the attribute combination record
1019
 *             to remove. It will be ignored if the named attribute
1020
 *             does not exist. It can be NULL to remove the default
1021
 *             combination record.
1022
 * \return Error code. This currently always returns IGRAPH_SUCCESS.
1023
 *
1024
 * Time complexity: O(n), where n is the number of records in the attribute
1025
                    combination list.
1026
 */
1027
igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_t *comb,
1028
0
                                        const char *name) {
1029
0
    igraph_int_t i, n = igraph_vector_ptr_size(&comb->list);
1030
1031
    /* Search, in case it is already there */
1032
0
    for (i = 0; i < n; i++) {
1033
0
        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
1034
0
        const char *n = r->name;
1035
0
        if ( (!name && !n) ||
1036
0
             (name && n && !strcmp(n, name)) ) {
1037
0
            break;
1038
0
        }
1039
0
    }
1040
1041
0
    if (i != n) {
1042
0
        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
1043
0
        if (r->name) {
1044
0
            IGRAPH_FREE(r->name);
1045
0
        }
1046
0
        IGRAPH_FREE(r);
1047
0
        igraph_vector_ptr_remove(&comb->list, i);
1048
0
    } else {
1049
        /* It is not there, we don't do anything */
1050
0
    }
1051
1052
0
    return IGRAPH_SUCCESS;
1053
0
}
1054
1055
igraph_error_t igraph_attribute_combination_query(const igraph_attribute_combination_t *comb,
1056
                                       const char *name,
1057
                                       igraph_attribute_combination_type_t *type,
1058
0
                                       igraph_function_pointer_t *func) {
1059
0
    igraph_int_t i, def = -1, len = igraph_vector_ptr_size(&comb->list);
1060
1061
0
    for (i = 0; i < len; i++) {
1062
0
        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
1063
0
        const char *n = rec->name;
1064
0
        if ( (!name && !n) ||
1065
0
             (name && n && !strcmp(n, name)) ) {
1066
0
            *type = rec->type;
1067
0
            *func = rec->func;
1068
0
            return IGRAPH_SUCCESS;
1069
0
        }
1070
0
        if (!n) {
1071
0
            def = i;
1072
0
        }
1073
0
    }
1074
1075
0
    if (def == -1) {
1076
        /* Did not find anything */
1077
0
        *type = IGRAPH_ATTRIBUTE_COMBINE_DEFAULT;
1078
0
        *func = 0;
1079
0
    } else {
1080
0
        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[def];
1081
0
        *type = rec->type;
1082
0
        *func = rec->func;
1083
0
    }
1084
1085
0
    return IGRAPH_SUCCESS;
1086
0
}
1087
1088
/**
1089
 * \function igraph_attribute_combination
1090
 * \brief Initialize attribute combination list and add records.
1091
 *
1092
 * \param comb The uninitialized attribute combination list.
1093
 * \param ...  A list of 'name, type[, func]', where:
1094
 * \param name The name of the attribute. If the name already exists
1095
 *             the attribute combination record will be replaced.
1096
 *             Use NULL to add a default combination record for all
1097
 *             atributes not in the list.
1098
 * \param type The type of the attribute combination. See \ref
1099
 *             igraph_attribute_combination_type_t for the options.
1100
 * \param func Function to be used if \p type is
1101
 *             \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION.
1102
 * The list is closed by setting the name to \c IGRAPH_NO_MORE_ATTRIBUTES.
1103
 * \return Error code.
1104
 *
1105
 * Time complexity: O(n^2), where n is the number attribute
1106
 *                  combinations records to add.
1107
 *
1108
 * \example examples/simple/igraph_attribute_combination.c
1109
 */
1110
igraph_error_t igraph_attribute_combination(
1111
0
        igraph_attribute_combination_t *comb, ...) {
1112
1113
0
    va_list ap;
1114
1115
0
    IGRAPH_CHECK(igraph_attribute_combination_init(comb));
1116
1117
0
    va_start(ap, comb);
1118
0
    while (true) {
1119
0
        igraph_function_pointer_t func = NULL;
1120
0
        igraph_attribute_combination_type_t type;
1121
0
        const char *name;
1122
1123
0
        name = va_arg(ap, const char *);
1124
1125
0
        if (name == IGRAPH_NO_MORE_ATTRIBUTES) {
1126
0
            break;
1127
0
        }
1128
1129
0
        type = (igraph_attribute_combination_type_t) va_arg(ap, int);
1130
0
        if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) {
1131
0
            func = va_arg(ap, igraph_function_pointer_t);
1132
0
        }
1133
1134
0
        if (strlen(name) == 0) {
1135
0
            name = 0;
1136
0
        }
1137
1138
0
        igraph_error_t ret = igraph_attribute_combination_add(comb, name, type, func);
1139
0
        if (ret != IGRAPH_SUCCESS) {
1140
0
            va_end(ap);
1141
0
            return ret;
1142
0
        }
1143
0
    }
1144
1145
0
    va_end(ap);
1146
1147
0
    return IGRAPH_SUCCESS;
1148
0
}