This commit is contained in:
Mateusz Gruszczyński committed 2026-10-12 11:22:42 +02:00
1 parent 68ada6680d
commit 180a0e4877
56 files changed
+9866 -205

No files matched your search

+13 -9
View File
@@ -29,8 +29,10 @@ struct V4Range {
}
#[derive(Debug, Clone, Copy)]
struct V6Range {
start: u128,
end: u128,
// Network byte order preserves the numeric ordering of IPv6 addresses
// without the 16-byte alignment/padding of u128 fields.
start: [u8; 16],
end: [u8; 16],
country: [u8; 2],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
@@ -48,7 +50,7 @@ impl Default for DatabaseFormat {
#[derive(Debug)]
enum Backend {
Csv { ipv4: Vec<V4Range>, ipv6: Vec<V6Range> },
Csv { ipv4: Box<[V4Range]>, ipv6: Box<[V6Range]> },
Mmdb(mmdb::Database),
}
@@ -66,7 +68,7 @@ pub struct View {
}
impl Database {
pub fn empty() -> Self {
Self { backend: Backend::Csv { ipv4: Vec::new(), ipv6: Vec::new() } }
Self { backend: Backend::Csv { ipv4: Box::new([]), ipv6: Box::new([]) } }
}
pub fn load(directory: &Path) -> Result<Self> {
if directory.join("country.mmdb").is_file() {
@@ -81,7 +83,9 @@ impl Database {
let ipv4 = parse_v4(&directory.join("user-country-ipv4.csv"))?;
let ipv6 = parse_v6(&directory.join("user-country-ipv6.csv"))?;
ensure!(!ipv4.is_empty() && !ipv6.is_empty(), "GeoIP country database is empty");
Ok(Self { backend: Backend::Csv { ipv4, ipv6 } })
// Convert to exactly sized immutable slices: the CSV parser may have
// doubled Vec capacity many times during loading.
Ok(Self { backend: Backend::Csv { ipv4: ipv4.into_boxed_slice(), ipv6: ipv6.into_boxed_slice() } })
}
DatabaseFormat::Mmdb => Ok(Self {
backend: Backend::Mmdb(mmdb::Database::load(&directory.join("country.mmdb"), MAX_FILE_BYTES)?),
@@ -118,7 +122,7 @@ impl Database {
Backend::Csv { ipv4, ipv6 } => {
let code = match ip {
IpAddr::V4(ip) => find_v4(ipv4, u32::from(ip)),
IpAddr::V6(ip) => find_v6(ipv6, u128::from(ip)),
IpAddr::V6(ip) => find_v6(ipv6, u128::from(ip).to_be_bytes()),
}?;
Some(String::from_utf8_lossy(&code).into_owned())
}
@@ -227,8 +231,8 @@ fn parse_v6(path: &Path) -> Result<Vec<V6Range>> {
previous_end = Some(end);
if let Some(country) = normalize_country_code(country) {
result.push(V6Range {
start,
end,
start: start.to_be_bytes(),
end: end.to_be_bytes(),
country,
});
ensure!(result.len() <= MAX_RANGES, "too many GeoIP IPv6 ranges");
@@ -243,7 +247,7 @@ fn find_v4(ranges: &[V4Range], ip: u32) -> Option<[u8; 2]> {
let item = ranges.get(index)?;
(ip <= item.end).then_some(item.country)
}
fn find_v6(ranges: &[V6Range], ip: u128) -> Option<[u8; 2]> {
fn find_v6(ranges: &[V6Range], ip: [u8; 16]) -> Option<[u8; 2]> {
let index = ranges
.partition_point(|item| item.start <= ip)
.checked_sub(1)?;