geoip2_c 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +3 -4
  3. data/README.md +21 -3
  4. data/docker-compose.yml +15 -1
  5. data/dockerfiles/{Dockerfile-ruby2.1 → Dockerfile-ruby2.5} +1 -1
  6. data/dockerfiles/{Dockerfile-ruby2.2 → Dockerfile-ruby2.6} +1 -1
  7. data/ext/geoip2/geoip2.c +5 -3
  8. data/ext/geoip2/libmaxminddb/.gitignore +3 -0
  9. data/ext/geoip2/libmaxminddb/.travis.yml +24 -2
  10. data/ext/geoip2/libmaxminddb/Changes.md +39 -0
  11. data/ext/geoip2/libmaxminddb/README.dev.md +41 -30
  12. data/ext/geoip2/libmaxminddb/README.md +3 -3
  13. data/ext/geoip2/libmaxminddb/bin/Makefile.am +5 -0
  14. data/ext/geoip2/libmaxminddb/bin/mmdblookup.c +333 -15
  15. data/ext/geoip2/libmaxminddb/configure.ac +5 -5
  16. data/ext/geoip2/libmaxminddb/dev-bin/ppa-release.sh +8 -5
  17. data/ext/geoip2/libmaxminddb/dev-bin/release.sh +7 -0
  18. data/ext/geoip2/libmaxminddb/dev-bin/valgrind-all.pl +10 -3
  19. data/ext/geoip2/libmaxminddb/include/maxminddb.h +11 -2
  20. data/ext/geoip2/libmaxminddb/projects/VS12/libmaxminddb.vcxproj +3 -1
  21. data/ext/geoip2/libmaxminddb/projects/VS12/libmaxminddb.vcxproj.filters +7 -1
  22. data/ext/geoip2/libmaxminddb/src/Makefile.am +18 -2
  23. data/ext/geoip2/libmaxminddb/src/data-pool.c +180 -0
  24. data/ext/geoip2/libmaxminddb/src/data-pool.h +52 -0
  25. data/ext/geoip2/libmaxminddb/src/maxminddb.c +58 -48
  26. data/ext/geoip2/libmaxminddb/t/Makefile.am +6 -2
  27. data/ext/geoip2/libmaxminddb/t/bad_databases_t.c +1 -0
  28. data/ext/geoip2/libmaxminddb/t/basic_lookup_t.c +35 -0
  29. data/ext/geoip2/libmaxminddb/t/data-pool-t.c +374 -0
  30. data/ext/geoip2/libmaxminddb/t/external_symbols_t.pl +106 -0
  31. data/lib/geoip2/version.rb +1 -1
  32. metadata +9 -7
  33. data/dockerfiles/Dockerfile-ruby2.3 +0 -8
@@ -1,6 +1,7 @@
1
1
  #if HAVE_CONFIG_H
2
2
  #include <config.h>
3
3
  #endif
4
+ #include "data-pool.h"
4
5
  #include "maxminddb.h"
5
6
  #include "maxminddb-compat-util.h"
6
7
  #include <assert.h>
@@ -130,6 +131,9 @@ typedef struct record_info_s {
130
131
  /* This is 128kb */
131
132
  #define METADATA_BLOCK_MAX_SIZE 131072
132
133
 
134
+ // 64 leads us to allocating 4 KiB on a 64bit system.
135
+ #define MMDB_POOL_INIT_SIZE 64
136
+
133
137
  /* *INDENT-OFF* */
134
138
  /* --prototypes automatically generated by dev-bin/regen-prototypes.pl - don't remove this comment */
135
139
  LOCAL int map_file(MMDB_s *const mmdb);
@@ -176,8 +180,10 @@ LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset,
176
180
  LOCAL int get_ext_type(int raw_ext_type);
177
181
  LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr,
178
182
  int ptr_size);
179
- LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset,
183
+ LOCAL int get_entry_data_list(MMDB_s *mmdb,
184
+ uint32_t offset,
180
185
  MMDB_entry_data_list_s *const entry_data_list,
186
+ MMDB_data_pool_s *const pool,
181
187
  int depth);
182
188
  LOCAL float get_ieee754_float(const uint8_t *restrict p);
183
189
  LOCAL double get_ieee754_double(const uint8_t *restrict p);
@@ -186,7 +192,6 @@ LOCAL uint32_t get_uint24(const uint8_t *p);
186
192
  LOCAL uint32_t get_uint16(const uint8_t *p);
187
193
  LOCAL uint64_t get_uintX(const uint8_t *p, int length);
188
194
  LOCAL int32_t get_sintX(const uint8_t *p, int length);
189
- LOCAL MMDB_entry_data_list_s *new_entry_data_list(void);
190
195
  LOCAL void free_mmdb_struct(MMDB_s *const mmdb);
191
196
  LOCAL void free_languages_metadata(MMDB_s *mmdb);
192
197
  LOCAL void free_descriptions_metadata(MMDB_s *mmdb);
@@ -241,7 +246,7 @@ int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb)
241
246
  }
242
247
  mmdb->flags = flags;
243
248
 
244
- if (MMDB_SUCCESS != (status = map_file(mmdb)) ) {
249
+ if (MMDB_SUCCESS != (status = map_file(mmdb))) {
245
250
  goto cleanup;
246
251
  }
247
252
 
@@ -297,6 +302,15 @@ int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb)
297
302
  mmdb->ipv4_start_node.node_value = 0;
298
303
  mmdb->ipv4_start_node.netmask = 0;
299
304
 
305
+ // We do this immediately as otherwise there is a race to set
306
+ // ipv4_start_node.node_value and ipv4_start_node.netmask.
307
+ if (mmdb->metadata.ip_version == 6) {
308
+ status = find_ipv4_start_node(mmdb);
309
+ if (status != MMDB_SUCCESS) {
310
+ goto cleanup;
311
+ }
312
+ }
313
+
300
314
  cleanup:
