Merge branch 'master' into blender2.8
[blender.git] / source / blender / blenkernel / intern / library_idmap.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 #include <string.h>
22 #include <stdlib.h>
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BLI_utildefines.h"
27 #include "BLI_ghash.h"
28 #include "BLI_listbase.h"
29
30 #include "DNA_ID.h"
31
32 #include "BKE_idcode.h"
33 #include "BKE_library.h"
34 #include "BKE_library_idmap.h"  /* own include */
35 #include "BKE_main.h"
36
37 /** \file blender/blenkernel/intern/library_idmap.c
38  *  \ingroup bke
39  *
40  * Utility functions for faster ID lookups.
41  */
42
43 /** \name BKE_main_idmap API
44  *
45  * Cache ID (name, library lookups).
46  * This doesn't account for adding/removing data-blocks,
47  * and should only be used when performing many lookups.
48  *
49  * \note GHash's are initialized on demand,
50  * since its likely some types will never have lookups run on them,
51  * so its a waste to create and never use.
52  * \{ */
53
54 struct IDNameLib_Key {
55         /** ``ID.name + 2``: without the ID type prefix, since each id type gets it's own 'map' */
56         const char *name;
57         /** ``ID.lib``: */
58         const Library *lib;
59 };
60
61 struct IDNameLib_TypeMap {
62         GHash *map;
63         short id_type;
64         /* only for storage of keys in the ghash, avoid many single allocs */
65         struct IDNameLib_Key *keys;
66 };
67
68 /**
69  * Opaque structure, external API users only see this.
70  */
71 struct IDNameLib_Map {
72         struct IDNameLib_TypeMap type_maps[MAX_LIBARRAY];
73         struct Main *bmain;
74 };
75
76 static struct IDNameLib_TypeMap *main_idmap_from_idcode(struct IDNameLib_Map *id_map, short id_type)
77 {
78         for (int i = 0; i < MAX_LIBARRAY; i++) {
79                 if (id_map->type_maps[i].id_type == id_type) {
80                         return &id_map->type_maps[i];
81                 }
82         }
83         return NULL;
84 }
85
86 struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain)
87 {
88         struct IDNameLib_Map *id_map = MEM_mallocN(sizeof(*id_map), __func__);
89
90         int index = 0;
91         while (index < MAX_LIBARRAY) {
92                 struct IDNameLib_TypeMap *type_map = &id_map->type_maps[index];
93                 type_map->map = NULL;
94                 type_map->id_type = BKE_idcode_iter_step(&index);
95                 BLI_assert(type_map->id_type != 0);
96         }
97         BLI_assert(index == MAX_LIBARRAY);
98
99         id_map->bmain = bmain;
100
101         return id_map;
102 }
103
104 struct Main *BKE_main_idmap_main_get(struct IDNameLib_Map *id_map)
105 {
106         return id_map->bmain;
107 }
108
109 static unsigned int idkey_hash(const void *ptr)
110 {
111         const struct IDNameLib_Key *idkey = ptr;
112         unsigned int key = BLI_ghashutil_strhash(idkey->name);
113         if (idkey->lib) {
114                 key ^= BLI_ghashutil_ptrhash(idkey->lib);
115         }
116         return key;
117 }
118
119 static bool idkey_cmp(const void *a, const void *b)
120 {
121         const struct IDNameLib_Key *idkey_a = a;
122         const struct IDNameLib_Key *idkey_b = b;
123         return strcmp(idkey_a->name, idkey_b->name) || (idkey_a->lib != idkey_b->lib);
124 }
125
126 ID *BKE_main_idmap_lookup(struct IDNameLib_Map *id_map, short id_type, const char *name, const Library *lib)
127 {
128         struct IDNameLib_TypeMap *type_map = main_idmap_from_idcode(id_map, id_type);
129
130         if (UNLIKELY(type_map == NULL)) {
131                 return NULL;
132         }
133
134         /* lazy init */
135         if (type_map->map == NULL) {
136                 ListBase *lb = which_libbase(id_map->bmain, id_type);
137                 const int lb_len = BLI_listbase_count(lb);
138                 if (lb_len == 0) {
139                         return NULL;
140                 }
141                 type_map->map = BLI_ghash_new_ex(idkey_hash, idkey_cmp, __func__, lb_len);
142                 type_map->keys = MEM_mallocN(sizeof(struct IDNameLib_Key) * lb_len, __func__);
143
144                 GHash *map = type_map->map;
145                 struct IDNameLib_Key *key = type_map->keys;
146
147                 for (ID *id = lb->first; id; id = id->next, key++) {
148                         key->name = id->name + 2;
149                         key->lib = id->lib;
150                         BLI_ghash_insert(map, key, id);
151                 }
152         }
153
154         const struct IDNameLib_Key key_lookup = {name, lib};
155         return BLI_ghash_lookup(type_map->map, &key_lookup);
156 }
157
158 ID *BKE_main_idmap_lookup_id(struct IDNameLib_Map *id_map, const ID *id)
159 {
160         return BKE_main_idmap_lookup(id_map, GS(id->name), id->name + 2, id->lib);
161 }
162
163 void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map)
164 {
165         struct IDNameLib_TypeMap *type_map = id_map->type_maps;
166         for (int i = 0; i < MAX_LIBARRAY; i++, type_map++) {
167                 if (type_map->map) {
168                         BLI_ghash_free(type_map->map, NULL, NULL);
169                         type_map->map = NULL;
170                         MEM_freeN(type_map->keys);
171                 }
172         }
173
174         MEM_freeN(id_map);
175 }
176
177 /** \} */