Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,23 @@ jobs:
- os: windows-latest

runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash

steps:
- uses: actions/checkout@v6
# The zig 0.16 aarch64-windows binary and LLVM libs are broken,
# see https://codeberg.org/ziglang/zig/issues/31865.
# As a workaround x86_64 Zig is used via Windows ARM emulation.
- uses: mlugg/setup-zig@v2
if: matrix.os != 'windows-11-arm'
- name: Setup Zig
if: matrix.os == 'windows-11-arm'
run: |
curl -fsSLo zig.zip https://ziglang.org/download/0.16.0/zig-x86_64-windows-0.16.0.zip
unzip -q zig.zip -d zig-sdk
echo "$PWD/zig-sdk/zig-x86_64-windows-0.16.0" >> $GITHUB_PATH
- name: Fetch test-data
run: git submodule update --init
- name: Run tests
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ It returns `Result` or null when the IP is not found or the record is empty.
Each result owns an arena so you should call `result.deinit()` to free it.

```zig
var db = try maxminddb.Reader.mmap(allocator, db_path, .{});
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{});
defer db.close();

if (try db.lookup(maxminddb.geolite2.City, allocator, ip, .{})) |result| {
Expand Down Expand Up @@ -134,7 +134,7 @@ and uses ~320KB at depth 16, or 12 (~20KB) for constrained devices.
It's not worth creating an index for short-lived readers.

```zig
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = 16 });
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = 16 });
defer db.close();
```

Expand Down
35 changes: 18 additions & 17 deletions benchmarks/inspect.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ const default_db_path: []const u8 = "GeoLite2-City.mmdb";
const default_num_lookups: u64 = 1_000_000;
const max_mmdb_fields = 32;

pub fn main() !void {
const allocator = std.heap.smp_allocator;
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
const io = init.io;

const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);

var db_path: []const u8 = default_db_path;
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
defer args.deinit();
_ = args.skip();
const db_path = args.next() orelse default_db_path;
var num_lookups = default_num_lookups;
var fields: ?[]const []const u8 = null;
if (args.len > 1) db_path = args[1];
if (args.len > 2) num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
if (args.len > 3) {
const f = try maxminddb.Fields(max_mmdb_fields).parse(args[3], ',');
if (args.next()) |arg| num_lookups = try std.fmt.parseUnsigned(u64, arg, 10);
if (args.next()) |arg| {
const f = try maxminddb.Fields(max_mmdb_fields).parse(arg, ',');
fields = f.only();
}

Expand All @@ -27,10 +27,11 @@ pub fn main() !void {
std.debug.print(" Lookups: {d}\n", .{num_lookups});
std.debug.print("Opening database...\n", .{});

var open_timer = try std.time.Timer.start();
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = 16 });
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = 16 });
defer db.close();
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
open_time_ms,
Expand All @@ -42,14 +43,14 @@ pub fn main() !void {
const arena_allocator = arena.allocator();

std.debug.print("Starting benchmark...\n", .{});
var timer = try std.time.Timer.start();
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);
var not_found_count: u64 = 0;
var lookup_errors: u64 = 0;
var ip_bytes: [4]u8 = undefined;

for (0..num_lookups) |_| {
std.crypto.random.bytes(&ip_bytes);
const ip = std.net.Address.initIp4(ip_bytes, 0);
io.random(&ip_bytes);
const ip: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = ip_bytes, .port = 0 } };

const result = db.lookup(
maxminddb.any.Value,
Expand All @@ -69,7 +70,7 @@ pub fn main() !void {
_ = arena.reset(.retain_capacity);
}

const elapsed_ns = timer.read();
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_s));
const lookups_per_second = if (elapsed_s > 0)
Expand Down
37 changes: 19 additions & 18 deletions benchmarks/lookup.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,34 @@ const default_db_path: []const u8 = "GeoLite2-City.mmdb";
const default_num_lookups: u64 = 1_000_000;
const max_mmdb_fields = 32;

pub fn main() !void {
const allocator = std.heap.smp_allocator;
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
const io = init.io;

const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);

var db_path: []const u8 = default_db_path;
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
defer args.deinit();
_ = args.skip();
const db_path = args.next() orelse default_db_path;
var num_lookups = default_num_lookups;
var fields: ?[]const []const u8 = null;
var index_bits: u8 = 16;
if (args.len > 1) db_path = args[1];
if (args.len > 2) num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
if (args.len > 3) {
const f = try maxminddb.Fields(max_mmdb_fields).parse(args[3], ',');
if (args.next()) |arg| num_lookups = try std.fmt.parseUnsigned(u64, arg, 10);
if (args.next()) |arg| {
const f = try maxminddb.Fields(max_mmdb_fields).parse(arg, ',');
fields = f.only();
}
if (args.len > 4) index_bits = try std.fmt.parseUnsigned(u8, args[4], 10);
if (args.next()) |arg| index_bits = try std.fmt.parseUnsigned(u8, arg, 10);

std.debug.print("Benchmarking with:\n", .{});
std.debug.print(" Database: {s}\n", .{db_path});
std.debug.print(" Lookups: {d}\n", .{num_lookups});
std.debug.print("Opening database...\n", .{});

var open_timer = try std.time.Timer.start();
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = index_bits });
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = index_bits });
defer db.close();
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
open_time_ms,
Expand All @@ -44,14 +45,14 @@ pub fn main() !void {
const arena_allocator = arena.allocator();

std.debug.print("Starting benchmark...\n", .{});
var timer = try std.time.Timer.start();
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);
var not_found_count: u64 = 0;
var lookup_errors: u64 = 0;
var ip_bytes: [4]u8 = undefined;

for (0..num_lookups) |_| {
std.crypto.random.bytes(&ip_bytes);
const ip = std.net.Address.initIp4(ip_bytes, 0);
io.random(&ip_bytes);
const ip: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = ip_bytes, .port = 0 } };

const result = db.lookup(
maxminddb.geolite2.City,
Expand All @@ -71,7 +72,7 @@ pub fn main() !void {
_ = arena.reset(.retain_capacity);
}

const elapsed_ns = timer.read();
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_s));
const lookups_per_second = if (elapsed_s > 0)
Expand Down
35 changes: 18 additions & 17 deletions benchmarks/lookup_cache.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ const default_db_path: []const u8 = "GeoLite2-City.mmdb";
const default_num_lookups: u64 = 1_000_000;
const max_mmdb_fields = 32;

pub fn main() !void {
const allocator = std.heap.smp_allocator;
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
const io = init.io;

const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);

var db_path: []const u8 = default_db_path;
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
defer args.deinit();
_ = args.skip();
const db_path = args.next() orelse default_db_path;
var num_lookups = default_num_lookups;
var fields: ?[]const []const u8 = null;
if (args.len > 1) db_path = args[1];
if (args.len > 2) num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
if (args.len > 3) {
const f = try maxminddb.Fields(max_mmdb_fields).parse(args[3], ',');
if (args.next()) |arg| num_lookups = try std.fmt.parseUnsigned(u64, arg, 10);
if (args.next()) |arg| {
const f = try maxminddb.Fields(max_mmdb_fields).parse(arg, ',');
fields = f.only();
}

Expand All @@ -26,10 +26,11 @@ pub fn main() !void {
std.debug.print(" Lookups: {d}\n", .{num_lookups});
std.debug.print("Opening database...\n", .{});

var open_timer = try std.time.Timer.start();
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = 16 });
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = 16 });
defer db.close();
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
open_time_ms,
Expand All @@ -40,14 +41,14 @@ pub fn main() !void {
defer cache.deinit();

std.debug.print("Starting benchmark...\n", .{});
var timer = try std.time.Timer.start();
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);
var not_found_count: u64 = 0;
var lookup_errors: u64 = 0;
var ip_bytes: [4]u8 = undefined;

for (0..num_lookups) |_| {
std.crypto.random.bytes(&ip_bytes);
const ip = std.net.Address.initIp4(ip_bytes, 0);
io.random(&ip_bytes);
const ip: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = ip_bytes, .port = 0 } };

const entry = db.find(ip, .{}) catch |err| {
std.debug.print("! Lookup error for IP {any}: {any}\n", .{ ip, err });
Expand All @@ -65,7 +66,7 @@ pub fn main() !void {
};
}

const elapsed_ns = timer.read();
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_s));
const lookups_per_second = if (elapsed_s > 0)
Expand Down
31 changes: 16 additions & 15 deletions benchmarks/mycity.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,27 @@ const MyCity = struct {
} = .{},
};

pub fn main() !void {
const allocator = std.heap.smp_allocator;
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
const io = init.io;

const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);

var db_path: []const u8 = default_db_path;
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
defer args.deinit();
_ = args.skip();
const db_path = args.next() orelse default_db_path;
var num_lookups = default_num_lookups;
if (args.len > 1) db_path = args[1];
if (args.len > 2) num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
if (args.next()) |arg| num_lookups = try std.fmt.parseUnsigned(u64, arg, 10);

std.debug.print("Benchmarking with:\n", .{});
std.debug.print(" Database: {s}\n", .{db_path});
std.debug.print(" Lookups: {d}\n", .{num_lookups});
std.debug.print("Opening database...\n", .{});

var open_timer = try std.time.Timer.start();
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = 16 });
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = 16 });
defer db.close();
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
open_time_ms,
Expand All @@ -43,14 +44,14 @@ pub fn main() !void {
const arena_allocator = arena.allocator();

std.debug.print("Starting benchmark...\n", .{});
var timer = try std.time.Timer.start();
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);
var not_found_count: u64 = 0;
var lookup_errors: u64 = 0;
var ip_bytes: [4]u8 = undefined;

for (0..num_lookups) |_| {
std.crypto.random.bytes(&ip_bytes);
const ip = std.net.Address.initIp4(ip_bytes, 0);
io.random(&ip_bytes);
const ip: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = ip_bytes, .port = 0 } };

const result = db.lookup(
MyCity,
Expand All @@ -70,7 +71,7 @@ pub fn main() !void {
_ = arena.reset(.retain_capacity);
}

const elapsed_ns = timer.read();
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_s));
const lookups_per_second = if (elapsed_s > 0)
Expand Down
29 changes: 15 additions & 14 deletions benchmarks/scan.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,29 @@ const maxminddb = @import("maxminddb");
const default_db_path: []const u8 = "GeoLite2-City.mmdb";
const max_mmdb_fields = 32;

pub fn main() !void {
const allocator = std.heap.smp_allocator;
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
const io = init.io;

const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);

var db_path: []const u8 = default_db_path;
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
defer args.deinit();
_ = args.skip();
const db_path = args.next() orelse default_db_path;
var fields: ?[]const []const u8 = null;
if (args.len > 1) db_path = args[1];
if (args.len > 2) {
const f = try maxminddb.Fields(max_mmdb_fields).parse(args[2], ',');
if (args.next()) |arg| {
const f = try maxminddb.Fields(max_mmdb_fields).parse(arg, ',');
fields = f.only();
}

std.debug.print("Benchmarking with:\n", .{});
std.debug.print(" Database: {s}\n", .{db_path});
std.debug.print("Opening database...\n", .{});

var open_timer = try std.time.Timer.start();
var db = try maxminddb.Reader.mmap(allocator, db_path, .{});
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{});
defer db.close();
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_ms));
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
open_time_ms,
Expand All @@ -38,7 +39,7 @@ pub fn main() !void {
maxminddb.Network.all_ipv6;

std.debug.print("Starting benchmark...\n", .{});
var timer = try std.time.Timer.start();
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);

var it = try db.scan(maxminddb.any.Value, allocator, network, .{ .only = fields });

Expand All @@ -48,7 +49,7 @@ pub fn main() !void {
item.deinit();
}

const elapsed_ns = timer.read();
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
@as(f64, @floatFromInt(std.time.ns_per_s));

Expand Down
Loading