2 * Copyright (c) 2022 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <sys/queue.h>
27 #include "got_compat.h"
29 #include "got_object.h"
30 #include "got_error.h"
32 #include "got_lib_delta.h"
33 #include "got_lib_hash.h"
34 #include "got_lib_inflate.h"
35 #include "got_lib_object.h"
36 #include "got_lib_object_qid.h"
37 #include "got_lib_object_idset.h"
38 #include "got_lib_object_parse.h"
40 #define GOT_OBJECT_IDSET_MIN_BUCKETS 64
42 struct got_object_idset
{
43 struct got_object_id_queue
*ids
;
47 #define GOT_OBJECT_IDSET_F_TRAVERSAL 0x01
48 #define GOT_OBJECT_IDSET_F_NOMEM 0x02
52 struct got_object_idset
*
53 got_object_idset_alloc(void)
55 struct got_object_idset
*set
;
58 set
= malloc(sizeof(*set
));
62 set
->ids
= calloc(GOT_OBJECT_IDSET_MIN_BUCKETS
, sizeof(set
->ids
[0]));
63 if (set
->ids
== NULL
) {
67 for (i
= 0; i
< GOT_OBJECT_IDSET_MIN_BUCKETS
; i
++)
68 STAILQ_INIT(&set
->ids
[i
]);
71 set
->nbuckets
= GOT_OBJECT_IDSET_MIN_BUCKETS
;
73 arc4random_buf(&set
->key
, sizeof(set
->key
));
78 got_object_idset_free(struct got_object_idset
*set
)
81 struct got_object_qid
*qid
;
83 for (i
= 0; i
< set
->nbuckets
; i
++) {
84 while (!STAILQ_EMPTY(&set
->ids
[i
])) {
85 qid
= STAILQ_FIRST(&set
->ids
[i
]);
86 STAILQ_REMOVE(&set
->ids
[i
], qid
, got_object_qid
, entry
);
87 got_object_qid_free(qid
);
90 /* User data should be freed by caller. */
96 idset_hash(struct got_object_idset
*set
, struct got_object_id
*id
)
98 return SipHash24(&set
->key
, id
->hash
, got_hash_digest_length(id
->algo
));
101 static const struct got_error
*
102 idset_resize(struct got_object_idset
*set
, size_t nbuckets
)
104 struct got_object_id_queue
*ids
;
107 ids
= calloc(nbuckets
, sizeof(ids
[0]));
110 return got_error_from_errno("calloc");
111 /* Proceed with our current amount of hash buckets. */
112 set
->flags
|= GOT_OBJECT_IDSET_F_NOMEM
;
116 for (i
= 0; i
< nbuckets
; i
++)
117 STAILQ_INIT(&ids
[i
]);
119 arc4random_buf(&set
->key
, sizeof(set
->key
));
121 for (i
= 0; i
< set
->nbuckets
; i
++) {
122 while (!STAILQ_EMPTY(&set
->ids
[i
])) {
123 struct got_object_qid
*qid
;
125 qid
= STAILQ_FIRST(&set
->ids
[i
]);
126 STAILQ_REMOVE(&set
->ids
[i
], qid
, got_object_qid
, entry
);
127 idx
= idset_hash(set
, &qid
->id
) % nbuckets
;
128 STAILQ_INSERT_HEAD(&ids
[idx
], qid
, entry
);
134 set
->nbuckets
= nbuckets
;
138 static const struct got_error
*
139 idset_grow(struct got_object_idset
*set
)
143 if (set
->flags
& GOT_OBJECT_IDSET_F_NOMEM
)
146 if (set
->nbuckets
>= UINT_MAX
/ 2)
149 nbuckets
= set
->nbuckets
* 2;
151 return idset_resize(set
, nbuckets
);
154 const struct got_error
*
155 got_object_idset_add(struct got_object_idset
*set
, struct got_object_id
*id
,
158 const struct got_error
*err
;
159 struct got_object_qid
*qid
;
161 struct got_object_id_queue
*head
;
163 /* This function may resize the set. */
164 if (set
->flags
& GOT_OBJECT_IDSET_F_TRAVERSAL
)
165 return got_error_msg(GOT_ERR_NOT_IMPL
,
166 "cannot add elements to idset during traversal");
168 if (set
->totelem
== UINT_MAX
)
169 return got_error(GOT_ERR_NO_SPACE
);
171 err
= got_object_qid_alloc_partial(&qid
);
174 memcpy(&qid
->id
, id
, sizeof(qid
->id
));
177 idx
= idset_hash(set
, id
) % set
->nbuckets
;
178 head
= &set
->ids
[idx
];
179 STAILQ_INSERT_HEAD(head
, qid
, entry
);
182 if (set
->nbuckets
< set
->totelem
)
183 err
= idset_grow(set
);
188 static struct got_object_qid
*
189 find_element(struct got_object_idset
*set
, struct got_object_id
*id
)
191 uint64_t idx
= idset_hash(set
, id
) % set
->nbuckets
;
192 struct got_object_id_queue
*head
= &set
->ids
[idx
];
193 struct got_object_qid
*qid
;
195 STAILQ_FOREACH(qid
, head
, entry
) {
196 if (got_object_id_cmp(&qid
->id
, id
) == 0)
204 got_object_idset_get(struct got_object_idset
*set
, struct got_object_id
*id
)
206 struct got_object_qid
*qid
= find_element(set
, id
);
207 return qid
? qid
->data
: NULL
;
210 const struct got_error
*
211 got_object_idset_remove(void **data
, struct got_object_idset
*set
,
212 struct got_object_id
*id
)
215 struct got_object_id_queue
*head
;
216 struct got_object_qid
*qid
;
221 if (set
->totelem
== 0)
222 return got_error(GOT_ERR_NO_OBJ
);
225 /* Remove a "random" element. */
226 for (idx
= 0; idx
< set
->nbuckets
; idx
++) {
227 head
= &set
->ids
[idx
];
228 qid
= STAILQ_FIRST(head
);
233 idx
= idset_hash(set
, id
) % set
->nbuckets
;
234 head
= &set
->ids
[idx
];
235 STAILQ_FOREACH(qid
, head
, entry
) {
236 if (got_object_id_cmp(&qid
->id
, id
) == 0)
240 return got_error_no_obj(id
);
245 STAILQ_REMOVE(head
, qid
, got_object_qid
, entry
);
246 got_object_qid_free(qid
);
253 got_object_idset_contains(struct got_object_idset
*set
,
254 struct got_object_id
*id
)
256 struct got_object_qid
*qid
= find_element(set
, id
);
260 const struct got_error
*
261 got_object_idset_for_each(struct got_object_idset
*set
,
262 const struct got_error
*(*cb
)(struct got_object_id
*, void *, void *),
265 const struct got_error
*err
= NULL
;
266 struct got_object_id_queue
*head
;
267 struct got_object_qid
*qid
, *tmp
;
270 set
->flags
|= GOT_OBJECT_IDSET_F_TRAVERSAL
;
271 for (i
= 0; i
< set
->nbuckets
; i
++) {
273 STAILQ_FOREACH_SAFE(qid
, head
, entry
, tmp
) {
274 err
= (*cb
)(&qid
->id
, qid
->data
, arg
);
280 set
->flags
&= ~GOT_OBJECT_IDSET_F_TRAVERSAL
;
285 got_object_idset_num_elements(struct got_object_idset
*set
)