]> git.ipfire.org Git - people/ms/libloc.git/blob - src/country-list.c
*_unref: Always expect a valid pointer
[people/ms/libloc.git] / src / country-list.c
1 /*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2020 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library 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 GNU
14 Lesser General Public License for more details.
15 */
16
17 #include <errno.h>
18 #include <stdlib.h>
19
20 #include <libloc/country.h>
21 #include <libloc/country-list.h>
22 #include <libloc/private.h>
23
24 struct loc_country_list {
25 struct loc_ctx* ctx;
26 int refcount;
27
28 struct loc_country** elements;
29 size_t elements_size;
30
31 size_t size;
32 };
33
34 static int loc_country_list_grow(struct loc_country_list* list) {
35 size_t size = list->elements_size * 2;
36 if (size < 1024)
37 size = 1024;
38
39 DEBUG(list->ctx, "Growing country list %p by %zu to %zu\n",
40 list, size, list->elements_size + size);
41
42 struct loc_country** elements = reallocarray(list->elements,
43 list->elements_size + size, sizeof(*list->elements));
44 if (!elements)
45 return 1;
46
47 list->elements = elements;
48 list->elements_size += size;
49
50 return 0;
51 }
52
53 LOC_EXPORT int loc_country_list_new(struct loc_ctx* ctx,
54 struct loc_country_list** list) {
55 struct loc_country_list* l = calloc(1, sizeof(*l));
56 if (!l)
57 return -ENOMEM;
58
59 l->ctx = loc_ref(ctx);
60 l->refcount = 1;
61
62 DEBUG(l->ctx, "Country list allocated at %p\n", l);
63 *list = l;
64
65 return 0;
66 }
67
68 LOC_EXPORT struct loc_country_list* loc_country_list_ref(struct loc_country_list* list) {
69 list->refcount++;
70
71 return list;
72 }
73
74 static void loc_country_list_free(struct loc_country_list* list) {
75 DEBUG(list->ctx, "Releasing country list at %p\n", list);
76
77 loc_country_list_clear(list);
78
79 loc_unref(list->ctx);
80 free(list);
81 }
82
83 LOC_EXPORT struct loc_country_list* loc_country_list_unref(struct loc_country_list* list) {
84 if (--list->refcount > 0)
85 return list;
86
87 loc_country_list_free(list);
88 return NULL;
89 }
90
91 LOC_EXPORT size_t loc_country_list_size(struct loc_country_list* list) {
92 return list->size;
93 }
94
95 LOC_EXPORT int loc_country_list_empty(struct loc_country_list* list) {
96 return list->size == 0;
97 }
98
99 LOC_EXPORT void loc_country_list_clear(struct loc_country_list* list) {
100 if (!list->elements)
101 return;
102
103 for (unsigned int i = 0; i < list->size; i++)
104 loc_country_unref(list->elements[i]);
105
106 free(list->elements);
107 list->elements = NULL;
108 list->elements_size = 0;
109
110 list->size = 0;
111 }
112
113 LOC_EXPORT struct loc_country* loc_country_list_get(struct loc_country_list* list, size_t index) {
114 // Check index
115 if (index >= list->size)
116 return NULL;
117
118 return loc_country_ref(list->elements[index]);
119 }
120
121 LOC_EXPORT int loc_country_list_append(
122 struct loc_country_list* list, struct loc_country* country) {
123 if (loc_country_list_contains(list, country))
124 return 0;
125
126 // Check if we have space left
127 if (list->size >= list->elements_size) {
128 int r = loc_country_list_grow(list);
129 if (r)
130 return r;
131 }
132
133 DEBUG(list->ctx, "%p: Appending country %p to list\n", list, country);
134
135 list->elements[list->size++] = loc_country_ref(country);
136
137 return 0;
138 }
139
140 LOC_EXPORT int loc_country_list_contains(
141 struct loc_country_list* list, struct loc_country* country) {
142 for (unsigned int i = 0; i < list->size; i++) {
143 if (loc_country_cmp(country, list->elements[i]) == 0)
144 return 1;
145 }
146
147 return 0;
148 }
149
150 LOC_EXPORT int loc_country_list_contains_code(
151 struct loc_country_list* list, const char* code) {
152 struct loc_country* country;
153
154 int r = loc_country_new(list->ctx, &country, code);
155 if (r) {
156 // Ignore invalid country codes which would never match
157 if (errno == EINVAL)
158 return 0;
159
160 return r;
161 }
162
163 r = loc_country_list_contains(list, country);
164 loc_country_unref(country);
165
166 return r;
167 }
168
169 static int __loc_country_cmp(const void* country1, const void* country2) {
170 return loc_country_cmp(*(struct loc_country**)country1, *(struct loc_country**)country2);
171 }
172
173 LOC_EXPORT void loc_country_list_sort(struct loc_country_list* list) {
174 // Sort everything
175 qsort(list->elements, list->size, sizeof(*list->elements), __loc_country_cmp);
176 }