geoip2_c 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }