@@ -54,13 +54,37 @@ var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bi
5454defer db.close();
5555```
5656
57- Use ` ArenaAllocator ` for best performance, see [ benchmarks] ( ./benchmarks/ ) .
57+ Each ` lookup ` result owns an arena with all decoded allocations.
58+ Call ` deinit() ` to free it or use ` ArenaAllocator ` with ` reset() ` ,
59+ see [ benchmarks] ( ./benchmarks/lookup.zig ) .
60+
61+ ``` zig
62+ if (try db.lookup(maxminddb.geolite2.City, allocator, ip, .{})) |result| {
63+ defer result.deinit();
64+ std.debug.print("{f} {s}\n", .{ result.network, result.value.city.names.?.get("en").? });
65+ }
66+
67+ var arena = std.heap.ArenaAllocator.init(allocator);
68+ defer arena.deinit();
69+
70+ const arena_allocator = arena.allocator();
71+ for (ips) |ip| {
72+ if (try db.lookup(maxminddb.geolite2.City, arena_allocator, ip, .{})) |result| {
73+ std.debug.print("{f} {s}\n", .{ result.network, result.value.city.names.?.get("en").? });
74+ }
75+
76+ _ = arena.reset(.retain_capacity);
77+ }
78+ ```
5879
5980If you don't need all the fields, use ` .only ` to decode only the top-level fields you want.
6081
6182``` zig
6283const fields = &.{ "city", "country" };
63- const city = try db.lookup(allocator, maxminddb.geolite2.City, ip, .{ .only = fields });
84+ if (try db.lookup(maxminddb.geolite2.City, allocator, ip, .{ .only = fields })) |result| {
85+ defer result.deinit();
86+ std.debug.print("{f} {s}\n", .{ result.network, result.value.city.names.?.get("en").? });
87+ }
6488```
6589
6690Alternatively, define your own struct with only the fields you need.
@@ -74,27 +98,42 @@ const MyCity = struct {
7498 } = .{},
7599};
76100
77- const city = try db.lookup(allocator, MyCity, ip, .{});
101+ if (try db.lookup(MyCity, allocator, ip, .{})) |result| {
102+ defer result.deinit();
103+ std.debug.print("{s}\n", .{result.value.city.names.en});
104+ }
78105```
79106
80107Use ` any.Value ` to decode any record without knowing the schema.
81108
82109``` zig
83- const result = try db.lookup(allocator, maxminddb.any.Value, ip, .{ .only = fields });
84- if (result) |r| {
110+ if ( try db.lookup(maxminddb.any.Value, allocator, ip, .{ .only = fields })) |result| {
111+ defer result.deinit();
85112 // Formats as compact JSON.
86- std.debug.print("{f}\n", .{r.value});
113+ std.debug.print("{f}\n", .{result.value});
114+ }
115+ ```
116+
117+ Use ` lookupWithCache ` to skip decoding when different IPs resolve to the same record.
118+ The cache owns decoded memory, so results don't need to be individually freed.
119+
120+ ``` zig
121+ var cache = try maxminddb.Cache(maxminddb.geolite2.City).init(allocator, .{});
122+ defer cache.deinit();
123+
124+ if (try db.lookupWithCache(maxminddb.geolite2.City, &cache, ip, .{})) |result| {
125+ std.debug.print("{f} {s}\n", .{ result.network, result.value.city.names.?.get("en").? });
87126}
88127```
89128
90129Here are reference results on Apple M2 Pro (1M random IPv4 lookups against GeoLite2-City
91130with ` ipv4_index_first_n_bits = 16 ` ):
92131
93- | Benchmark | All fields | Filtered (city) |
94- | --- | --- | --- |
95- | ` geolite2.City ` | ~ 1,284 ,000 | ~ 1,348,000 |
96- | ` MyCity ` | ~ 1,383,000 | — |
97- | ` any.Value ` | ~ 1,254,000 | ~ 1,349,000 |
132+ | Type | Default | ` .only ` | ` Cache ` |
133+ | --- | --- | --- | --- |
134+ | ` geolite2.City ` | ~ 1,420 ,000 | ~ 1,348,000 | ~ 1,565,000 |
135+ | ` MyCity ` | ~ 1,383,000 | | |
136+ | ` any.Value ` | ~ 1,254,000 | ~ 1,349,000 | |
98137
99138<details >
100139
@@ -140,6 +179,30 @@ Lookups Per Second (avg):1315870.3443053183
140179
141180<details >
142181
182+ <summary >geolite2.City with Cache</summary >
183+
184+ ``` sh
185+ $ for i in $( seq 1 10) ; do
186+ zig build benchmark_lookup_cache -Doptimize=ReleaseFast -- GeoLite2-City.mmdb 1000000 \
187+ 2>&1 | grep ' Lookups Per Second'
188+ done
189+
190+ Lookups Per Second (avg):1493822.3908664712
191+ Lookups Per Second (avg):1503051.0049070602
192+ Lookups Per Second (avg):1499514.437731375
193+ Lookups Per Second (avg):1491749.9700251492
194+ Lookups Per Second (avg):1449924.9391983037
195+ Lookups Per Second (avg):1396100.6211600688
196+ Lookups Per Second (avg):1465750.9875955326
197+ Lookups Per Second (avg):1515611.9396877384
198+ Lookups Per Second (avg):1485235.6423035355
199+ Lookups Per Second (avg):1439334.222943596
200+ ```
201+
202+ </details >
203+
204+ <details >
205+
143206<summary >MyCity</summary >
144207
145208``` sh
@@ -203,3 +266,83 @@ Lookups Per Second (avg):1315986.2950186788
203266```
204267
205268</details >
269+
270+ Use ` scan ` to iterate over all networks in the database.
271+
272+ ``` zig
273+ var it = try db.scan(maxminddb.any.Value, allocator, maxminddb.Network.all_ipv6, .{});
274+
275+ while (try it.next()) |item| {
276+ defer item.deinit();
277+ std.debug.print("{f} {f}\n", .{ item.network, item.value });
278+ }
279+ ```
280+
281+ Use ` scanWithCache ` to avoid re-decoding networks that share the same record.
282+ The cache owns decoded memory, so results don't need to be individually freed.
283+
284+ ``` zig
285+ var cache = try maxminddb.Cache(maxminddb.any.Value).init(allocator, .{});
286+ defer cache.deinit();
287+
288+ var it = try db.scanWithCache(maxminddb.any.Value, &cache, maxminddb.Network.all_ipv6, .{});
289+
290+ while (try it.next()) |item| {
291+ std.debug.print("{f} {f}\n", .{ item.network, item.value });
292+ }
293+ ```
294+
295+ Here are reference results on Apple M2 Pro (full GeoLite2-City scan using ` any.Value ` ):
296+
297+ | Mode | Records/sec |
298+ | --- | --- |
299+ | Default | ~ 1,295,000 |
300+ | ` Cache ` | ~ 2,930,000 |
301+
302+ <details >
303+
304+ <summary >no cache (any.Value)</summary >
305+
306+ ``` sh
307+ $ for i in $( seq 1 10) ; do
308+ zig build benchmark_scan -Doptimize=ReleaseFast -- GeoLite2-City.mmdb \
309+ 2>&1 | grep ' Records Per Second'
310+ done
311+
312+ Records Per Second: 1216758.945145436
313+ Records Per Second: 1238440.9772222256
314+ Records Per Second: 1234710.6362391203
315+ Records Per Second: 1229527.4688849829
316+ Records Per Second: 1243478.3908140333
317+ Records Per Second: 1226863.3718734735
318+ Records Per Second: 1240073.3248202254
319+ Records Per Second: 1247541.1528026997
320+ Records Per Second: 1230510.441029532
321+ Records Per Second: 1246311.587919839
322+ ```
323+
324+ </details >
325+
326+ <details >
327+
328+ <summary >cache (any.Value)</summary >
329+
330+ ``` sh
331+ $ for i in $( seq 1 10) ; do
332+ zig build benchmark_scan_cache -Doptimize=ReleaseFast -- GeoLite2-City.mmdb \
333+ 2>&1 | grep ' Records Per Second'
334+ done
335+
336+ Records Per Second: 2847560.3756875996
337+ Records Per Second: 2925388.867798729
338+ Records Per Second: 2919203.9046571665
339+ Records Per Second: 2814410.555872645
340+ Records Per Second: 2933972.04386147
341+ Records Per Second: 2900700.06160036
342+ Records Per Second: 2922279.338699886
343+ Records Per Second: 2862525.847598088
344+ Records Per Second: 2916760.542913819
345+ Records Per Second: 2908245.98918392
346+ ```
347+
348+ </details >
0 commit comments