Coverage Report

Created: 2025-08-25 07:03

/src/igraph/src/graph/caching.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   IGraph library.
3
   Copyright (C) 2022  The igraph development team <igraph@igraph.org>
4
5
   This program is free software; you can redistribute it and/or modify
6
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 2 of the License, or
8
   (at your option) any later version.
9
10
   This program is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
   GNU General Public License for more details.
14
15
   You should have received a copy of the GNU General Public License
16
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
*/
18
19
#include "igraph_interface.h"
20
21
#include "graph/caching.h"
22
23
#include <assert.h>
24
25
/****** Strictly internal functions ******/
26
27
/**
28
 * \brief Initializes a property cache, ensuring that all values are unknown.
29
 */
30
3.22k
igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache) {
31
3.22k
    IGRAPH_STATIC_ASSERT(IGRAPH_PROP_I_SIZE <= 32);
32
33
3.22k
    memset(cache->value, 0, sizeof(cache->value));
34
3.22k
    cache->known = 0;
35
3.22k
    return IGRAPH_SUCCESS;
36
3.22k
}
37
38
/**
39
 * \brief Copies a property cache.
40
 */
41
igraph_error_t igraph_i_property_cache_copy(
42
        igraph_i_property_cache_t *cache,
43
0
        const igraph_i_property_cache_t *other_cache) {
44
0
    *cache = *other_cache;
45
0
    return IGRAPH_SUCCESS;
46
0
}
47
48
/**
49
 * \brief Destroys a property cache.
50
 */
51
3.22k
void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache) {
52
3.22k
    IGRAPH_UNUSED(cache);
53
    /* Nothing to do */
54
3.22k
}
55
56
/***** Developer functions, exposed *****/
57
58
/**
59
 * \brief Returns the value of a cached boolean property.
60
 *
61
 * This function provides valid results only when the property is already
62
 * cached. Use \ref igraph_i_property_cache_has() to retrieve whether the
63
 * property is cached.
64
 *
65
 * \param graph  the graph whose cache is to be checked
66
 * \param prop   the property to retrieve from the cache
67
 * \return the cached value of the property if the value is in the cache, or
68
 *         an undefined value otherwise
69
 */
70
0
igraph_bool_t igraph_i_property_cache_get_bool(const igraph_t *graph, igraph_cached_property_t prop) {
71
0
    IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
72
0
    assert(graph->cache != NULL);
73
0
    return graph->cache->value[prop];
74
0
}
75
76
/**
77
 * \brief Returns whether the cache contains a value for the given cached property.
78
 *
79
 * \param graph  the graph whose cache is to be checked
80
 * \param prop   the property to check in the cache
81
 */
82
3.56k
igraph_bool_t igraph_i_property_cache_has(const igraph_t *graph, igraph_cached_property_t prop) {
83
3.56k
    IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
84
3.56k
    assert(graph->cache != NULL);
85
3.56k
    return graph->cache->known & (1 << prop);
86
3.56k
}
87
88
/**
89
 * \brief Stores a property value in the cache.
90
 *
91
 * \param graph  the graph whose cache is to be modified
92
 * \param prop   the property to update in the cache
93
 * \param value  the value of the property to add to the cache
94
 */
95
0
void igraph_i_property_cache_set_bool(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value) {
96
0
    IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
97
0
    assert(graph->cache != NULL);
98
    /* Even though graph is const, updating the cache is not considered modification.
99
     * Functions that merely compute graph properties, and thus leave the graph structure
100
     * intact, will often update the cache. */
101
0
    graph->cache->value[prop] = value;
102
0
    graph->cache->known |= (1 << prop);
103
0
}
104
105
/**
106
 * \brief Stores a property value in the cache.
107
 *
108
 * This function asserts that if the value of \p prop was already known,
109
 * then \p value is consistent with the previously stored value.
110
 * If this is not the case, a fatal error is triggered, with the reasoning
111
 * that the cache must have become invalid/inconsistent due to a bug.
112
 *
113
 * Therefore, this function cannot be used to change an already stored
114
 * property to a different value. If this is your intention, invalidate
115
 * the cache explicitly first.
116
 *
117
 * \param graph  the graph whose cache is to be modified
118
 * \param prop   the property to update in the cache
119
 * \param value  the value of the property to add to the cache
120
 */
