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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ jobs:
run: zig build example_lookup
- name: Run within example
run: zig build example_within
- name: Run inspect example
run: zig build example_inspect
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Here is how you can decode `City.city` and `City.country` fields.

```zig
// This gets us ~34% of performance gains, i.e., ~859K lookups per second.
const fields = maxminddb.Fields.from(maxminddb.geolite2.City, &.{ "city", "country" });
const fields = &.{ "city", "country" };
const city = try db.lookup(allocator, maxminddb.geolite2.City, ip, .{ .only = fields });
```

Expand Down
1 change: 1 addition & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub fn build(b: *std.Build) void {
}{
.{ .file = "examples/lookup.zig", .name = "example_lookup" },
.{ .file = "examples/within.zig", .name = "example_within" },
.{ .file = "examples/inspect.zig", .name = "example_inspect" },
.{ .file = "examples/benchmark.zig", .name = "example_benchmark" },
};

Expand Down
38 changes: 38 additions & 0 deletions examples/inspect.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const std = @import("std");
const maxminddb = @import("maxminddb");

pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
const allocator = gpa.allocator();
defer _ = gpa.detectLeaks();

var args = std.process.args();
_ = args.next();
const db_path = args.next() orelse "test-data/test-data/GeoIP2-City-Test.mmdb";
const ip = args.next() orelse "89.160.20.128";

var db = try maxminddb.Reader.mmap(allocator, db_path);
defer db.unmap();

const m = db.metadata;
std.debug.print("{s} v{}.{} ({} nodes, IPv{})\n", .{
m.database_type,
m.binary_format_major_version,
m.binary_format_minor_version,
m.node_count,
m.ip_version,
});

const result = try db.lookup(
allocator,
maxminddb.any.Value,
try std.net.Address.parseIp(ip, 0),
.{},
) orelse {
std.debug.print("{s}: not found\n", .{ip});
return;
};
defer result.deinit();

std.debug.print("{f}\n", .{result.value});
}
5 changes: 2 additions & 3 deletions examples/lookup.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ pub fn main() !void {
const city = try db.lookup(allocator, maxminddb.geoip2.City, ip, .{}) orelse return;
defer city.deinit();

var it = city.value.country.names.?.iterator();
while (it.next()) |kv| {
std.debug.print("{s} = {s}\n", .{ kv.key_ptr.*, kv.value_ptr.* });
for (city.value.country.names.?.entries) |e| {
std.debug.print("{s} = {s}\n", .{ e.key, e.value });
}
}
79 changes: 79 additions & 0 deletions src/any.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const std = @import("std");

/// A tagged union that can hold any MaxMind DB data type.
/// Use instead of a predefined struct to decode any record without knowing the schema.
pub const Value = union(enum) {
string: []const u8,
double: f64,
uint16: u16,
uint32: u32,
int32: i32,
uint64: u64,
uint128: u128,
boolean: bool,
float: f32,
array: []Value,
map: []Entry,

pub const Entry = struct {
key: []const u8,
value: Value,
};

/// Returns the value for the given key if this Value is a map, or null otherwise.
pub fn get(self: Value, key: []const u8) ?Value {
switch (self) {
.map => |entries| {
for (entries) |e| {
if (std.mem.eql(u8, e.key, key)) {
return e.value;
}
}

return null;
},
else => return null,
}
}

pub fn format(self: Value, writer: anytype) !void {
switch (self) {
.string => |s| try writer.print("\"{s}\"", .{s}),
.double => |v| try writer.print("{d}", .{v}),
.float => |v| try writer.print("{d}", .{v}),
.uint16 => |v| try writer.print("{}", .{v}),
.uint32 => |v| try writer.print("{}", .{v}),
.int32 => |v| try writer.print("{}", .{v}),
.uint64 => |v| try writer.print("{}", .{v}),
.uint128 => |v| try writer.print("{}", .{v}),
.boolean => |v| try writer.print("{}", .{v}),
.array => |a| {
try writer.writeAll("[");

for (a, 0..) |item, i| {
if (i > 0) {
try writer.writeAll(", ");
}

try item.format(writer);
}

try writer.writeAll("]");
},
.map => |entries| {
try writer.writeAll("{");

for (entries, 0..) |entry, i| {
if (i > 0) {
try writer.writeAll(", ");
}

try writer.print("\"{s}\": ", .{entry.key});
try entry.value.format(writer);
}

try writer.writeAll("}");
},
}
}
};
37 changes: 37 additions & 0 deletions src/collection.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const std = @import("std");

/// A decoded MaxMind DB array with elements of type T.
/// Use as a field type in record structs to decode MaxMind DB arrays.
pub fn Array(comptime T: type) type {
return struct {
items: []const T = &.{},

pub const array_marker: void = {};
};
}

/// A decoded MaxMind DB map with string keys and values of type V.
/// Use as a field type in record structs to decode MaxMind DB maps.
/// The MaxMind DB format requires all map keys to be UTF-8 strings.
pub fn Map(comptime V: type) type {
return struct {
entries: []const Entry = &.{},

pub const Entry = struct {
key: []const u8,
value: V,
};

pub const map_marker: void = {};

pub fn get(self: @This(), key: []const u8) ?V {
for (self.entries) |e| {
if (std.mem.eql(u8, e.key, key)) {
return e.value;
}
}

return null;
}
};
}
Loading