1 #include "geonames.h"
2 #include "msgpack_utils.h"
3 
4 #define DEFAULT_BUFFER_SIZE 4096
5 
6 #define GEONAMES_NAME_DEFAULT_LENGTH 255
7 #define GEONAMES_ISO_LANGUAGE_DEFAULT_LENGTH 10
8 #define GEONAMES_FEATURE_CODE_DEFAULT_LENGTH 5
9 #define GEONAMES_COUNTRY_CODE_DEFAULT_LENGTH 3
10 #define GEONAMES_ADMIN1_CODE_DEFAULT_LENGTH 32
11 #define GEONAMES_ADMIN2_CODE_DEFAULT_LENGTH 64
12 #define GEONAMES_ADMIN3_CODE_DEFAULT_LENGTH 32
13 #define GEONAMES_ADMIN4_CODE_DEFAULT_LENGTH 32
14 #define GEONAMES_POSTAL_CODE_DEFAULT_LENGTH 64
15 
16 
17 #define GEONAMES_POSTAL_ADMIN1_IDS_DEFAULT_LENGTH 10
18 #define GEONAMES_POSTAL_ADMIN2_IDS_DEFAULT_LENGTH 20
19 #define GEONAMES_POSTAL_ADMIN3_IDS_DEFAULT_LENGTH 20
20 
21 /* To save on malloc calls, create just one of these and call geoname_clear
22 *  (which does not deallocate) and geoname_deserialize
23 */
geoname_new(void)24 geoname_t *geoname_new(void) {
25     geoname_t *self = malloc(sizeof(geoname_t));
26     if (self == NULL) {
27         return NULL;
28     }
29 
30     self->name = char_array_new_size(GEONAMES_NAME_DEFAULT_LENGTH);
31     self->canonical = char_array_new_size(GEONAMES_NAME_DEFAULT_LENGTH);
32 
33     self->iso_language = char_array_new_size(GEONAMES_ISO_LANGUAGE_DEFAULT_LENGTH);
34 
35     self->feature_code = char_array_new_size(GEONAMES_FEATURE_CODE_DEFAULT_LENGTH);
36     self->country_code = char_array_new_size(GEONAMES_COUNTRY_CODE_DEFAULT_LENGTH);
37 
38     self->admin1_code = char_array_new_size(GEONAMES_ADMIN1_CODE_DEFAULT_LENGTH);
39     self->admin2_code = char_array_new_size(GEONAMES_ADMIN2_CODE_DEFAULT_LENGTH);
40     self->admin3_code = char_array_new_size(GEONAMES_ADMIN3_CODE_DEFAULT_LENGTH);
41     self->admin4_code = char_array_new_size(GEONAMES_ADMIN4_CODE_DEFAULT_LENGTH);
42 
43     if (!(self->name && self->canonical && self->iso_language
44           && self->feature_code && self->country_code
45           && self->admin1_code && self->admin2_code
46           && self->admin3_code && self->admin4_code)) {
47         geoname_destroy(self);
48         return NULL;
49     }
50 
51     return self;
52 }
53 
geoname_clear(geoname_t * self)54 void geoname_clear(geoname_t *self) {
55     char_array_clear(self->name);
56     char_array_clear(self->canonical);
57     char_array_clear(self->iso_language);
58     char_array_clear(self->feature_code);
59     char_array_clear(self->country_code);
60     char_array_clear(self->admin1_code);
61     char_array_clear(self->admin2_code);
62     char_array_clear(self->admin3_code);
63     char_array_clear(self->admin4_code);
64 }
65 
geoname_destroy(geoname_t * self)66 void geoname_destroy(geoname_t *self) {
67     if (!self)
68         return;
69 
70     if (self->name) {
71         char_array_destroy(self->name);
72     }
73 
74     if (self->canonical) {
75         char_array_destroy(self->canonical);
76     }
77 
78     if (self->iso_language) {
79         char_array_destroy(self->iso_language);
80     }
81 
82     if (self->feature_code) {
83         char_array_destroy(self->feature_code);
84     }
85 
86     if (self->country_code) {
87         char_array_destroy(self->country_code);
88     }
89 
90     if (self->admin1_code) {
91         char_array_destroy(self->admin1_code);
92     }
93 
94     if (self->admin2_code) {
95         char_array_destroy(self->admin2_code);
96     }
97 
98     if (self->admin3_code) {
99         char_array_destroy(self->admin3_code);
100     }
101 
102     if (self->admin4_code) {
103         char_array_destroy(self->admin4_code);
104     }
105 
106     free(self);
107 }
108 
geoname_deserialize_ctx(geoname_t * self,cmp_ctx_t * ctx)109 bool geoname_deserialize_ctx(geoname_t *self, cmp_ctx_t *ctx) {
110 
111     uint32_t len;
112 
113     if (!cmp_read_uint(ctx, &self->geonames_id)) {
114         return false;
115     }
116 
117     if (!cmp_read_str_size_or_nil(ctx, &self->name, &len)) {
118         return false;
119     }
120 
121     if (!cmp_read_str_size_or_nil(ctx, &self->canonical, &len)) {
122         return false;
123     }
124 
125     if (!cmp_read_uint(ctx, &self->type)) {
126         return false;
127     }
128 
129     if (!cmp_read_str_size_or_nil(ctx, &self->iso_language, &len)) {
130         return false;
131     }
132 
133     if (!cmp_read_bool(ctx, &self->has_wikipedia_entry)) {
134         return false;
135     }
136 
137     if (!cmp_read_bool(ctx, &self->is_preferred_name)) {
138         return false;
139     }
140 
141     if (!cmp_read_bool(ctx, &self->is_short_name)) {
142         return false;
143     }
144 
145     if (!cmp_read_bool(ctx, &self->is_colloquial)) {
146         return false;
147     }
148 
149     if (!cmp_read_bool(ctx, &self->is_historical)) {
150         return false;
151     }
152 
153     if (!cmp_read_uint(ctx, &self->population)) {
154         return false;
155     }
156 
157     if (!cmp_read_double(ctx, &self->latitude)) {
158         return false;
159     }
160 
161     if (!cmp_read_double(ctx, &self->longitude)) {
162         return false;
163     }
164 
165     if (!cmp_read_str_size_or_nil(ctx, &self->feature_code, &len)) {
166         return false;
167     }
168 
169     if (!cmp_read_str_size_or_nil(ctx, &self->country_code, &len)) {
170         return false;
171     }
172 
173     if (!cmp_read_uint(ctx, &self->country_geonames_id)) {
174         return false;
175     }
176 
177     if (!cmp_read_str_size_or_nil(ctx, &self->admin1_code, &len)) {
178         return false;
179     }
180 
181     if (!cmp_read_uint(ctx, &self->admin1_geonames_id)) {
182         return false;
183     }
184 
185     if (!cmp_read_str_size_or_nil(ctx, &self->admin2_code, &len)) {
186         return false;
187     }
188 
189     if (!cmp_read_uint(ctx, &self->admin2_geonames_id)) {
190         return false;
191     }
192 
193     if (!cmp_read_str_size_or_nil(ctx, &self->admin3_code, &len)) {
194         return false;
195     }
196 
197     if (!cmp_read_uint(ctx, &self->admin3_geonames_id)) {
198         return false;
199     }
200 
201     if (!cmp_read_str_size_or_nil(ctx, &self->admin4_code, &len)) {
202         return false;
203     }
204 
205     if (!cmp_read_uint(ctx, &self->admin4_geonames_id)) {
206         return false;
207     }
208 
209     return true;
210 }
211 
geoname_deserialize(geoname_t * self,char_array * str)212 bool geoname_deserialize(geoname_t *self, char_array *str) {
213     cmp_ctx_t ctx;
214     msgpack_buffer_t buffer = (msgpack_buffer_t){str, 0};
215     cmp_init(&ctx, &buffer, msgpack_bytes_reader, msgpack_bytes_writer);
216 
217     return geoname_deserialize_ctx(self, &ctx);
218 }
219 
geoname_serialize_ctx(geoname_t * self,cmp_ctx_t * ctx)220 bool geoname_serialize_ctx(geoname_t *self, cmp_ctx_t *ctx) {
221 
222     if (!cmp_write_uint(ctx, self->geonames_id) ||
223         !cmp_write_str_or_nil(ctx, self->name) ||
224         !cmp_write_str_or_nil(ctx, self->canonical) ||
225         !cmp_write_uint(ctx, self->type) ||
226         !cmp_write_str_or_nil(ctx, self->iso_language) ||
227         !cmp_write_bool(ctx, self->has_wikipedia_entry) ||
228         !cmp_write_bool(ctx, self->is_preferred_name) ||
229         !cmp_write_bool(ctx, self->is_short_name) ||
230         !cmp_write_bool(ctx, self->is_colloquial) ||
231         !cmp_write_bool(ctx, self->is_historical) ||
232         !cmp_write_uint(ctx, self->population) ||
233         !cmp_write_double(ctx, self->latitude) ||
234         !cmp_write_double(ctx, self->longitude) ||
235         !cmp_write_str_or_nil(ctx, self->feature_code) ||
236         !cmp_write_str_or_nil(ctx, self->country_code) ||
237         !cmp_write_uint(ctx, self->country_geonames_id) ||
238         !cmp_write_str_or_nil(ctx, self->admin1_code) ||
239         !cmp_write_uint(ctx, self->admin1_geonames_id) ||
240         !cmp_write_str_or_nil(ctx, self->admin2_code) ||
241         !cmp_write_uint(ctx, self->admin2_geonames_id) ||
242         !cmp_write_str_or_nil(ctx, self->admin3_code) ||
243         !cmp_write_uint(ctx, self->admin3_geonames_id) ||
244         !cmp_write_str_or_nil(ctx, self->admin4_code) ||
245         !cmp_write_uint(ctx, self->admin4_geonames_id)
246         ) { return false; }
247 
248     return true;
249 }
250 
geoname_serialize(geoname_t * self,char_array * str)251 bool geoname_serialize(geoname_t *self, char_array *str) {
252     cmp_ctx_t ctx;
253     msgpack_buffer_t buffer = (msgpack_buffer_t){str, 0};
254     cmp_init(&ctx, &buffer, msgpack_bytes_reader, msgpack_bytes_writer);
255 
256     return geoname_serialize_ctx(self, &ctx);
257 }
258 
geoname_print(geoname_t * self)259 void geoname_print(geoname_t *self) {
260     printf("geonames_id: %d\n", self->geonames_id);
261     printf("name: %s\n", char_array_get_string(self->name));
262     printf("canonical: %s\n", char_array_get_string(self->canonical));
263     printf("type: %d\n", self->type);
264     printf("iso_language: %s\n", char_array_get_string(self->iso_language));
265     printf("has_wikipedia_entry: %d\n", self->has_wikipedia_entry);
266     printf("is_preferred: %d\n", self->is_preferred_name);
267     printf("is_short_name: %d\n", self->is_short_name);
268     printf("is_colloquial: %d\n", self->is_colloquial);
269     printf("is_historical: %d\n", self->is_historical);
270     printf("population: %d\n", self->population);
271     printf("latitude: %f\n", self->latitude);
272     printf("longitude: %f\n", self->longitude);
273     printf("feature_code: %s\n", char_array_get_string(self->feature_code));
274     printf("country_code: %s\n", char_array_get_string(self->country_code));
275     printf("country_geonames_id: %d\n", self->country_geonames_id);
276     printf("admin1_code: %s\n", char_array_get_string(self->admin1_code));
277     printf("admin1_geonames_id: %d\n", self->admin1_geonames_id);
278     printf("admin2_code: %s\n", char_array_get_string(self->admin2_code));
279     printf("admin2_geonames_id: %d\n", self->admin2_geonames_id);
280     printf("admin3_code: %s\n", char_array_get_string(self->admin3_code));
281     printf("admin3_geonames_id: %d\n", self->admin3_geonames_id);
282     printf("admin4_code: %s\n", char_array_get_string(self->admin4_code));
283     printf("admin4_geonames_id: %d\n", self->admin4_geonames_id);
284 }
285 
gn_postal_code_new(void)286 gn_postal_code_t *gn_postal_code_new(void) {
287     gn_postal_code_t *self = malloc(sizeof(gn_postal_code_t));
288     if (self == NULL) {
289         return NULL;
290     }
291 
292     self->postal_code = char_array_new_size(GEONAMES_POSTAL_CODE_DEFAULT_LENGTH);
293     self->country_code = char_array_new_size(GEONAMES_COUNTRY_CODE_DEFAULT_LENGTH);
294     self->containing_geoname = char_array_new_size(GEONAMES_NAME_DEFAULT_LENGTH);
295 
296     self->have_lat_lon = false;
297     self->have_containing_geoname = false;
298 
299     self->admin1_ids = uint32_array_new_size(GEONAMES_POSTAL_ADMIN1_IDS_DEFAULT_LENGTH);
300     self->admin2_ids = uint32_array_new_size(GEONAMES_POSTAL_ADMIN2_IDS_DEFAULT_LENGTH);
301     self->admin3_ids = uint32_array_new_size(GEONAMES_POSTAL_ADMIN3_IDS_DEFAULT_LENGTH);
302 
303     if (!(self->postal_code && self->country_code && self->containing_geoname
304           && self->admin1_ids && self->admin2_ids && self->admin3_ids)) {
305         gn_postal_code_destroy(self);
306         return NULL;
307     }
308 
309     return self;
310 }
311 
gn_postal_code_clear(gn_postal_code_t * self)312 void gn_postal_code_clear(gn_postal_code_t *self) {
313     char_array_clear(self->postal_code);
314     char_array_clear(self->country_code);
315     char_array_clear(self->containing_geoname);
316     uint32_array_clear(self->admin1_ids);
317     uint32_array_clear(self->admin2_ids);
318     uint32_array_clear(self->admin3_ids);
319 }
320 
gn_postal_code_destroy(gn_postal_code_t * self)321 void gn_postal_code_destroy(gn_postal_code_t *self) {
322     if (!self)
323         return;
324 
325     if (self->postal_code) {
326         char_array_destroy(self->postal_code);
327     }
328 
329     if (self->country_code) {
330         char_array_destroy(self->country_code);
331     }
332 
333     if (self->containing_geoname) {
334         char_array_destroy(self->containing_geoname);
335     }
336 
337     if (self->admin1_ids) {
338         uint32_array_destroy(self->admin1_ids);
339     }
340 
341     if (self->admin2_ids) {
342         uint32_array_destroy(self->admin2_ids);
343     }
344 
345     if (self->admin3_ids) {
346         uint32_array_destroy(self->admin3_ids);
347     }
348 
349     free(self);
350 }
351 
gn_postal_code_deserialize_ctx(gn_postal_code_t * self,cmp_ctx_t * ctx)352 bool gn_postal_code_deserialize_ctx(gn_postal_code_t *self, cmp_ctx_t *ctx) {
353     uint32_t len;
354 
355     if (!cmp_read_str_size_or_nil(ctx, &self->postal_code, &len)) {
356         return false;
357     }
358 
359     if (!cmp_read_str_size_or_nil(ctx, &self->country_code, &len)) {
360         return false;
361     }
362 
363     if (!cmp_read_uint(ctx, &self->country_geonames_id)) {
364         return false;
365     }
366 
367     if (!cmp_read_bool(ctx, &self->have_containing_geoname)) {
368         return false;
369     }
370 
371     if (self->have_containing_geoname) {
372         if (!cmp_read_str_size_or_nil(ctx, &self->containing_geoname, &len))
373             return false;
374 
375         if (!cmp_read_uint(ctx, &self->containing_geonames_id))
376             return false;
377     }
378 
379     uint32_t array_size;
380 
381     if (!cmp_read_array(ctx, &array_size)) {
382         return false;
383     }
384 
385     if (array_size > 0) {
386         uint32_t admin1_id;
387         for ( ;array_size; array_size--) {
388             if (!cmp_read_uint(ctx, &admin1_id)) {
389                 return false;
390             }
391             uint32_array_push(self->admin1_ids, admin1_id);
392         }
393     }
394 
395     if (!cmp_read_array(ctx, &array_size)) {
396         return false;
397     }
398 
399     if (array_size > 0) {
400         uint32_t admin2_id;
401         for (; array_size > 0; array_size--) {
402             if (!cmp_read_uint(ctx, &admin2_id)) {
403                 return false;
404             }
405             uint32_array_push(self->admin2_ids, admin2_id);
406         }
407     }
408 
409     if (!cmp_read_array(ctx, &array_size)) {
410         return false;
411     }
412 
413     if (array_size > 0) {
414         uint32_t admin3_id;
415         for (; array_size > 0; array_size--) {
416             if (!cmp_read_uint(ctx, &admin3_id)) {
417                 return false;
418             }
419             uint32_array_push(self->admin3_ids, admin3_id);
420         }
421     }
422 
423     return true;
424 }
425 
gn_postal_code_deserialize(gn_postal_code_t * self,char_array * str)426 bool gn_postal_code_deserialize(gn_postal_code_t *self, char_array *str) {
427     cmp_ctx_t ctx;
428     msgpack_buffer_t buffer = (msgpack_buffer_t){str, 0};
429     cmp_init(&ctx, &buffer, msgpack_bytes_reader, msgpack_bytes_writer);
430 
431     return gn_postal_code_deserialize_ctx(self, &ctx);
432 }
433 
gn_postal_code_serialize_ctx(gn_postal_code_t * self,cmp_ctx_t * ctx)434 static bool gn_postal_code_serialize_ctx(gn_postal_code_t *self, cmp_ctx_t *ctx) {
435 
436     if (!cmp_write_str_or_nil(ctx, self->postal_code) ||
437         !cmp_write_str_or_nil(ctx, self->country_code) ||
438         !cmp_write_uint(ctx, self->country_geonames_id) ||
439         !cmp_write_bool(ctx, self->have_containing_geoname) ||
440         (self->have_containing_geoname &&
441             (!cmp_write_str_or_nil(ctx, self->containing_geoname) ||
442              !cmp_write_uint(ctx, self->containing_geonames_id)
443             )
444         ) ||
445         !cmp_write_uint_vector(ctx, self->admin1_ids) ||
446         !cmp_write_uint_vector(ctx, self->admin2_ids) ||
447         !cmp_write_uint_vector(ctx, self->admin3_ids)
448         ) { return false; }
449 
450     return true;
451 }
452 
gn_postal_code_serialize(gn_postal_code_t * self,char_array * str)453 bool gn_postal_code_serialize(gn_postal_code_t *self, char_array *str) {
454     cmp_ctx_t ctx;
455     msgpack_buffer_t buffer = (msgpack_buffer_t){str, 0};
456     cmp_init(&ctx, &buffer, msgpack_bytes_reader, msgpack_bytes_writer);
457 
458     return gn_postal_code_serialize_ctx(self, &ctx);
459 
460 }
461 
gn_postal_code_print(gn_postal_code_t * self)462 void gn_postal_code_print(gn_postal_code_t *self) {
463     int i;
464     printf("postal_code: %s\n", char_array_get_string(self->postal_code));
465     printf("country_code: %s\n", char_array_get_string(self->country_code));
466     printf("have_containing_geoname: %d\n", self->have_containing_geoname);
467     if (self->have_containing_geoname) {
468         printf("containing_geoname: %s\n", char_array_get_string(self->containing_geoname));
469         printf("containing_geonames_id: %d\n", self->containing_geonames_id);
470     }
471     printf("admin1_ids: [ ");
472     for (i = 0; i < self->admin1_ids->n; i++) {
473         printf("%d ", self->admin1_ids->a[i]);
474     }
475     printf("]\n");
476     printf("admin2_ids: [ ");
477     for (i = 0; i < self->admin2_ids->n; i++) {
478         printf("%d ", self->admin2_ids->a[i]);
479     }
480     printf("]\n");
481     printf("admin3_ids: [ ");
482     for (i = 0; i < self->admin3_ids->n; i++) {
483         printf("%d ", self->admin3_ids->a[i]);
484     }
485     printf("]\n");
486 }
487 
488 
geonames_generic_serialize(geonames_generic_t * gn,char_array * str)489 bool geonames_generic_serialize(geonames_generic_t *gn, char_array *str) {
490     cmp_ctx_t ctx;
491     msgpack_buffer_t buffer = (msgpack_buffer_t){str, 0};
492     cmp_init(&ctx, &buffer, msgpack_bytes_reader, msgpack_bytes_writer);
493 
494     if (!cmp_write_u8(&ctx, (uint8_t)gn->type)) {
495         return false;
496     }
497 
498     if (gn->type == GEONAMES_PLACE) {
499         return geoname_serialize_ctx(gn->geoname, &ctx);
500     } else if (gn->type == GEONAMES_POSTAL_CODE) {
501         return gn_postal_code_serialize_ctx(gn->postal_code, &ctx);
502     }
503 
504     return false;
505 }
506 
geonames_generic_deserialize(gn_type * type,geoname_t * geoname,gn_postal_code_t * postal_code,char_array * str)507 bool geonames_generic_deserialize(gn_type *type, geoname_t *geoname, gn_postal_code_t *postal_code, char_array *str) {
508     cmp_ctx_t ctx;
509     msgpack_buffer_t buffer = (msgpack_buffer_t){str, 0};
510     cmp_init(&ctx, &buffer, msgpack_bytes_reader, msgpack_bytes_writer);
511 
512     uint8_t geonames_type;
513 
514     if (!cmp_read_u8(&ctx, &geonames_type)) {
515         return false;
516     }
517 
518     *type = geonames_type;
519 
520     if (geonames_type == GEONAMES_PLACE) {
521         return geoname_deserialize_ctx(geoname, &ctx);
522     } else if (geonames_type == GEONAMES_POSTAL_CODE) {
523         return gn_postal_code_deserialize_ctx(postal_code, &ctx);
524     }
525 
526 
527     return false;
528 
529 }
530