301
315
  if (MMDB_SUCCESS != status) {
302
316
  int saved_errno = errno;
@@ -361,7 +375,11 @@ LOCAL int map_file(MMDB_s *const mmdb)
361
375
  ssize_t size;
362
376
  int status = MMDB_SUCCESS;
363
377
 
364
- int fd = open(mmdb->filename, O_RDONLY);
378
+ int flags = O_RDONLY;
379
+ #ifdef O_CLOEXEC
380
+ flags |= O_CLOEXEC;
381
+ #endif
382
+ int fd = open(mmdb->filename, flags);
365
383
  struct stat s;
366
384
  if (fd < 0 || fstat(fd, &s)) {
367
385
  status = MMDB_FILE_OPEN_ERROR;
@@ -1634,15 +1652,33 @@ int MMDB_get_metadata_as_entry_data_list(
1634
1652
  int MMDB_get_entry_data_list(
1635
1653
  MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list)
1636
1654
  {
1637
- *entry_data_list = new_entry_data_list();
1638
- if (NULL == *entry_data_list) {
1655
+ MMDB_data_pool_s *const pool = data_pool_new(MMDB_POOL_INIT_SIZE);
1656
+ if (!pool) {
1657
+ return MMDB_OUT_OF_MEMORY_ERROR;
1658
+ }
1659
+
1660
+ MMDB_entry_data_list_s *const list = data_pool_alloc(pool);
1661
+ if (!list) {
1662
+ data_pool_destroy(pool);
1663
+ return MMDB_OUT_OF_MEMORY_ERROR;
1664
+ }
1665
+
1666
+ int const status = get_entry_data_list(start->mmdb, start->offset, list,
1667
+ pool, 0);
1668
+
1669
+ *entry_data_list = data_pool_to_list(pool);
1670
+ if (!*entry_data_list) {
1671
+ data_pool_destroy(pool);
1639
1672
  return MMDB_OUT_OF_MEMORY_ERROR;
1640
1673
  }
1641
- return get_entry_data_list(start->mmdb, start->offset, *entry_data_list, 0);
1674
+
1675
+ return status;
1642
1676
  }
1643
1677
 
1644
- LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset,
1678
+ LOCAL int get_entry_data_list(MMDB_s *mmdb,
1679
+ uint32_t offset,
1645
1680
  MMDB_entry_data_list_s *const entry_data_list,
1681
+ MMDB_data_pool_s *const pool,
1646
1682
  int depth)
1647
1683
  {
1648
1684
  if (depth >= MAXIMUM_DATA_STRUCTURE_DEPTH) {
@@ -1672,7 +1708,7 @@ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset,
1672
1708
 
1673
1709
  int status =
1674
1710
  get_entry_data_list(mmdb, last_offset, entry_data_list,
1675
- depth);
1711
+ pool, depth);
1676
1712
  if (MMDB_SUCCESS != status) {
1677
1713
  DEBUG_MSG("get_entry_data_list on pointer failed.");
1678
1714
  return status;
@@ -1685,26 +1721,22 @@ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset,
1685
1721
  {
1686
1722
  uint32_t array_size = entry_data_list->entry_data.data_size;
1687
1723
  uint32_t array_offset = entry_data_list->entry_data.offset_to_next;
1688
- MMDB_entry_data_list_s *previous = entry_data_list;
1689
1724
  while (array_size-- > 0) {
1690
- MMDB_entry_data_list_s *entry_data_list_to = previous->next =
1691
- new_entry_data_list();
1692
- if (NULL == entry_data_list_to) {
1725
+ MMDB_entry_data_list_s *entry_data_list_to =
1726
+ data_pool_alloc(pool);
1727
+ if (!entry_data_list_to) {
1693
1728
  return MMDB_OUT_OF_MEMORY_ERROR;
1694
1729
  }
1695
1730
 
1696
1731
  int status =
1697
1732
  get_entry_data_list(mmdb, array_offset, entry_data_list_to,
1698
- depth);
1733
+ pool, depth);
1699
1734
  if (MMDB_SUCCESS != status) {
1700
1735
  DEBUG_MSG("get_entry_data_list on array element failed.");
1701
1736
  return status;
1702
1737
  }
1703
1738
 
1704
1739
  array_offset = entry_data_list_to->entry_data.offset_to_next;
1705
- while (previous->next) {
1706
- previous = previous->next;
1707
- }
1708
1740
  }
1709
1741
  entry_data_list->entry_data.offset_to_next = array_offset;
1710
1742
 
@@ -1715,45 +1747,33 @@ LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset,
1715
1747
  uint32_t size = entry_data_list->entry_data.data_size;
1716
1748
 
1717
1749
  offset = entry_data_list->entry_data.offset_to_next;
1718
- MMDB_entry_data_list_s *previous = entry_data_list;
1719
1750
  while (size-- > 0) {
1720
- MMDB_entry_data_list_s *entry_data_list_to = previous->next =
1721
- new_entry_data_list();
1722
- if (NULL == entry_data_list_to) {
1751
+ MMDB_entry_data_list_s *list_key = data_pool_alloc(pool);
1752
+ if (!list_key) {
1723
1753
  return MMDB_OUT_OF_MEMORY_ERROR;
1724
1754
  }
1725
1755
 
1726
1756
  int status =
1727
- get_entry_data_list(mmdb, offset, entry_data_list_to,
1728
- depth);
1757
+ get_entry_data_list(mmdb, offset, list_key, pool, depth);
1729
1758
  if (MMDB_SUCCESS != status) {
1730
1759
  DEBUG_MSG("get_entry_data_list on map key failed.");
1731
1760
  return status;
1732
1761
  }
1733
1762
 
1734
- while (previous->next) {
1735
- previous = previous->next;
1736
- }
1737
-
1738
- offset = entry_data_list_to->entry_data.offset_to_next;
1739
- entry_data_list_to = previous->next =
1740
- new_entry_data_list();
1763
+ offset = list_key->entry_data.offset_to_next;
1741
1764
 
1742
- if (NULL == entry_data_list_to) {
1765
+ MMDB_entry_data_list_s *list_value = data_pool_alloc(pool);
1766
+ if (!list_value) {
1743
1767
  return MMDB_OUT_OF_MEMORY_ERROR;
1744
1768
  }
1745
1769
 
1746
- status = get_entry_data_list(mmdb, offset, entry_data_list_to,
1770
+ status = get_entry_data_list(mmdb, offset, list_value, pool,
1747
1771
  depth);
1748
1772
  if (MMDB_SUCCESS != status) {
1749
1773
  DEBUG_MSG("get_entry_data_list on map element failed.");
1750
1774
  return status;
1751
1775
  }
1752
-
1753
- while (previous->next) {
1754
- previous = previous->next;
1755
- }
1756
- offset = entry_data_list_to->entry_data.offset_to_next;
1776
+ offset = list_value->entry_data.offset_to_next;
1757
1777
  }
1758
1778
  entry_data_list->entry_data.offset_to_next = offset;
1759
1779
  }
@@ -1832,22 +1852,12 @@ LOCAL int32_t get_sintX(const uint8_t *p, int length)
1832
1852
  return (int32_t)get_uintX(p, length);
1833
1853
  }
1834
1854
 
1835
- LOCAL MMDB_entry_data_list_s *new_entry_data_list(void)
1836
- {
1837
- /* We need calloc here in order to ensure that the ->next pointer in the
1838
- * struct doesn't point to some random address. */
1839
- return calloc(1, sizeof(MMDB_entry_data_list_s));
1840
- }
1841
-
1842
1855
  void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list)
1843
1856
  {
1844
1857
  if (entry_data_list == NULL) {
1845
1858
  return;
1846
1859
  }
1847
- if (entry_data_list->next) {
1848
- MMDB_free_entry_data_list(entry_data_list->next);
1849
- }
1850
- free(entry_data_list);
1860
+ data_pool_destroy(entry_data_list->pool);
1851
1861
  }
1852
1862
 
1853
1863
  void MMDB_close(MMDB_s *const mmdb)
@@ -12,12 +12,16 @@ libmmdbtest_la_SOURCES = maxminddb_test_helper.c
12
12
 
13
13
  check_PROGRAMS = \
14
14
  bad_pointers_t bad_databases_t basic_lookup_t data_entry_list_t \
15
- data_types_t dump_t get_value_t get_value_pointer_bug_t \
15
+ data-pool-t data_types_t dump_t get_value_t get_value_pointer_bug_t \
16
16
  ipv4_start_cache_t ipv6_lookup_in_ipv4_t metadata_t metadata_pointers_t \
17
17
  no_map_get_value_t read_node_t threads_t version_t
18
18
 
19
+ data_pool_t_CFLAGS = $(CFLAGS) -I$(top_srcdir)/src
20
+ data_pool_t_LDFLAGS = $(AM_LDFLAGS) -lm
21
+ data_pool_t_SOURCES = data-pool-t.c ../src/data-pool.c
22
+
19
23
  threads_t_CFLAGS = $(CFLAGS) -pthread
20
24
 
21
- TESTS = $(check_PROGRAMS) compile_c++_t.pl mmdblookup_t.pl
25
+ TESTS = $(check_PROGRAMS) compile_c++_t.pl external_symbols_t.pl mmdblookup_t.pl
22
26
 
23
27
  LDADD = libmmdbtest.la libtap/libtap.a
@@ -26,6 +26,7 @@ int test_read(const char *path, const struct stat *UNUSED(
26
26
 
27
27
  if (status != MMDB_SUCCESS) {
28
28
  ok(1, "received error when opening %s", path);
29
+ free(mmdb);
29
30
  return 0;
30
31
  }
31
32
 
@@ -1,5 +1,7 @@
1
1
  #include "maxminddb_test_helper.h"
2
2
 
3
+ static void test_big_lookup(void);
4
+
3
5
  /* These globals are gross but it's the easiest way to mix calling
4
6
  * for_all_modes() and for_all_record_sizes() */
5
7
  static int Current_Mode;
@@ -164,9 +166,42 @@ void all_record_sizes(int mode, const char *description)
164
166
  }
165
167
  }
166
168
 
169
+ static void test_big_lookup(void)
170
+ {
171
+ const char *const db_filename = "GeoIP2-Precision-Enterprise-Test.mmdb";
172
+ const char *const db_path = test_database_path(db_filename);
173
+ ok(db_path != NULL, "got database path");
174
+
175
+ MMDB_s * const mmdb = open_ok(db_path, MMDB_MODE_MMAP, "mmap mode");
176
+ ok(mmdb != NULL, "opened MMDB");
177
+ free((char *)db_path);
178
+
179
+ int gai_err = 0, mmdb_err = 0;
180
+ const char *const ip_address = "81.2.69.160";
181
+ MMDB_lookup_result_s result = MMDB_lookup_string(mmdb, ip_address, &gai_err,
182
+ &mmdb_err);
183
+ ok(gai_err == 0, "no getaddrinfo error");
184
+ ok(mmdb_err == MMDB_SUCCESS, "no error from maxminddb library");
185
+ ok(result.found_entry, "found IP");
186
+
187
+ MMDB_entry_data_list_s *entry_data_list = NULL;
188
+ ok(
189
+ MMDB_get_entry_data_list(&result.entry,
190
+ &entry_data_list) == MMDB_SUCCESS,
191
+ "successfully looked up entry data list"
192
+ );
193
+ ok(entry_data_list != NULL, "got an entry_data_list");
194
+
195
+ MMDB_free_entry_data_list(entry_data_list);
196
+
197
+ MMDB_close(mmdb);
198
+ free(mmdb);
199
+ }
200
+
167
201
  int main(void)
168
202
  {
169
203
  plan(NO_PLAN);
170
204
  for_all_modes(&all_record_sizes);
205
+ test_big_lookup();
171
206
  done_testing();
172
207
  }
@@ -0,0 +1,374 @@
1
+ #include <assert.h>
2
+ #include <data-pool.h>
3
+ #include <inttypes.h>
4
+ #include "libtap/tap.h"
5
+ #include <math.h>
6
+ #include "maxminddb_test_helper.h"
7
+
8
+ static void test_data_pool_new(void);
9
+ static void test_data_pool_destroy(void);
10
+ static void test_data_pool_alloc(void);
11
+ static void test_data_pool_to_list(void);
12
+ static bool create_and_check_list(size_t const,
13
+ size_t const);
14
+ static void check_block_count(MMDB_entry_data_list_s const *const,
15
+ size_t const);
16
+
17
+ int main(void)
18
+ {
19
+ plan(NO_PLAN);
20
+ test_data_pool_new();
21
+ test_data_pool_destroy();
22
+ test_data_pool_alloc();
23
+ test_data_pool_to_list();
24
+ done_testing();
25
+ }
26
+
27
+ static void test_data_pool_new(void)
28
+ {
29
+ {
30
+ MMDB_data_pool_s *const pool = data_pool_new(0);
31
+ ok(!pool, "size 0 is not valid");
32
+ }
33
+
34
+ {
35
+ MMDB_data_pool_s *const pool = data_pool_new(SIZE_MAX - 10);
36
+ ok(!pool, "very large size is not valid");
37
+ }
38
+
39
+ {
40
+ MMDB_data_pool_s *const pool = data_pool_new(512);
41
+ ok(pool != NULL, "size 512 is valid");
42
+ cmp_ok(pool->size, "==", 512, "size is 512");
43
+ cmp_ok(pool->used, "==", 0, "used size is 0");
44
+ data_pool_destroy(pool);
45
+ }
46
+ }
47
+
48
+ static void test_data_pool_destroy(void)
49
+ {
50
+ {
51
+ data_pool_destroy(NULL);
52
+ }
53
+
54
+ {
55
+ MMDB_data_pool_s *const pool = data_pool_new(512);
56
+ ok(pool != NULL, "created pool");
57
+ data_pool_destroy(pool);
58
+ }
59
+ }
60
+
61
+ static void test_data_pool_alloc(void)
62
+ {
63
+ {
64
+ MMDB_data_pool_s *const pool = data_pool_new(1);
65
+ ok(pool != NULL, "created pool");
66
+ cmp_ok(pool->used, "==", 0, "used size starts at 0");
67
+
68
+ MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
69
+ ok(entry1 != NULL, "allocated first entry");
70
+ // Arbitrary so that we can recognize it.
71
+ entry1->entry_data.offset = (uint32_t)123;
72
+
73
+ cmp_ok(pool->size, "==", 1, "size is still 1");
74
+ cmp_ok(pool->used, "==", 1, "used size is 1 after taking one");
75
+
76
+ MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
77
+ ok(entry2 != NULL, "got another entry");
78
+ ok(entry1 != entry2, "second entry is different from first entry");
79
+
80
+ cmp_ok(pool->size, "==", 2, "size is 2 (new block)");
81
+ cmp_ok(pool->used, "==", 1, "used size is 1 in current block");
82
+
83
+ ok(entry1->entry_data.offset == 123,
84
+ "accessing the original entry's memory is ok");
85
+
86
+ data_pool_destroy(pool);
87
+ }
88
+
89
+ {
90
+ size_t const initial_size = 10;
91
+ MMDB_data_pool_s *const pool = data_pool_new(initial_size);
92
+ ok(pool != NULL, "created pool");
93
+
94
+ MMDB_entry_data_list_s *entry1 = NULL;
95
+ for (size_t i = 0; i < initial_size; i++) {
96
+ MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
97
+ ok(entry != NULL, "got an entry");
98
+ // Give each a unique number so we can check it.
99
+ entry->entry_data.offset = (uint32_t)i;
100
+ if (i == 0) {
101
+ entry1 = entry;
102
+ }
103
+ }
104
+
105
+ cmp_ok(pool->size, "==", initial_size, "size is the initial size");
106
+ cmp_ok(pool->used, "==", initial_size, "used size is as expected");
107
+
108
+ MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
109
+ ok(entry != NULL, "got an entry");
110
+ entry->entry_data.offset = (uint32_t)initial_size;
111
+
112
+ cmp_ok(pool->size, "==", initial_size * 2,
113
+ "size is the initial size*2");
114
+ cmp_ok(pool->used, "==", 1, "used size is as expected");
115
+
116
+ MMDB_entry_data_list_s *const list = data_pool_to_list(pool);
117
+
118
+ MMDB_entry_data_list_s *element = list;
119
+ for (size_t i = 0; i < initial_size + 1; i++) {
120
+ ok(
121
+ element->entry_data.offset == (uint32_t)i,
122
+ "found offset %" PRIu32 ", should have %zu",
123
+ element->entry_data.offset,
124
+ i
125
+ );
126
+ element = element->next;
127
+ }
128
+
129
+ ok(entry1->entry_data.offset == (uint32_t)0,
130
+ "accessing entry1's original memory is ok after growing the pool");
131
+
132
+ data_pool_destroy(pool);
133
+ }
134
+ }
135
+
136
+ static void test_data_pool_to_list(void)
137
+ {
138
+ {
139
+ size_t const initial_size = 16;
140
+ MMDB_data_pool_s *const pool = data_pool_new(initial_size);
141
+ ok(pool != NULL, "created pool");
142
+
143
+ MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
144
+ ok(entry1 != NULL, "got an entry");
145
+
146
+ MMDB_entry_data_list_s *const list_one_element
147
+ = data_pool_to_list(pool);
148
+ ok(list_one_element != NULL, "got a list");
149
+ ok(list_one_element == entry1,
150
+ "list's first element is the first we retrieved");
151
+ ok(list_one_element->next == NULL, "list is one element in size");
152
+
153
+ MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
154
+ ok(entry2 != NULL, "got another entry");
155
+
156
+ MMDB_entry_data_list_s *const list_two_elements
157
+ = data_pool_to_list(pool);
158
+ ok(list_two_elements != NULL, "got a list");
159
+ ok(list_two_elements == entry1,
160
+ "list's first element is the first we retrieved");
161
+ ok(list_two_elements->next != NULL, "list has a second element");
162
+
163
+ MMDB_entry_data_list_s *const second_element = list_two_elements->next;
164
+ ok(second_element == entry2,
165
+ "second item in list is second we retrieved");
166
+ ok(second_element->next == NULL, "list ends with the second element");
167
+
168
+ data_pool_destroy(pool);
169
+ }
170
+
171
+ {
172
+ size_t const initial_size = 1;
173
+ MMDB_data_pool_s *const pool = data_pool_new(initial_size);
174
+ ok(pool != NULL, "created pool");
175
+
176
+ MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
177
+ ok(entry1 != NULL, "got an entry");
178
+
179
+ MMDB_entry_data_list_s *const list_one_element
180
+ = data_pool_to_list(pool);
181
+ ok(list_one_element != NULL, "got a list");
182
+ ok(list_one_element == entry1,
183
+ "list's first element is the first we retrieved");
184
+ ok(list_one_element->next == NULL, "list ends with this element");
185
+
186
+ data_pool_destroy(pool);
187
+ }
188
+
189
+ {
190
+ size_t const initial_size = 2;
191
+ MMDB_data_pool_s *const pool = data_pool_new(initial_size);
192
+ ok(pool != NULL, "created pool");
193
+
194
+ MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
195
+ ok(entry1 != NULL, "got an entry");
196
+
197
+ MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
198
+ ok(entry2 != NULL, "got an entry");
199
+ ok(entry1 != entry2, "second entry is different from the first");
200
+
201
+ MMDB_entry_data_list_s *const list_element1 = data_pool_to_list(pool);
202
+ ok(list_element1 != NULL, "got a list");
203
+ ok(list_element1 == entry1,
204
+ "list's first element is the first we retrieved");
205
+
206
+ MMDB_entry_data_list_s *const list_element2 = list_element1->next;
207
+ ok(list_element2 == entry2,
208
+ "second element is the second we retrieved");
209
+ ok(list_element2->next == NULL, "list ends with this element");
210
+
211
+ data_pool_destroy(pool);
212
+ }
213
+
214
+ {
215
+ diag("starting test: fill one block save for one spot");
216
+ ok(
217
+ create_and_check_list(3, 2),
218
+ "fill one block save for one spot"
219
+ );
220
+ }
221
+
222
+ {
223
+ diag("starting test: fill one block");
224
+ ok(
225
+ create_and_check_list(3, 3),
226
+ "fill one block"
227
+ );
228
+ }
229
+
230
+ {
231
+ diag("starting test: fill one block and use one spot in the next block");
232
+ ok(
233
+ create_and_check_list(3, 3 + 1),
234
+ "fill one block and use one spot in the next block"
235
+ );
236
+ }
237
+
238
+ {
239
+ diag("starting test: fill two blocks save for one spot");
240
+ ok(
241
+ create_and_check_list(3, 3 + 3 * 2 - 1),
242
+ "fill two blocks save for one spot"
243
+ );
244
+ }
245
+
246
+ {
247
+ diag("starting test: fill two blocks");
248
+ ok(
249
+ create_and_check_list(3, 3 + 3 * 2),
250
+ "fill two blocks"
251
+ );
252
+ }
253
+
254
+ {
255
+ diag("starting test: fill two blocks and use one spot in the next");
256
+ ok(
257
+ create_and_check_list(3, 3 + 3 * 2 + 1),
258
+ "fill two blocks and use one spot in the next"
259
+ );
260
+ }
261
+
262
+ {
263
+ diag("starting test: fill three blocks save for one spot");
264
+ ok(
265
+ create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2 - 1),
266
+ "fill three blocks save for one spot"
267
+ );
268
+ }
269
+
270
+ {
271
+ diag("starting test: fill three blocks");
272
+ ok(
273
+ create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2),
274
+ "fill three blocks"
275
+ );
276
+ }
277
+
278
+ // It would be nice to have a larger number of these, but it's expensive to
279
+ // run many. We currently hardcode what this will be anyway, so varying
280
+ // this is not very interesting.
281
+ size_t const initial_sizes[] = { 1, 2, 32, 64, 128, 256 };
282
+
283
+ size_t const max_element_count = 4096;
284
+
285
+ for (size_t i = 0; i < sizeof(initial_sizes) / sizeof(initial_sizes[0]);
286
+ i++) {
287
+ size_t const initial_size = initial_sizes[i];
288
+
289
+ for (size_t element_count = 0; element_count < max_element_count;
290
+ element_count++) {
291
+ assert(create_and_check_list(initial_size, element_count));
292
+ }
293
+ }
294
+ }
295
+
296
+ // Use assert() rather than libtap as libtap is significantly slower and we run
297
+ // this frequently.
298
+ static bool create_and_check_list(size_t const initial_size,
299
+ size_t const element_count)
300
+ {
301
+ MMDB_data_pool_s *const pool = data_pool_new(initial_size);
302
+ assert(pool != NULL);
303
+
304
+ assert(pool->used == 0);
305
+
306
+ // Hold on to the pointers as we initially see them so that we can check
307
+ // they are still valid after building the list.
308
+ MMDB_entry_data_list_s **const entry_array
309
+ = calloc(element_count, sizeof(MMDB_entry_data_list_s *));
310
+ assert(entry_array != NULL);
311
+
312
+ for (size_t i = 0; i < element_count; i++) {
313
+ MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
314
+ assert(entry != NULL);
315
+
316
+ entry->entry_data.offset = (uint32_t)i;
317
+
318
+ entry_array[i] = entry;
319
+ }
320
+
321
+ MMDB_entry_data_list_s *const list = data_pool_to_list(pool);
322
+
323
+ if (element_count == 0) {
324
+ assert(list == NULL);
325
+ data_pool_destroy(pool);
326
+ free(entry_array);
327
+ return true;
328
+ }
329
+
330
+ assert(list != NULL);
331
+
332
+ MMDB_entry_data_list_s *element = list;
333
+ for (size_t i = 0; i < element_count; i++) {
334
+ assert(element->entry_data.offset == (uint32_t)i);
335
+
336
+ assert(element == entry_array[i]);
337
+
338
+ element = element->next;
339
+ }
340
+ assert(element == NULL);
341
+
342
+ check_block_count(list, initial_size);
343
+
344
+ data_pool_destroy(pool);
345
+ free(entry_array);
346
+ return true;
347
+ }
348
+
349
+ // Use assert() rather than libtap as libtap is significantly slower and we run
350
+ // this frequently.
351
+ static void check_block_count(MMDB_entry_data_list_s const *const list,
352
+ size_t const initial_size)
353
+ {
354
+ size_t got_block_count = 0;
355
+ size_t got_element_count = 0;
356
+
357
+ MMDB_entry_data_list_s const *element = list;
358
+ while (element) {
359
+ got_element_count++;
360
+
361
+ if (element->pool) {
362
+ got_block_count++;
363
+ }
364
+
365
+ element = element->next;
366
+ }
367
+
368
+ // Because <number of elements> = <initial size> * 2^(number of blocks)
369
+ double const a = ceil((double)got_element_count / (double)initial_size);
370
+ double const b = log2(a);
371
+ size_t const expected_block_count = ((size_t)b) + 1;
372
+
373
+ assert(got_block_count == expected_block_count);
374
+ }