121
0
void igraph_i_property_cache_set_bool_checked(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value) {
122
0
    IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
123
0
    assert(graph->cache != NULL);
124
    /* Even though graph is const, updating the cache is not considered modification.
125
     * Functions that merely compute graph properties, and thus leave the graph structure
126
     * intact, will often update the cache. */
127
0
    if (graph->cache->known & (1 << prop)) {
128
0
        IGRAPH_ASSERT(graph->cache->value[prop] == value);
129
0
    } else {
130
0
        igraph_i_property_cache_set_bool(graph, prop, value);
131
0
    }
132
0
}
133
134
/**
135
 * \brief Invalidates the cached value of a property in a graph.
136
 *
137
 * \param graph  the graph whose cache is to be modified
138
 * \param prop   the property to invalidate in the cache
139
 */
140
0
void igraph_i_property_cache_invalidate(const igraph_t *graph, igraph_cached_property_t prop) {
141
0
    IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE);
142
0
    assert(graph->cache != NULL);
143
0
    graph->cache->known &= ~(1 << prop);
144
0
}
145
146
/**
147
 * \brief Invalidates all cached properties of the graph.
148
 *
149
 * This function is typically called after the graph is modified.
150
 *
151
 * \param graph  the graph whose cache is to be invalidated
152
 */
153
0
void igraph_i_property_cache_invalidate_all(const igraph_t *graph) {
154
0
    assert(graph->cache != NULL);
155
0
    graph->cache->known = 0;
156
0
}
157
158
/**
159
 * \brief Invalidates all but a few cached properties of the graph, subject to specific conditions.
160
 *
161
 * This function is typically called after the graph is modified if we know that
162
 * the modification does not affect certain cached properties in certain cases.
163
 * For instance, adding more vertices does not make a connected graph disconnected,
164
 * so we can keep the cached properties related to graph connectivity if they
165
 * were already cached as true, but we need to invalidate them if they were
166
 * cached as false.
167
 *
168
 * </para><para>
169
 * Use <code>1 << IGRAPH_PROP_SOMETHING</code> to encode an individual property
170
 * in the bits of the bitmask used in the arguments of this function.
171
 *
172
 * \param graph       the graph whose cache is to be invalidated
173
 * \param keep_always bitmask where the i-th bit corresponds to cached property \em i
174
 *        and it should be set to 1 if the property should be \em kept ,
175
 *        irrespectively of its current cached value.
176
 */
177
void igraph_i_property_cache_invalidate_conditionally(
178
    const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false,
179
    uint32_t keep_when_true
180
7.59k
) {
181
7.59k
    uint32_t invalidate = ~keep_always;
182
7.59k
    uint32_t mask;
183
7.59k
    uint32_t maybe_keep;
184
7.59k
    igraph_bool_t cached_value;
185
186
7.59k
    assert(graph->cache != NULL);
187
188
    /* The bits of maybe_keep are set to 1 for those properties that are:
189
     *
190
     * - currently cached
191
     * - should _probably_ be invalidated
192
     * - _but_ the current cached value of the property may change the decision
193
     */
194
7.59k
    maybe_keep = graph->cache->known & invalidate & (keep_when_false | keep_when_true);
195
196
7.59k
    if (maybe_keep) {
197
0
        for (igraph_cached_property_t prop = (igraph_cached_property_t ) 0; prop < IGRAPH_PROP_I_SIZE; ++prop) {
198
0
            mask = 1 << prop;
199
0
            if (maybe_keep & mask) {
200
                /* if we get here, we know that the property is cached; we have
201
                 * masked maybe_keep with graph->cache->known */
202
0
                cached_value = igraph_i_property_cache_get_bool(graph, prop);
203
0
                if (
204
0
                    ((keep_when_false & mask) && !cached_value) ||
205
0
                    ((keep_when_true & mask) && cached_value)
206
0
                ) {
207
0
                    invalidate &= ~mask;
208
0
                }
209
0
            }
210
0
        }
211
0
    }
212
213
7.59k
    graph->cache->known &= ~invalidate;
214
7.59k
}