From 2834763c0a8eea745a5cf8bd575c4a83d0af10cb Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Thu, 18 Dec 2025 15:54:17 -0800 Subject: [PATCH] talked AI into using a cImport and removed 60 LoC of duplicate code --- build.zig | 4 ++ src/location/GeoIp.zig | 125 ++++++++++------------------------------- 2 files changed, 35 insertions(+), 94 deletions(-) diff --git a/build.zig b/build.zig index 30112a0..3b06530 100644 --- a/build.zig +++ b/build.zig @@ -61,6 +61,8 @@ pub fn build(b: *std.Build) void { exe.root_module.addAnonymousImport("airports.dat", .{ .root_source_file = openflights.path("data/airports.dat"), }); + exe.root_module.addIncludePath(maxminddb_upstream.path("include")); + exe.root_module.addConfigHeader(maxminddb_config); exe.linkLibrary(maxminddb); exe.linkLibC(); @@ -86,6 +88,8 @@ pub fn build(b: *std.Build) void { tests.root_module.addAnonymousImport("airports.dat", .{ .root_source_file = openflights.path("data/airports.dat"), }); + tests.root_module.addIncludePath(maxminddb_upstream.path("include")); + tests.root_module.addConfigHeader(maxminddb_config); tests.linkLibrary(maxminddb); tests.linkLibC(); diff --git a/src/location/GeoIp.zig b/src/location/GeoIp.zig index 5d3b86a..9a698ef 100644 --- a/src/location/GeoIp.zig +++ b/src/location/GeoIp.zig @@ -1,94 +1,29 @@ const std = @import("std"); const Coordinates = @import("../Coordinates.zig"); -pub const MMDB = extern struct { - filename: [*:0]const u8, - flags: u32, - file_content: ?*anyopaque, - file_size: usize, - data_section: ?*anyopaque, - data_section_size: u32, - metadata_section: ?*anyopaque, - metadata_section_size: u32, - full_record_byte_size: u16, - depth: u16, - ipv4_start_node: extern struct { - node_value: u32, - netmask: u16, - }, - metadata: extern struct { - node_count: u32, - record_size: u16, - ip_version: u16, - database_type: [*:0]const u8, - languages: extern struct { - count: usize, - names: [*][*:0]const u8, - }, - binary_format_major_version: u16, - binary_format_minor_version: u16, - build_epoch: u64, - description: extern struct { - count: usize, - descriptions: [*]?*anyopaque, - }, - }, -}; - -pub const MMDBLookupResult = extern struct { - found_entry: bool, - entry: MMDBEntry, - netmask: u16, -}; - -pub const MMDBEntry = extern struct { - mmdb: *MMDB, - offset: u32, -}; - -pub const MMDBEntryData = extern struct { - has_data: bool, - data_type: u32, - offset: u32, - offset_to_next: u32, - data_size: u32, - utf8_string: [*:0]const u8, - double_value: f64, - bytes: [*]const u8, - uint16: u16, - uint32: u32, - int32: i32, - uint64: u64, - uint128: u128, - boolean: bool, - float_value: f32, -}; - -extern fn MMDB_open(filename: [*:0]const u8, flags: u32, mmdb: *MMDB) c_int; -extern fn MMDB_close(mmdb: *MMDB) void; -extern fn MMDB_lookup_string(mmdb: *MMDB, ipstr: [*:0]const u8, gai_error: *c_int, mmdb_error: *c_int) MMDBLookupResult; -extern fn MMDB_get_value(entry: *MMDBEntry, entry_data: *MMDBEntryData, ...) c_int; -extern fn MMDB_strerror(error_code: c_int) [*:0]const u8; +const c = @cImport({ + @cInclude("maxminddb.h"); +}); const GeoIP = @This(); -mmdb: MMDB, +mmdb: c.MMDB_s, pub fn init(db_path: []const u8) !GeoIP { - var mmdb: MMDB = undefined; const path_z = try std.heap.c_allocator.dupeZ(u8, db_path); defer std.heap.c_allocator.free(path_z); - const status = MMDB_open(path_z.ptr, 0, &mmdb); - if (status != 0) { + // SAFETY: The C API will initialize this on the next line + var mmdb: c.MMDB_s = undefined; + const status = c.MMDB_open(path_z.ptr, c.MMDB_MODE_MMAP, &mmdb); + if (status != c.MMDB_SUCCESS) return error.CannotOpenDatabase; - } return GeoIP{ .mmdb = mmdb }; } pub fn deinit(self: *GeoIP) void { - MMDB_close(&self.mmdb); + c.MMDB_close(&self.mmdb); } pub fn lookup(self: *GeoIP, ip: []const u8) !?Coordinates { @@ -98,7 +33,7 @@ pub fn lookup(self: *GeoIP, ip: []const u8) !?Coordinates { var gai_error: c_int = 0; var mmdb_error: c_int = 0; - const result = MMDB_lookup_string(&self.mmdb, ip_z.ptr, &gai_error, &mmdb_error); + const result = c.MMDB_lookup_string(&self.mmdb, ip_z.ptr, &gai_error, &mmdb_error); if (gai_error != 0 or mmdb_error != 0) { return null; @@ -108,7 +43,7 @@ pub fn lookup(self: *GeoIP, ip: []const u8) !?Coordinates { return null; } - return try self.extractCoordinates(result.entry); + return try self.extractCoordinates(result); } pub fn isUSIP(self: *GeoIP, ip: []const u8) bool { @@ -118,46 +53,48 @@ pub fn isUSIP(self: *GeoIP, ip: []const u8) bool { var gai_error: c_int = 0; var mmdb_error: c_int = 0; - const result = MMDB_lookup_string(&self.mmdb, ip_z.ptr, &gai_error, &mmdb_error); + const result = c.MMDB_lookup_string(&self.mmdb, ip_z.ptr, &gai_error, &mmdb_error); if (gai_error != 0 or mmdb_error != 0 or !result.found_entry) { return false; } var entry_mut = result.entry; - var country_data: MMDBEntryData = undefined; const null_term: [*:0]const u8 = @ptrCast(&[_]u8{0}); - const status = MMDB_get_value(&entry_mut, &country_data, "country\x00", "iso_code\x00", null_term); + // SAFETY: The C API will initialize this on the next line + var country_data: c.MMDB_entry_data_s = undefined; + const status = c.MMDB_get_value(&entry_mut, &country_data, "country\x00", "iso_code\x00", null_term); - if (status != 0 or !country_data.has_data) { + if (status != c.MMDB_SUCCESS or !country_data.has_data) return false; - } - const country_code = std.mem.span(country_data.utf8_string); + const country_code = std.mem.span(country_data.unnamed_0.utf8_string); return std.mem.eql(u8, country_code, "US"); } -fn extractCoordinates(self: *GeoIP, entry: MMDBEntry) !Coordinates { +fn extractCoordinates(self: *GeoIP, result: c.MMDB_lookup_result_s) !Coordinates { _ = self; - var entry_mut = entry; - var latitude_data: MMDBEntryData = undefined; - var longitude_data: MMDBEntryData = undefined; + var entry_mut = result.entry; - const lat_status = MMDB_get_value(&entry_mut, &latitude_data, "location", "latitude", @as([*:0]const u8, @ptrCast(&[_]u8{0}))); - const lon_status = MMDB_get_value(&entry_mut, &longitude_data, "location", "longitude", @as([*:0]const u8, @ptrCast(&[_]u8{0}))); + // SAFETY: The C API will initialize below + var latitude_data: c.MMDB_entry_data_s = undefined; + // SAFETY: The C API will initialize below + var longitude_data: c.MMDB_entry_data_s = undefined; - if (lat_status != 0 or lon_status != 0 or !latitude_data.has_data or !longitude_data.has_data) { + const lat_status = c.MMDB_get_value(&entry_mut, &latitude_data, "location", "latitude", @as([*:0]const u8, @ptrCast(&[_]u8{0}))); + const lon_status = c.MMDB_get_value(&entry_mut, &longitude_data, "location", "longitude", @as([*:0]const u8, @ptrCast(&[_]u8{0}))); + + if (lat_status != c.MMDB_SUCCESS or lon_status != c.MMDB_SUCCESS or !latitude_data.has_data or !longitude_data.has_data) return error.CoordinatesNotFound; - } - return Coordinates{ - .latitude = latitude_data.double_value, - .longitude = longitude_data.double_value, + return .{ + .latitude = latitude_data.unnamed_0.double_value, + .longitude = longitude_data.unnamed_0.double_value, }; } test "MMDB functions are callable" { - const mmdb_error = MMDB_strerror(0); + const mmdb_error = c.MMDB_strerror(0); try std.testing.expect(mmdb_error[0] != 0); }