1 #include "geodb.h"
2 
3 static geodb_t *geodb = NULL;
4 
get_geodb(void)5 geodb_t *get_geodb(void) {
6     return geodb;
7 }
8 
geodb_destroy(geodb_t * self)9 void geodb_destroy(geodb_t *self) {
10     if (self == NULL) return;
11 
12     if (self->names != NULL) {
13         trie_destroy(self->names);
14     }
15 
16     if (self->features != NULL) {
17         trie_destroy(self->features);
18     }
19 
20     if (self->postal_codes != NULL) {
21         cstring_array_destroy(self->postal_codes);
22     }
23 
24     if (self->hash_reader != NULL) {
25         sparkey_hash_close(&self->hash_reader);
26     }
27 
28     if (self->log_iter != NULL) {
29         sparkey_logiter_close(&self->log_iter);
30     }
31 
32     if (self->value_buf != NULL) {
33         char_array_destroy(self->value_buf);
34     }
35 
36     if (self->geoname != NULL) {
37         geoname_destroy(self->geoname);
38     }
39 
40     if (self->postal_code != NULL) {
41         gn_postal_code_destroy(self->postal_code);
42     }
43 
44     free(self);
45 }
46 
geodb_init(char * dir)47 geodb_t *geodb_init(char *dir) {
48     if (dir == NULL) return NULL;
49 
50     geodb_t *gdb = calloc(1, sizeof(geodb_t));
51 
52     if (gdb == NULL) return NULL;
53 
54     char_array *path = char_array_new_size(strlen(dir));
55     char_array_cat_joined(path, PATH_SEPARATOR, true, 2, dir, GEODB_NAMES_TRIE_FILENAME);
56 
57     char *names_path = char_array_get_string(path);
58 
59     gdb->names = trie_load(names_path);
60     if (gdb->names == NULL) {
61         goto exit_geodb_created;
62     }
63 
64     char_array_clear(path);
65 
66     char_array_cat_joined(path, PATH_SEPARATOR, true, 2, dir, GEODB_FEATURES_TRIE_FILENAME);
67 
68     char *features_path = char_array_get_string(path);
69 
70     gdb->features = trie_load(features_path);
71     if(gdb->features == NULL) {
72         goto exit_geodb_created;
73     }
74 
75     char_array_clear(path);
76 
77     char_array_cat_joined(path, PATH_SEPARATOR, true, 2, dir, GEODB_POSTAL_CODES_FILENAME);
78     char *postal_codes_path = char_array_get_string(path);
79 
80     FILE *f = fopen(postal_codes_path, "rb");
81 
82     uint64_t num_postal_strings = 0;
83     if (!file_read_uint64(f, &num_postal_strings)) {
84         goto exit_geodb_created;
85     }
86 
87     uint64_t postal_codes_str_len;
88 
89     if (!file_read_uint64(f, &postal_codes_str_len)) {
90         goto exit_geodb_created;
91     }
92 
93     char_array *array = char_array_new_size((size_t)postal_codes_str_len);
94 
95     if (!file_read_chars(f, array->a, (size_t)postal_codes_str_len)) {
96         goto exit_geodb_created;
97     }
98 
99     array->n = (size_t)postal_codes_str_len;
100 
101     gdb->postal_codes = cstring_array_from_char_array(array);
102 
103     if (cstring_array_num_strings(gdb->postal_codes) != num_postal_strings) {
104         goto exit_geodb_created;
105     }
106 
107     fclose(f);
108     char_array_clear(path);
109 
110     char_array_cat_joined(path, PATH_SEPARATOR, true, 2, dir, GEODB_HASH_FILENAME);
111 
112     char *hash_file_path = strdup(char_array_get_string(path));
113 
114     char_array_clear(path);
115 
116     char_array_cat_joined(path, PATH_SEPARATOR, true, 2, dir, GEODB_LOG_FILENAME);
117 
118     char *log_path = char_array_get_string(path);
119 
120     gdb->hash_reader = NULL;
121 
122     if ((sparkey_hash_open(&gdb->hash_reader, hash_file_path, log_path)) != SPARKEY_SUCCESS) {
123         free(hash_file_path);
124         char_array_destroy(path);
125         goto exit_geodb_created;
126     }
127 
128     free(hash_file_path);
129     char_array_destroy(path);
130 
131     gdb->log_iter = NULL;
132 
133     if ((sparkey_logiter_create(&gdb->log_iter, sparkey_hash_getreader(gdb->hash_reader))) != SPARKEY_SUCCESS) {
134         goto exit_geodb_created;
135     }
136 
137     gdb->value_buf = char_array_new_size(sparkey_logreader_maxvaluelen(sparkey_hash_getreader(gdb->hash_reader)));
138     if (gdb->value_buf == NULL) {
139         goto exit_geodb_created;
140     }
141 
142     gdb->geoname = geoname_new();
143     if (gdb->geoname == NULL) {
144         goto exit_geodb_created;
145     }
146 
147     gdb->postal_code = gn_postal_code_new();
148     if (gdb->postal_code == NULL) {
149         goto exit_geodb_created;
150     }
151 
152     return gdb;
153 
154 exit_geodb_created:
155     geodb_destroy(gdb);
156     return NULL;
157 }
158 
geodb_load(char * dir)159 bool geodb_load(char *dir) {
160     geodb = geodb_init(dir);
161     return (geodb != NULL);
162 }
163 
164 
search_geodb_with_phrases(char * str,phrase_array ** phrases)165 bool search_geodb_with_phrases(char *str, phrase_array **phrases) {
166     if (str == NULL) return false;
167 
168     return trie_search_with_phrases(geodb->names, str, phrases);
169 }
170 
search_geodb(char * str)171 phrase_array *search_geodb(char *str) {
172     phrase_array *phrases = NULL;
173 
174     if (!search_geodb_with_phrases(str, &phrases)) {
175         return NULL;
176     }
177 
178     return phrases;
179 }
180 
181 
search_geodb_tokens_with_phrases(char * str,token_array * tokens,phrase_array ** phrases)182 bool search_geodb_tokens_with_phrases(char *str, token_array *tokens, phrase_array **phrases) {
183     if (str == NULL) return false;
184 
185     return trie_search_tokens_with_phrases(geodb->names, str, tokens, phrases);
186 }
187 
188 
search_geodb_tokens(char * str,token_array * tokens)189 phrase_array *search_geodb_tokens(char *str, token_array *tokens) {
190     phrase_array *phrases = NULL;
191 
192     if (!search_geodb_tokens_with_phrases(str, tokens, &phrases)) {
193         return NULL;
194     }
195 
196     return phrases;
197 }
198 
199 
geodb_get_len(char * key,size_t len)200 geonames_generic_t *geodb_get_len(char *key, size_t len) {
201     if (geodb == NULL || geodb->hash_reader == NULL || geodb->log_iter == NULL) return NULL;
202     sparkey_returncode ret = sparkey_hash_get(geodb->hash_reader, (uint8_t *)key, len, geodb->log_iter);
203     if (sparkey_logiter_state(geodb->log_iter) == SPARKEY_ITER_ACTIVE) {
204         uint64_t expected_value_len = sparkey_logiter_valuelen(geodb->log_iter);
205         uint64_t actual_value_len;
206         ret = sparkey_logiter_fill_value(geodb->log_iter, sparkey_hash_getreader(geodb->hash_reader), expected_value_len, (uint8_t *)geodb->value_buf->a, &actual_value_len);
207         if (ret == SPARKEY_SUCCESS) {
208             geonames_generic_t *generic = malloc(sizeof(geonames_generic_t));
209             if (geonames_generic_deserialize(&generic->type, geodb->geoname, geodb->postal_code, geodb->value_buf)) {
210                 if (generic->type == GEONAMES_PLACE) {
211                     generic->geoname = geodb->geoname;
212                 } else if (generic->type == GEONAMES_POSTAL_CODE) {
213                     generic->postal_code = geodb->postal_code;
214                 } else {
215                     free(generic);
216                     return NULL;
217                 }
218                 return generic;
219             }
220         }
221     }
222     return NULL;
223 }
224 
geodb_get(char * key)225 inline geonames_generic_t *geodb_get(char *key) {
226     return geodb_get_len(key, strlen(key));
227 }
228 
229 
230 
geodb_module_setup(char * dir)231 bool geodb_module_setup(char *dir) {
232     if (geodb == NULL) {
233         return geodb_load(dir == NULL ? LIBPOSTAL_GEODB_DIR : dir);
234     }
235 
236     return true;
237 }
238 
239 
geodb_module_teardown(void)240 void geodb_module_teardown(void) {
241     if (geodb != NULL) {
242         geodb_destroy(geodb);
243     }
244     geodb = NULL;
245 }
246 
247