1use std::{
2 collections::{BTreeMap, HashMap},
3 hash::{Hash, Hasher},
4 io::Write,
5 net::IpAddr,
6 time::{SystemTime, UNIX_EPOCH},
7};
8
9use ipnetwork::IpNetwork;
10use serde::Serialize;
11
12use crate::encoder::{self, Value, encode_serialize};
13
14pub type WriterResult<T> = Result<T, String>;
20
21fn map_io_err(e: std::io::Error) -> String {
22 e.to_string()
23}
24
25const DATA_SECTION_SEPARATOR: &[u8; 16] = &[0u8; 16];
26const METADATA_START_MARKER: &[u8] = b"\xab\xcd\xefMaxMind.com";
27
28#[derive(Debug, Clone)]
29enum RecordType {
30 Empty,
31 Data,
32 Node,
33}
34
35#[derive(Debug, Clone)]
36struct DataRecord {
37 key: u64,
38}
39
40#[derive(Debug, Clone)]
41struct ChildRecord {
42 record_type: RecordType,
43 data: Option<DataRecord>,
44 node: Option<Box<Node>>,
45}
46
47impl ChildRecord {
48 fn empty() -> Self {
49 ChildRecord {
50 record_type: RecordType::Empty,
51 data: None,
52 node: None,
53 }
54 }
55
56 fn data(key: u64) -> Self {
57 ChildRecord {
58 record_type: RecordType::Data,
59 data: Some(DataRecord {
60 key,
61 }),
62 node: None,
63 }
64 }
65
66 fn node(node: Node) -> Self {
67 ChildRecord {
68 record_type: RecordType::Node,
69 data: None,
70 node: Some(Box::new(node)),
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
76struct Node {
77 node_num: Option<usize>,
78 children: [ChildRecord; 2],
79}
80
81impl Node {
82 fn new() -> Self {
83 Node {
84 node_num: None,
85 children: [ChildRecord::empty(), ChildRecord::empty()],
86 }
87 }
88
89 fn insert(&mut self, ip: &[u8], prefix_len: usize, depth: usize, data_key: u64) -> WriterResult<()> {
90 if depth == prefix_len {
91 self.children[0] = ChildRecord::data(data_key);
92 self.children[1] = ChildRecord::data(data_key);
93 return Ok(());
94 }
95
96 let bit = bit_at(ip, depth);
97 let child = &mut self.children[bit as usize];
98
99 match &child.record_type {
100 RecordType::Empty => {
101 let mut subtree = Node::new();
102 subtree.insert(ip, prefix_len, depth + 1, data_key)?;
103 *child = ChildRecord::node(subtree);
104 }
105 RecordType::Data => {
106 let existing = child.data.as_ref().unwrap();
107 let mut subtree = Node::new();
108 subtree.children[0] = ChildRecord::data(existing.key);
109 subtree.children[1] = ChildRecord::data(existing.key);
110 subtree.insert(ip, prefix_len, depth + 1, data_key)?;
111 *child = ChildRecord::node(subtree);
112 }
113 RecordType::Node => {
114 child
115 .node
116 .as_mut()
117 .unwrap()
118 .insert(ip, prefix_len, depth + 1, data_key)?;
119 }
120 }
121
122 Ok(())
123 }
124
125 fn finalize(&mut self, next_node: &mut usize) {
126 self.node_num = Some(*next_node);
127 *next_node += 1;
128
129 for i in 0..2 {
130 if let Some(node) = self.children[i].node.as_mut() {
131 node.finalize(next_node);
132 }
133 }
134 }
135
136 fn write_node<W: Write>(
137 &self,
138 writer: &mut W,
139 node_count: usize,
140 data_offsets: &BTreeMap<u64, usize>,
141 record_size: u16,
142 ) -> WriterResult<()> {
143 let left_val = self.resolve_record_value(&self.children[0], node_count, data_offsets)?;
144 let right_val = self.resolve_record_value(&self.children[1], node_count, data_offsets)?;
145
146 let max_record = 1u64 << record_size;
147 if left_val as u64 >= max_record || right_val as u64 >= max_record {
148 return Err(format!(
149 "record value ({}, {}) exceeds max for {} bit record size",
150 left_val, right_val, record_size
151 ));
152 }
153
154 match record_size {
155 24 => writer
156 .write_all(&[
157 ((left_val >> 16) & 0xFF) as u8,
158 ((left_val >> 8) & 0xFF) as u8,
159 (left_val & 0xFF) as u8,
160 ((right_val >> 16) & 0xFF) as u8,
161 ((right_val >> 8) & 0xFF) as u8,
162 (right_val & 0xFF) as u8,
163 ])
164 .map_err(map_io_err)?,
165 28 => writer
166 .write_all(&[
167 ((left_val >> 16) & 0xFF) as u8,
168 ((left_val >> 8) & 0xFF) as u8,
169 (left_val & 0xFF) as u8,
170 ((((left_val >> 24) & 0x0F) << 4) | ((right_val >> 24) & 0x0F)) as u8,
171 ((right_val >> 16) & 0xFF) as u8,
172 ((right_val >> 8) & 0xFF) as u8,
173 (right_val & 0xFF) as u8,
174 ])
175 .map_err(map_io_err)?,
176 32 => writer
177 .write_all(&[
178 ((left_val >> 24) & 0xFF) as u8,
179 ((left_val >> 16) & 0xFF) as u8,
180 ((left_val >> 8) & 0xFF) as u8,
181 (left_val & 0xFF) as u8,
182 ((right_val >> 24) & 0xFF) as u8,
183 ((right_val >> 16) & 0xFF) as u8,
184 ((right_val >> 8) & 0xFF) as u8,
185 (right_val & 0xFF) as u8,
186 ])
187 .map_err(map_io_err)?,
188 s => return Err(format!("unsupported record size: {s}")),
189 }
190
191 for child in &self.children {
192 if let Some(node) = &child.node {
193 node.write_node(writer, node_count, data_offsets, record_size)?;
194 }
195 }
196
197 Ok(())
198 }
199
200 fn resolve_record_value(
201 &self,
202 record: &ChildRecord,
203 node_count: usize,
204 data_offsets: &BTreeMap<u64, usize>,
205 ) -> WriterResult<usize> {
206 match &record.record_type {
207 RecordType::Empty => Ok(node_count),
208 RecordType::Data => {
209 let d = record.data.as_ref().unwrap();
210 let offset = data_offsets.get(&d.key).unwrap();
211 Ok(node_count + 16 + offset)
212 }
213 RecordType::Node => Ok(record.node.as_ref().unwrap().node_num.unwrap()),
214 }
215 }
216}
217
218fn bit_at(ip: &[u8], depth: usize) -> u8 {
219 (ip[depth >> 3] >> (7 - (depth & 7))) & 1
220}
221
222#[derive(Debug)]
248pub struct Writer {
249 root: Node,
250 data_map: BTreeMap<u64, Vec<u8>>,
251 data_index: HashMap<u64, Vec<u64>>,
254 node_count: usize,
255 ip_version: u16,
256 record_size: u16,
257 database_type: String,
258 description: BTreeMap<String, String>,
259 languages: Vec<String>,
260 build_epoch: u64,
261 next_data_key: u64,
262}
263
264#[derive(Clone)]
266pub struct WriterOptions {
267 pub ip_version: u16,
270 pub record_size: u16,
273 pub database_type: String,
275 pub description: BTreeMap<String, String>,
277 pub languages: Vec<String>,
279 pub build_epoch: u64,
281}
282
283impl Default for WriterOptions {
284 fn default() -> Self {
285 WriterOptions {
286 ip_version: 6,
287 record_size: 28,
288 database_type: String::new(),
289 description: BTreeMap::new(),
290 languages: Vec::new(),
291 build_epoch: SystemTime::now()
292 .duration_since(UNIX_EPOCH)
293 .unwrap_or_default()
294 .as_secs(),
295 }
296 }
297}
298
299impl Writer {
300 pub fn new(opts: WriterOptions) -> WriterResult<Self> {
307 if opts.ip_version != 4 && opts.ip_version != 6 {
308 return Err(format!("invalid ip_version: {} (must be 4 or 6)", opts.ip_version));
309 }
310 if opts.record_size != 24 && opts.record_size != 28 && opts.record_size != 32 {
311 return Err(format!("invalid record_size: {} (must be 24, 28, or 32)", opts.record_size));
312 }
313 Ok(Writer {
314 root: Node::new(),
315 data_map: BTreeMap::new(),
316 data_index: HashMap::new(),
317 node_count: 0,
318 ip_version: opts.ip_version,
319 record_size: opts.record_size,
320 database_type: opts.database_type,
321 description: opts.description,
322 languages: opts.languages,
323 build_epoch: opts.build_epoch,
324 next_data_key: 1,
325 })
326 }
327
328 fn intern_value(&mut self, encoded: Vec<u8>) -> u64 {
331 let mut hasher = std::collections::hash_map::DefaultHasher::new();
332 encoded.hash(&mut hasher);
333 let digest = hasher.finish();
334
335 if let Some(keys) = self.data_index.get(&digest) {
336 for key in keys {
337 if self.data_map.get(key).is_some_and(|existing| existing == &encoded) {
338 return *key;
339 }
340 }
341 }
342
343 let key = self.next_data_key;
344 self.next_data_key += 1;
345 self.data_map.insert(key, encoded);
346 self.data_index.entry(digest).or_default().push(key);
347 key
348 }
349
350 pub fn insert_value(&mut self, network: IpNetwork, value: Value) -> WriterResult<()> {
366 let prefix_len = network.prefix() as usize;
367 let ip = ip_to_bytes(network.network());
368
369 if self.ip_version == 4 && ip.len() != 4 {
370 return Err("cannot insert IPv6 network into an IPv4 tree".to_string());
371 }
372
373 let (ip_bytes, actual_prefix_len) = if self.ip_version == 6 && ip.len() == 4 {
374 let mut v6 = [0u8; 16];
375 v6[12..].copy_from_slice(&ip);
376 (v6.to_vec(), prefix_len + 96)
377 } else {
378 (ip, prefix_len)
379 };
380
381 let encoded = encoder::encode_value(&value)?;
382
383 let data_key = self.intern_value(encoded);
384
385 self.node_count = 0;
386 self.root.insert(&ip_bytes, actual_prefix_len, 0, data_key)?;
387
388 Ok(())
389 }
390
391 pub fn insert<T: Serialize>(&mut self, network: IpNetwork, value: T) -> WriterResult<()> {
405 let encoded = encode_serialize(&value).map_err(|e| format!("serialization failed: {e}"))?;
406
407 let prefix_len = network.prefix() as usize;
408 let ip = ip_to_bytes(network.network());
409
410 if self.ip_version == 4 && ip.len() != 4 {
411 return Err("cannot insert IPv6 network into an IPv4 tree".to_string());
412 }
413
414 let (ip_bytes, actual_prefix_len) = if self.ip_version == 6 && ip.len() == 4 {
415 let mut v6 = [0u8; 16];
416 v6[12..].copy_from_slice(&ip);
417 (v6.to_vec(), prefix_len + 96)
418 } else {
419 (ip, prefix_len)
420 };
421
422 let data_key = self.intern_value(encoded);
423
424 self.node_count = 0;
425 self.root.insert(&ip_bytes, actual_prefix_len, 0, data_key)?;
426
427 Ok(())
428 }
429
430 pub fn write_to<W: Write>(&mut self, writer: &mut W) -> WriterResult<u64> {
440 let mut next = 1usize;
441 for child in &mut self.root.children {
442 if let Some(node) = child.node.as_mut() {
443 node.finalize(&mut next);
444 }
445 }
446 self.node_count = next;
447
448 let search_tree_size_bytes = self
449 .node_count
450 .checked_mul(self.record_size as usize)
451 .map(|v| v / 4)
452 .ok_or_else(|| "search tree size overflow".to_string())?;
453 let node_count_u32 =
454 u32::try_from(self.node_count).map_err(|_| format!("node count {} exceeds u32", self.node_count))?;
455
456 let mut data_offsets: BTreeMap<u64, usize> = BTreeMap::new();
457 let mut data_section = Vec::new();
458 for (key, encoded) in &self.data_map {
459 let offset = data_section.len();
460 data_section.extend_from_slice(encoded);
461 data_offsets.insert(*key, offset);
462 }
463
464 let node_count = self.node_count;
465 self.root
466 .write_node(writer, node_count, &data_offsets, self.record_size)?;
467
468 writer.write_all(DATA_SECTION_SEPARATOR).map_err(map_io_err)?;
469 writer.write_all(&data_section).map_err(map_io_err)?;
470 writer.write_all(METADATA_START_MARKER).map_err(map_io_err)?;
471
472 let metadata = build_metadata_value(
473 node_count_u32,
474 self.record_size,
475 self.ip_version,
476 &self.database_type,
477 &self.description,
478 &self.languages,
479 self.build_epoch,
480 );
481 let metadata_bytes = encoder::encode_value(&metadata)?;
482 const METADATA_MAX_SIZE: usize = 128 * 1024;
483 if metadata_bytes.len() > METADATA_MAX_SIZE {
484 return Err(format!(
485 "encoded metadata is {} bytes; the MaxMind DB maximum is {} bytes",
486 metadata_bytes.len(),
487 METADATA_MAX_SIZE
488 ));
489 }
490 writer.write_all(&metadata_bytes).map_err(map_io_err)?;
491
492 Ok((search_tree_size_bytes
493 + DATA_SECTION_SEPARATOR.len()
494 + data_section.len()
495 + METADATA_START_MARKER.len()
496 + metadata_bytes.len()) as u64)
497 }
498}
499
500fn build_metadata_value(
501 node_count: u32,
502 record_size: u16,
503 ip_version: u16,
504 database_type: &str,
505 description: &BTreeMap<String, String>,
506 languages: &[String],
507 build_epoch: u64,
508) -> Value {
509 let mut desc_map = BTreeMap::new();
510 for (k, v) in description {
511 desc_map.insert(k.clone(), Value::String(v.clone()));
512 }
513
514 let mut lang_array = Vec::new();
515 for l in languages {
516 lang_array.push(Value::String(l.clone()));
517 }
518
519 let mut m = BTreeMap::new();
520 m.insert("binary_format_major_version".to_string(), Value::Uint16(2));
521 m.insert("binary_format_minor_version".to_string(), Value::Uint16(0));
522 m.insert("build_epoch".to_string(), Value::Uint64(build_epoch));
523 m.insert("database_type".to_string(), Value::String(database_type.to_string()));
524 m.insert("description".to_string(), Value::Map(desc_map));
525 m.insert("ip_version".to_string(), Value::Uint16(ip_version));
526 m.insert("languages".to_string(), Value::Slice(lang_array));
527 m.insert("node_count".to_string(), Value::Uint32(node_count));
528 m.insert("record_size".to_string(), Value::Uint16(record_size));
529 Value::Map(m)
530}
531
532fn ip_to_bytes(address: IpAddr) -> Vec<u8> {
533 match address {
534 IpAddr::V4(a) => a.octets().to_vec(),
535 IpAddr::V6(a) => a.octets().to_vec(),
536 }
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542
543 #[test]
544 fn test_write_and_read_simple_v4() {
545 let opts = WriterOptions {
546 database_type: "Test".to_string(),
547 ip_version: 4,
548 record_size: 24,
549 ..Default::default()
550 };
551 let mut tree = Writer::new(opts).unwrap();
552 let mut m = BTreeMap::new();
553 m.insert("country".to_string(), Value::String("US".to_string()));
554 tree.insert_value("1.2.3.0/24".parse().unwrap(), Value::Map(m)).unwrap();
555 let mut buf = Vec::new();
556 tree.write_to(&mut buf).unwrap();
557 let reader = crate::Reader::from_source(buf).unwrap();
558 #[derive(serde::Deserialize, Debug)]
559 struct TestRecord {
560 country: String,
561 }
562 let result: TestRecord = reader.lookup("1.2.3.4".parse().unwrap()).unwrap();
563 assert_eq!(result.country, "US");
564 }
565
566 #[test]
567 fn test_write_and_read_multiple_networks() {
568 let opts = WriterOptions {
569 database_type: "Test".to_string(),
570 ip_version: 4,
571 record_size: 24,
572 ..Default::default()
573 };
574 let mut tree = Writer::new(opts).unwrap();
575 let mut m1 = BTreeMap::new();
576 m1.insert("country".to_string(), Value::String("US".to_string()));
577 tree.insert_value("1.0.0.0/8".parse().unwrap(), Value::Map(m1)).unwrap();
578 let mut m2 = BTreeMap::new();
579 m2.insert("country".to_string(), Value::String("FR".to_string()));
580 tree.insert_value("2.0.0.0/8".parse().unwrap(), Value::Map(m2)).unwrap();
581 let mut buf = Vec::new();
582 tree.write_to(&mut buf).unwrap();
583 let reader = crate::Reader::from_source(buf).unwrap();
584 #[derive(serde::Deserialize, Debug)]
585 struct TestRecord {
586 country: String,
587 }
588 let r1: TestRecord = reader.lookup("1.2.3.4".parse().unwrap()).unwrap();
589 assert_eq!(r1.country, "US");
590 let r2: TestRecord = reader.lookup("2.3.4.5".parse().unwrap()).unwrap();
591 assert_eq!(r2.country, "FR");
592 assert!(reader.lookup::<TestRecord>("3.4.5.6".parse().unwrap()).is_err());
593 }
594
595 #[test]
596 fn test_v6_database() {
597 let opts = WriterOptions {
598 database_type: "Test".to_string(),
599 ip_version: 6,
600 record_size: 28,
601 ..Default::default()
602 };
603 let mut tree = Writer::new(opts).unwrap();
604 let mut m = BTreeMap::new();
605 m.insert("country".to_string(), Value::String("FR".to_string()));
606 tree.insert_value("2a00:1450:4000::/36".parse().unwrap(), Value::Map(m))
607 .unwrap();
608 let mut buf = Vec::new();
609 tree.write_to(&mut buf).unwrap();
610 let reader = crate::Reader::from_source(buf).unwrap();
611 #[derive(serde::Deserialize, Debug)]
612 struct TestRecord {
613 country: String,
614 }
615 let r: TestRecord = reader.lookup("2a00:1450:4000::".parse().unwrap()).unwrap();
616 assert_eq!(r.country, "FR");
617 }
618
619 #[test]
620 fn test_all_record_sizes() {
621 for record_size in [24, 28, 32] {
622 let opts = WriterOptions {
623 database_type: "Test".to_string(),
624 ip_version: 4,
625 record_size,
626 ..Default::default()
627 };
628 let mut tree = Writer::new(opts).unwrap();
629 let mut m = BTreeMap::new();
630 m.insert("ip".to_string(), Value::String("test".to_string()));
631 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
632 let mut buf = Vec::new();
633 tree.write_to(&mut buf).unwrap();
634 let reader = crate::Reader::from_source(buf.clone()).unwrap();
635 #[derive(serde::Deserialize, Debug)]
636 struct TestRecord {
637 ip: String,
638 }
639 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
640 assert_eq!(r.ip, "test", "failed for record_size={record_size}");
641 }
642 }
643
644 #[test]
645 fn test_write_with_metadata() {
646 let mut desc = BTreeMap::new();
647 desc.insert("en".to_string(), "Test Database".to_string());
648 let opts = WriterOptions {
649 database_type: "TestIP".to_string(),
650 ip_version: 4,
651 record_size: 24,
652 description: desc,
653 languages: vec!["en".to_string()],
654 build_epoch: 1000000,
655 };
656 let mut tree = Writer::new(opts).unwrap();
657 let mut m = BTreeMap::new();
658 m.insert("data".to_string(), Value::String("hello".to_string()));
659 tree.insert_value("192.168.0.0/16".parse().unwrap(), Value::Map(m))
660 .unwrap();
661 let mut buf = Vec::new();
662 tree.write_to(&mut buf).unwrap();
663 let reader = crate::Reader::from_source(buf).unwrap();
664 assert_eq!(reader.metadata.database_type, "TestIP");
665 assert_eq!(reader.metadata.build_epoch, 1000000);
666 assert_eq!(reader.metadata.description["en"], "Test Database");
667 }
668
669 #[test]
670 fn test_bool_value() {
671 let opts = WriterOptions {
672 database_type: "Test".to_string(),
673 ip_version: 4,
674 record_size: 24,
675 ..Default::default()
676 };
677 let mut tree = Writer::new(opts).unwrap();
678 let mut m = BTreeMap::new();
679 m.insert("active".to_string(), Value::Bool(true));
680 m.insert("inactive".to_string(), Value::Bool(false));
681 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
682 let mut buf = Vec::new();
683 tree.write_to(&mut buf).unwrap();
684 let reader = crate::Reader::from_source(buf).unwrap();
685 #[derive(serde::Deserialize, Debug)]
686 struct TestRecord {
687 active: bool,
688 inactive: bool,
689 }
690 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
691 assert!(r.active);
692 assert!(!r.inactive);
693 }
694
695 #[test]
696 fn test_slice_value() {
697 let opts = WriterOptions {
698 database_type: "Test".to_string(),
699 ip_version: 4,
700 record_size: 24,
701 ..Default::default()
702 };
703 let mut tree = Writer::new(opts).unwrap();
704 let mut m = BTreeMap::new();
705 m.insert(
706 "tags".to_string(),
707 Value::Slice(vec![
708 Value::String("a".to_string()),
709 Value::String("b".to_string()),
710 Value::String("c".to_string()),
711 ]),
712 );
713 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
714 let mut buf = Vec::new();
715 tree.write_to(&mut buf).unwrap();
716 let reader = crate::Reader::from_source(buf).unwrap();
717 #[derive(serde::Deserialize, Debug)]
718 struct TestRecord {
719 tags: Vec<String>,
720 }
721 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
722 assert_eq!(r.tags, vec!["a", "b", "c"]);
723 }
724
725 #[test]
726 fn test_integer_types() {
727 let opts = WriterOptions {
728 database_type: "Test".to_string(),
729 ip_version: 4,
730 record_size: 24,
731 ..Default::default()
732 };
733 let mut tree = Writer::new(opts).unwrap();
734 let mut m = BTreeMap::new();
735 m.insert("u16".to_string(), Value::Uint16(100));
736 m.insert("u32".to_string(), Value::Uint32(100000));
737 m.insert("i32".to_string(), Value::Int32(-42));
738 m.insert("u64".to_string(), Value::Uint64(1 << 40));
739 m.insert("u128".to_string(), Value::Uint128(1u128 << 100));
740 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
741 let mut buf = Vec::new();
742 tree.write_to(&mut buf).unwrap();
743 let reader = crate::Reader::from_source(buf).unwrap();
744 #[derive(serde::Deserialize, Debug)]
745 struct TestRecord {
746 u16: u16,
747 u32: u32,
748 i32: i32,
749 u64: u64,
750 u128: u128,
751 }
752 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
753 assert_eq!(r.u16, 100);
754 assert_eq!(r.u32, 100000);
755 assert_eq!(r.i32, -42);
756 assert_eq!(r.u64, 1 << 40);
757 assert_eq!(r.u128, 1u128 << 100);
758 }
759
760 #[test]
761 fn test_pointer_encoding_size3() {
762 let v = Value::Pointer(200000000);
763 let buf = encoder::encode_value(&v).unwrap();
764 assert_eq!(buf[0], 0b00111000, "size=3 pointer control byte should be 0x38");
765 let ptr_val = u32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
766 assert_eq!(ptr_val, 200000000);
767 }
768
769 #[test]
770 fn test_pointer_encoding_size0() {
771 let v = Value::Pointer(500);
772 let buf = encoder::encode_value(&v).unwrap();
773 assert_eq!(buf[0], 0b00100001);
774 assert_eq!(buf.len(), 2);
775 let decoded = ((buf[0] as u32 & 0x07) << 8) | buf[1] as u32;
776 assert_eq!(decoded, 500);
777 }
778
779 #[test]
780 fn test_pointer_encoding_size1() {
781 let v = Value::Pointer(10000);
782 let buf = encoder::encode_value(&v).unwrap();
783 assert_eq!((buf[0] >> 5) & 0x07, 0b001);
784 assert_eq!((buf[0] >> 3) & 0x03, 0b01);
785 assert_eq!(buf.len(), 3);
786 }
787
788 #[test]
789 fn test_empty_database() {
790 let opts = WriterOptions {
791 database_type: "Empty".to_string(),
792 ip_version: 4,
793 record_size: 24,
794 ..Default::default()
795 };
796 let mut tree = Writer::new(opts).unwrap();
797 let mut buf = Vec::new();
798 let n = tree.write_to(&mut buf).unwrap();
799 assert!(n > 0);
800 let reader = crate::Reader::from_source(buf).unwrap();
801 assert_eq!(reader.metadata.database_type, "Empty");
802 assert_eq!(reader.metadata.node_count, 1);
803 }
804
805 #[test]
806 fn test_overlapping_networks() {
807 let opts = WriterOptions {
808 database_type: "Test".to_string(),
809 ip_version: 4,
810 record_size: 24,
811 ..Default::default()
812 };
813 let mut tree = Writer::new(opts).unwrap();
814 let mut m1 = BTreeMap::new();
815 m1.insert("data".to_string(), Value::String("broad".to_string()));
816 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m1))
817 .unwrap();
818
819 let mut m2 = BTreeMap::new();
820 m2.insert("data".to_string(), Value::String("specific".to_string()));
821 tree.insert_value("10.1.0.0/16".parse().unwrap(), Value::Map(m2))
822 .unwrap();
823
824 let mut buf = Vec::new();
825 tree.write_to(&mut buf).unwrap();
826 let reader = crate::Reader::from_source(buf).unwrap();
827
828 #[derive(serde::Deserialize, Debug)]
829 struct TestRecord {
830 data: String,
831 }
832
833 let r1: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
834 assert_eq!(r1.data, "broad");
835
836 let r2: TestRecord = reader.lookup("10.1.0.1".parse().unwrap()).unwrap();
837 assert_eq!(r2.data, "specific");
838 }
839
840 #[test]
841 fn test_nested_map() {
842 let opts = WriterOptions {
843 database_type: "Test".to_string(),
844 ip_version: 4,
845 record_size: 24,
846 ..Default::default()
847 };
848 let mut tree = Writer::new(opts).unwrap();
849 let mut inner = BTreeMap::new();
850 inner.insert("code".to_string(), Value::Uint16(1));
851 inner.insert("name".to_string(), Value::String("US".to_string()));
852
853 let mut m = BTreeMap::new();
854 m.insert("country".to_string(), Value::Map(inner));
855 m.insert("active".to_string(), Value::Bool(true));
856 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
857
858 let mut buf = Vec::new();
859 tree.write_to(&mut buf).unwrap();
860 let reader = crate::Reader::from_source(buf).unwrap();
861
862 #[derive(serde::Deserialize, Debug)]
863 struct Country {
864 code: u16,
865 name: String,
866 }
867 #[derive(serde::Deserialize, Debug)]
868 struct TestRecord {
869 country: Country,
870 active: bool,
871 }
872
873 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
874 assert_eq!(r.country.code, 1);
875 assert_eq!(r.country.name, "US");
876 assert!(r.active);
877 }
878
879 #[test]
880 fn test_data_deduplication() {
881 let opts = WriterOptions {
882 database_type: "Test".to_string(),
883 ip_version: 4,
884 record_size: 24,
885 ..Default::default()
886 };
887
888 let mut tree = Writer::new(opts.clone()).unwrap();
889 let shared_data = {
890 let mut m = BTreeMap::new();
891 m.insert("val".to_string(), Value::String("shared".to_string()));
892 Value::Map(m)
893 };
894 tree.insert_value("10.0.0.0/8".parse().unwrap(), shared_data.clone())
895 .unwrap();
896 tree.insert_value("11.0.0.0/8".parse().unwrap(), shared_data).unwrap();
897 let mut buf_dedup = Vec::new();
898 tree.write_to(&mut buf_dedup).unwrap();
899
900 let mut tree2 = Writer::new(opts).unwrap();
901 let mut m1 = BTreeMap::new();
902 m1.insert("val".to_string(), Value::String("first".to_string()));
903 let mut m2 = BTreeMap::new();
904 m2.insert("val".to_string(), Value::String("second".to_string()));
905 tree2
906 .insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m1))
907 .unwrap();
908 tree2
909 .insert_value("11.0.0.0/8".parse().unwrap(), Value::Map(m2))
910 .unwrap();
911 let mut buf_no_dedup = Vec::new();
912 tree2.write_to(&mut buf_no_dedup).unwrap();
913
914 assert!(
915 buf_dedup.len() < buf_no_dedup.len(),
916 "dedup size {} should be < no-dedup size {}",
917 buf_dedup.len(),
918 buf_no_dedup.len()
919 );
920
921 let r1 = crate::Reader::from_source(buf_dedup).unwrap();
922 let r2 = crate::Reader::from_source(buf_no_dedup).unwrap();
923 assert_eq!(r1.metadata.node_count, r2.metadata.node_count);
924 }
925
926 #[test]
927 fn test_ipv4_in_ipv6_tree() {
928 let opts = WriterOptions {
929 database_type: "Test".to_string(),
930 ip_version: 6,
931 record_size: 28,
932 ..Default::default()
933 };
934 let mut tree = Writer::new(opts).unwrap();
935 let mut m = BTreeMap::new();
936 m.insert("v4in6".to_string(), Value::Bool(true));
937 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
938
939 let mut buf = Vec::new();
940 tree.write_to(&mut buf).unwrap();
941 let reader = crate::Reader::from_source(buf).unwrap();
942
943 #[derive(serde::Deserialize, Debug)]
944 struct TestRecord {
945 v4in6: bool,
946 }
947
948 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
949 assert!(r.v4in6);
950 }
951
952 #[test]
953 fn test_record_size_24_node_format() {
954 let opts = WriterOptions {
955 database_type: "Test".to_string(),
956 ip_version: 4,
957 record_size: 24,
958 ..Default::default()
959 };
960 let mut tree = Writer::new(opts).unwrap();
961 let mut m = BTreeMap::new();
962 m.insert("x".to_string(), Value::Uint16(1));
963 tree.insert_value("1.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
964 let mut buf = Vec::new();
965 tree.write_to(&mut buf).unwrap();
966
967 let reader = crate::Reader::from_source(buf.clone()).unwrap();
968 let node_count = reader.metadata.node_count;
969 let nc = node_count as u64;
970 let left = ((buf[0] as u64) << 16) | ((buf[1] as u64) << 8) | (buf[2] as u64);
971 let right = ((buf[3] as u64) << 16) | ((buf[4] as u64) << 8) | (buf[5] as u64);
972 assert!(left <= nc || left >= nc + 16, "left={left} nc={nc}");
973 assert!(right <= nc || right >= nc + 16, "right={right} nc={nc}");
974 }
975
976 #[test]
977 fn test_record_size_28_node_format() {
978 let opts = WriterOptions {
979 database_type: "Test".to_string(),
980 ip_version: 4,
981 record_size: 28,
982 ..Default::default()
983 };
984 let mut tree = Writer::new(opts).unwrap();
985 let mut m = BTreeMap::new();
986 m.insert("x".to_string(), Value::Uint16(1));
987 tree.insert_value("1.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
988 let mut buf = Vec::new();
989 tree.write_to(&mut buf).unwrap();
990
991 let node_count = crate::Reader::from_source(buf.clone()).unwrap().metadata.node_count as usize;
992 assert!(buf.len() > node_count * 7);
993 }
994
995 #[test]
996 fn test_record_size_32_node_format() {
997 let opts = WriterOptions {
998 database_type: "Test".to_string(),
999 ip_version: 4,
1000 record_size: 32,
1001 ..Default::default()
1002 };
1003 let mut tree = Writer::new(opts).unwrap();
1004 let mut m = BTreeMap::new();
1005 m.insert("x".to_string(), Value::Uint16(1));
1006 tree.insert_value("1.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1007 let mut buf = Vec::new();
1008 tree.write_to(&mut buf).unwrap();
1009
1010 let node_count = crate::Reader::from_source(buf.clone()).unwrap().metadata.node_count as usize;
1011 assert!(buf.len() > node_count * 8);
1012 }
1013
1014 #[test]
1015 fn test_search_tree_size_calculation() {
1016 let opts = WriterOptions {
1017 database_type: "Test".to_string(),
1018 ip_version: 4,
1019 record_size: 24,
1020 ..Default::default()
1021 };
1022 let mut tree = Writer::new(opts).unwrap();
1023 let mut m = BTreeMap::new();
1024 m.insert("x".to_string(), Value::Uint16(1));
1025 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1026 let mut buf = Vec::new();
1027 tree.write_to(&mut buf).unwrap();
1028
1029 let reader = crate::Reader::from_source(&buf[..]).unwrap();
1030 let node_count = reader.metadata.node_count as usize;
1031 let record_size = reader.metadata.record_size as usize;
1032
1033 let tree_size = node_count * record_size / 4;
1036 assert!(tree_size > 0);
1037 assert!(tree_size + 16 < buf.len());
1038 }
1039
1040 #[test]
1041 fn test_metadata_binary_format_version() {
1042 let opts = WriterOptions {
1043 database_type: "Test".to_string(),
1044 ip_version: 4,
1045 record_size: 24,
1046 ..Default::default()
1047 };
1048 let mut tree = Writer::new(opts).unwrap();
1049 let mut m = BTreeMap::new();
1050 m.insert("x".to_string(), Value::Uint16(1));
1051 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1052 let mut buf = Vec::new();
1053 tree.write_to(&mut buf).unwrap();
1054 let reader = crate::Reader::from_source(buf).unwrap();
1055 assert_eq!(reader.metadata.binary_format_major_version, 2);
1056 assert_eq!(reader.metadata.binary_format_minor_version, 0);
1057 }
1058
1059 #[test]
1060 fn test_metadata_description_optional() {
1061 let opts = WriterOptions {
1062 database_type: "Test".to_string(),
1063 ip_version: 4,
1064 record_size: 24,
1065 description: BTreeMap::new(),
1066 ..Default::default()
1067 };
1068 let mut tree = Writer::new(opts).unwrap();
1069 let mut m = BTreeMap::new();
1070 m.insert("x".to_string(), Value::Uint16(1));
1071 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1072 let mut buf = Vec::new();
1073 tree.write_to(&mut buf).unwrap();
1074 let reader = crate::Reader::from_source(buf).unwrap();
1075 assert!(reader.metadata.description.is_empty());
1076 }
1077
1078 #[test]
1079 fn test_languages_optional() {
1080 let opts = WriterOptions {
1081 database_type: "Test".to_string(),
1082 ip_version: 4,
1083 record_size: 24,
1084 languages: vec![],
1085 ..Default::default()
1086 };
1087 let mut tree = Writer::new(opts).unwrap();
1088 let mut m = BTreeMap::new();
1089 m.insert("x".to_string(), Value::Uint16(1));
1090 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1091 let mut buf = Vec::new();
1092 tree.write_to(&mut buf).unwrap();
1093 let reader = crate::Reader::from_source(buf).unwrap();
1094 assert!(reader.metadata.languages.is_empty());
1095 }
1096
1097 #[test]
1098 fn test_insert_multiple_cidrs_then_lookup() {
1099 let opts = WriterOptions {
1100 database_type: "Test".to_string(),
1101 ip_version: 4,
1102 record_size: 24,
1103 ..Default::default()
1104 };
1105 let mut tree = Writer::new(opts).unwrap();
1106 for i in 0u8..10 {
1107 let cidr = format!("{i}.0.0.0/8");
1108 let mut m = BTreeMap::new();
1109 m.insert("net".to_string(), Value::String(cidr.clone()));
1110 tree.insert_value(cidr.parse().unwrap(), Value::Map(m)).unwrap();
1111 }
1112 let mut buf = Vec::new();
1113 tree.write_to(&mut buf).unwrap();
1114 let reader = crate::Reader::from_source(buf).unwrap();
1115 #[derive(serde::Deserialize, Debug)]
1116 struct TestRecord {
1117 net: String,
1118 }
1119 for i in 0u8..10 {
1120 let ip = format!("{i}.0.0.1");
1121 let r: TestRecord = reader.lookup(ip.parse().unwrap()).unwrap();
1122 assert_eq!(r.net, format!("{i}.0.0.0/8"));
1123 }
1124 }
1125
1126 #[test]
1127 fn test_large_string_value() {
1128 let opts = WriterOptions {
1129 database_type: "Test".to_string(),
1130 ip_version: 4,
1131 record_size: 24,
1132 ..Default::default()
1133 };
1134 let mut tree = Writer::new(opts).unwrap();
1135 let long_str = "x".repeat(100);
1136 let mut m = BTreeMap::new();
1137 m.insert("long".to_string(), Value::String(long_str.clone()));
1138 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1139 let mut buf = Vec::new();
1140 tree.write_to(&mut buf).unwrap();
1141 let reader = crate::Reader::from_source(buf).unwrap();
1142 #[derive(serde::Deserialize, Debug)]
1143 struct TestRecord {
1144 long: String,
1145 }
1146 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1147 assert_eq!(r.long, long_str);
1148 }
1149
1150 #[test]
1151 fn test_metadata_start_marker_last_occurrence() {
1152 let opts = WriterOptions {
1153 database_type: "Test".to_string(),
1154 ip_version: 4,
1155 record_size: 24,
1156 ..Default::default()
1157 };
1158 let mut tree = Writer::new(opts).unwrap();
1159 let mut m = BTreeMap::new();
1160 m.insert("x".to_string(), Value::Uint16(1));
1161 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1162 let mut buf = Vec::new();
1163 tree.write_to(&mut buf).unwrap();
1164
1165 let marker = b"\xab\xcd\xefMaxMind.com";
1166 let all_positions: Vec<usize> = buf
1167 .windows(marker.len())
1168 .enumerate()
1169 .filter(|(_, w)| *w == marker)
1170 .map(|(i, _)| i)
1171 .collect();
1172 assert_eq!(all_positions.len(), 1);
1173 let last_pos = all_positions[0];
1174 assert!(last_pos + marker.len() <= buf.len());
1175 }
1176
1177 #[test]
1178 fn test_write_to_is_idempotent() {
1179 let opts = WriterOptions {
1180 database_type: "Test".to_string(),
1181 ip_version: 4,
1182 record_size: 24,
1183 ..Default::default()
1184 };
1185 let mut tree = Writer::new(opts).unwrap();
1186 let mut m = BTreeMap::new();
1187 m.insert("x".to_string(), Value::Uint16(42));
1188 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1189
1190 let mut buf1 = Vec::new();
1191 let n1 = tree.write_to(&mut buf1).unwrap();
1192
1193 let mut buf2 = Vec::new();
1194 let n2 = tree.write_to(&mut buf2).unwrap();
1195
1196 assert_eq!(n1, n2);
1197 assert_eq!(buf1.len(), buf2.len());
1198 assert_eq!(buf1, buf2);
1199 }
1200
1201 #[test]
1202 fn test_zero_length_prefix() {
1203 let opts = WriterOptions {
1204 database_type: "Test".to_string(),
1205 ip_version: 4,
1206 record_size: 24,
1207 ..Default::default()
1208 };
1209 let mut tree = Writer::new(opts).unwrap();
1210 let mut m = BTreeMap::new();
1211 m.insert("all".to_string(), Value::Bool(true));
1212 tree.insert_value("0.0.0.0/0".parse().unwrap(), Value::Map(m)).unwrap();
1213 let mut buf = Vec::new();
1214 tree.write_to(&mut buf).unwrap();
1215 let reader = crate::Reader::from_source(buf).unwrap();
1216 #[derive(serde::Deserialize, Debug)]
1217 struct TestRecord {
1218 all: bool,
1219 }
1220 let r: TestRecord = reader.lookup("255.255.255.255".parse().unwrap()).unwrap();
1221 assert!(r.all);
1222 }
1223
1224 #[test]
1225 fn test_exact_prefix_length() {
1226 let opts = WriterOptions {
1227 database_type: "Test".to_string(),
1228 ip_version: 4,
1229 record_size: 24,
1230 ..Default::default()
1231 };
1232 let mut tree = Writer::new(opts).unwrap();
1233 let mut m = BTreeMap::new();
1234 m.insert("host".to_string(), Value::String("single".to_string()));
1235 tree.insert_value("10.0.0.1/32".parse().unwrap(), Value::Map(m))
1236 .unwrap();
1237 let mut buf = Vec::new();
1238 tree.write_to(&mut buf).unwrap();
1239 let reader = crate::Reader::from_source(buf).unwrap();
1240 #[derive(serde::Deserialize, Debug)]
1241 struct TestRecord {
1242 host: String,
1243 }
1244 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1245 assert_eq!(r.host, "single");
1246 assert!(reader.lookup::<TestRecord>("10.0.0.2".parse().unwrap()).is_err());
1247 }
1248
1249 #[test]
1250 fn test_negative_int32() {
1251 let opts = WriterOptions {
1252 database_type: "Test".to_string(),
1253 ip_version: 4,
1254 record_size: 24,
1255 ..Default::default()
1256 };
1257 let mut tree = Writer::new(opts).unwrap();
1258 let mut m = BTreeMap::new();
1259 m.insert("neg".to_string(), Value::Int32(-1));
1260 m.insert("min".to_string(), Value::Int32(i32::MIN));
1261 m.insert("max".to_string(), Value::Int32(i32::MAX));
1262 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1263 let mut buf = Vec::new();
1264 tree.write_to(&mut buf).unwrap();
1265 let reader = crate::Reader::from_source(buf).unwrap();
1266 #[derive(serde::Deserialize, Debug)]
1267 struct TestRecord {
1268 neg: i32,
1269 min: i32,
1270 max: i32,
1271 }
1272 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1273 assert_eq!(r.neg, -1);
1274 assert_eq!(r.min, i32::MIN);
1275 assert_eq!(r.max, i32::MAX);
1276 }
1277
1278 #[test]
1279 fn test_float32_value() {
1280 let opts = WriterOptions {
1281 database_type: "Test".to_string(),
1282 ip_version: 4,
1283 record_size: 24,
1284 ..Default::default()
1285 };
1286 let mut tree = Writer::new(opts).unwrap();
1287 let mut m = BTreeMap::new();
1288 m.insert("f".to_string(), Value::Float32(1.5));
1289 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1290 let mut buf = Vec::new();
1291 tree.write_to(&mut buf).unwrap();
1292 let reader = crate::Reader::from_source(buf).unwrap();
1293 #[derive(serde::Deserialize, Debug)]
1294 struct TestRecord {
1295 f: f32,
1296 }
1297 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1298 assert!((r.f - 1.5).abs() < 1e-6);
1299 }
1300
1301 #[test]
1302 fn test_map_with_many_keys() {
1303 let opts = WriterOptions {
1304 database_type: "Test".to_string(),
1305 ip_version: 4,
1306 record_size: 24,
1307 ..Default::default()
1308 };
1309 let mut tree = Writer::new(opts).unwrap();
1310 let mut m = BTreeMap::new();
1311 for i in 0..20 {
1312 m.insert(format!("key{i}").to_string(), Value::Uint16(i as u16));
1313 }
1314 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1315 let mut buf = Vec::new();
1316 tree.write_to(&mut buf).unwrap();
1317 let reader = crate::Reader::from_source(buf).unwrap();
1318 #[derive(serde::Deserialize, Debug)]
1319 struct ManyKeys {}
1320 let _: ManyKeys = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1321 }
1322
1323 #[test]
1324 fn test_write_error_on_invalid_record_size() {
1325 let opts = WriterOptions {
1326 database_type: "Test".to_string(),
1327 ip_version: 4,
1328 record_size: 16,
1329 ..Default::default()
1330 };
1331 let result = Writer::new(opts);
1332 assert!(result.is_err());
1333 }
1334
1335 #[test]
1336 fn test_write_error_on_invalid_ip_version() {
1337 let result = Writer::new(WriterOptions {
1338 database_type: "Test".to_string(),
1339 ip_version: 5,
1340 record_size: 24,
1341 ..Default::default()
1342 });
1343 assert!(result.is_err());
1344 assert!(result.unwrap_err().contains("ip_version"));
1345 }
1346
1347 #[test]
1348 fn test_write_error_on_invalid_record_size_in_new() {
1349 let result = Writer::new(WriterOptions {
1350 database_type: "Test".to_string(),
1351 ip_version: 4,
1352 record_size: 20,
1353 ..Default::default()
1354 });
1355 assert!(result.is_err());
1356 assert!(result.unwrap_err().contains("record_size"));
1357 }
1358
1359 #[test]
1360 fn test_write_error_on_record_size_36() {
1361 let result = Writer::new(WriterOptions {
1362 database_type: "Test".to_string(),
1363 ip_version: 4,
1364 record_size: 36,
1365 ..Default::default()
1366 });
1367 assert!(result.is_err());
1368 assert!(result.unwrap_err().contains("record_size"));
1369 }
1370
1371 #[test]
1372 fn test_write_error_ipv6_in_ipv4_tree() {
1373 let mut tree = Writer::new(WriterOptions {
1374 database_type: "Test".to_string(),
1375 ip_version: 4,
1376 record_size: 24,
1377 ..Default::default()
1378 })
1379 .unwrap();
1380 let result = tree.insert_value("::1/128".parse().unwrap(), Value::Bool(true));
1381 assert!(result.is_err());
1382 assert!(result.unwrap_err().contains("IPv6"));
1383 }
1384
1385 #[test]
1386 fn test_float64_value() {
1387 let opts = WriterOptions {
1388 database_type: "Test".to_string(),
1389 ip_version: 4,
1390 record_size: 24,
1391 ..Default::default()
1392 };
1393 let mut tree = Writer::new(opts).unwrap();
1394 let mut m = BTreeMap::new();
1395 m.insert("f64".to_string(), Value::Float64(std::f64::consts::PI));
1396 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1397 let mut buf = Vec::new();
1398 tree.write_to(&mut buf).unwrap();
1399 let reader = crate::Reader::from_source(buf).unwrap();
1400 #[derive(serde::Deserialize, Debug)]
1401 struct TestRecord {
1402 f64: f64,
1403 }
1404 let r: TestRecord = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1405 assert!((r.f64 - std::f64::consts::PI).abs() < 1e-15);
1406 }
1407
1408 #[test]
1409 fn test_bytes_value() {
1410 let opts = WriterOptions {
1411 database_type: "Test".to_string(),
1412 ip_version: 4,
1413 record_size: 24,
1414 ..Default::default()
1415 };
1416 let mut tree = Writer::new(opts).unwrap();
1417 let mut m = BTreeMap::new();
1418 m.insert("raw".to_string(), Value::Bytes(vec![0xde, 0xad, 0xbe, 0xef]));
1419 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1420 let mut buf = Vec::new();
1421 tree.write_to(&mut buf).unwrap();
1422 let reader = crate::Reader::from_source(buf).unwrap();
1423 assert!(reader.metadata.node_count > 0);
1424 }
1425
1426 #[test]
1427 fn test_pointer_encoding_size2() {
1428 let v = Value::Pointer(1000000);
1429 let buf = encoder::encode_value(&v).unwrap();
1430 assert_eq!((buf[0] >> 5) & 0x07, 0b001);
1431 assert_eq!((buf[0] >> 3) & 0x03, 0b10);
1432 assert_eq!(buf.len(), 4);
1433 }
1434
1435 #[test]
1436 fn test_metadata_size_limit() {
1437 let mut desc = BTreeMap::new();
1438 desc.insert("en".to_string(), "x".repeat(65536));
1439 let opts = WriterOptions {
1440 database_type: "LargeMeta".to_string(),
1441 ip_version: 4,
1442 record_size: 24,
1443 description: desc,
1444 ..Default::default()
1445 };
1446 let mut tree = Writer::new(opts).unwrap();
1447 let mut m = BTreeMap::new();
1448 m.insert("x".to_string(), Value::Uint16(1));
1449 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1450 let mut buf = Vec::new();
1451 tree.write_to(&mut buf).unwrap();
1452 let marker = b"\xab\xcd\xefMaxMind.com";
1453 let marker_pos = buf.windows(marker.len()).rposition(|w| w == marker).unwrap();
1454 let metadata_section_size = buf.len() - marker_pos;
1455 assert!(
1456 metadata_section_size <= 128 * 1024,
1457 "metadata section {} bytes exceeds 128 KiB limit",
1458 metadata_section_size
1459 );
1460 }
1461
1462 #[test]
1463 fn test_encoded_size_matches_actual() {
1464 let cases: Vec<Value> = vec![
1465 Value::Pointer(0),
1466 Value::Pointer(2047),
1467 Value::Pointer(2048),
1468 Value::Pointer(526335),
1469 Value::Pointer(526336),
1470 Value::Pointer(134744063),
1471 Value::Pointer(134744064),
1472 Value::Pointer(u32::MAX),
1473 Value::String("hello".to_string()),
1474 Value::String("x".repeat(29)),
1475 Value::String("x".repeat(300)),
1476 Value::Float64(0.0),
1477 Value::Float64(-1.5),
1478 Value::Float64(f64::MAX),
1479 Value::Bytes(vec![]),
1480 Value::Bytes(vec![0; 42]),
1481 Value::Uint16(0),
1482 Value::Uint16(1),
1483 Value::Uint16(u16::MAX),
1484 Value::Uint32(0),
1485 Value::Uint32(1 << 20),
1486 Value::Uint32(u32::MAX),
1487 Value::Int32(0),
1488 Value::Int32(1),
1489 Value::Int32(-1),
1490 Value::Int32(i32::MIN),
1491 Value::Int32(i32::MAX),
1492 Value::Uint64(0),
1493 Value::Uint64(1 << 56),
1494 Value::Uint64(u64::MAX),
1495 Value::Uint128(0),
1496 Value::Uint128(1u128 << 120),
1497 Value::Uint128(u128::MAX),
1498 Value::Bool(true),
1499 Value::Bool(false),
1500 Value::Float32(0.0),
1501 Value::Float32(1.25),
1502 Value::Float32(f32::MAX),
1503 ];
1504
1505 for v in cases {
1506 let encoded = encoder::encode_value(&v).unwrap();
1507 let size = encoder::encoded_size(&v);
1508 assert_eq!(
1509 encoded.len(),
1510 size,
1511 "encoded_size mismatch for {v:?}: got {} encoded but size said {size}",
1512 encoded.len()
1513 );
1514 }
1515 }
1516
1517 #[test]
1518 fn test_encoded_size_matches_actual_empty_string() {
1519 let v = Value::String(String::new());
1520 let encoded = encoder::encode_value(&v).unwrap();
1521 assert_eq!(encoded.len(), encoder::encoded_size(&v));
1522 }
1523
1524 #[test]
1525 fn test_values_after_insert_and_write_are_preserved() {
1526 let v = Value::String("hello".to_string());
1527
1528 let opts = WriterOptions {
1529 database_type: "Test".to_string(),
1530 ip_version: 4,
1531 record_size: 24,
1532 ..Default::default()
1533 };
1534 let mut tree = Writer::new(opts).unwrap();
1535 tree.insert_value("0.0.0.0/0".parse().unwrap(), v.clone()).unwrap();
1536 let mut buf = Vec::new();
1537 tree.write_to(&mut buf).unwrap();
1538 let reader = crate::Reader::from_source(buf).unwrap();
1539 let val: Value = reader.lookup("1.2.3.4".parse().unwrap()).unwrap();
1540 assert_eq!(val, v);
1541 }
1542
1543 #[test]
1544 fn test_u128_roundtrip() {
1545 let opts = WriterOptions {
1546 database_type: "Test".to_string(),
1547 ip_version: 4,
1548 record_size: 24,
1549 ..Default::default()
1550 };
1551 let mut tree = Writer::new(opts).unwrap();
1552 let mut m = BTreeMap::new();
1553 let big = 0xdeadbeefcafebabeu128 << 64 | 0x1234567890abcdefu128;
1554 m.insert("v".to_string(), Value::Uint128(big));
1555 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1556 let mut buf = Vec::new();
1557 tree.write_to(&mut buf).unwrap();
1558 let reader = crate::Reader::from_source(buf).unwrap();
1559 #[derive(serde::Deserialize, Debug)]
1560 struct R {
1561 v: u128,
1562 }
1563 let r: R = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1564 assert_eq!(r.v, big);
1565 }
1566
1567 #[test]
1568 fn test_zero_values() {
1569 let opts = WriterOptions {
1570 database_type: "Test".to_string(),
1571 ip_version: 4,
1572 record_size: 24,
1573 ..Default::default()
1574 };
1575 let mut tree = Writer::new(opts).unwrap();
1576 let mut m = BTreeMap::new();
1577 m.insert("u16".to_string(), Value::Uint16(0));
1578 m.insert("u32".to_string(), Value::Uint32(0));
1579 m.insert("u64".to_string(), Value::Uint64(0));
1580 m.insert("u128".to_string(), Value::Uint128(0));
1581 tree.insert_value("10.0.0.0/8".parse().unwrap(), Value::Map(m)).unwrap();
1582 let mut buf = Vec::new();
1583 tree.write_to(&mut buf).unwrap();
1584 let reader = crate::Reader::from_source(buf).unwrap();
1585 #[derive(serde::Deserialize, Debug)]
1586 struct Zeros {
1587 u16: u16,
1588 u32: u32,
1589 u64: u64,
1590 u128: u128,
1591 }
1592 let r: Zeros = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1593 assert_eq!(r.u16, 0);
1594 assert_eq!(r.u32, 0);
1595 assert_eq!(r.u64, 0);
1596 assert_eq!(r.u128, 0);
1597 }
1598
1599 #[test]
1600 fn test_insert_with_serialize_struct() {
1601 #[derive(serde::Serialize)]
1602 struct Record {
1603 country: String,
1604 }
1605
1606 let opts = WriterOptions {
1607 database_type: "Test".to_string(),
1608 ip_version: 4,
1609 record_size: 24,
1610 ..Default::default()
1611 };
1612 let mut tree = Writer::new(opts).unwrap();
1613 tree.insert(
1614 "1.0.0.0/8".parse().unwrap(),
1615 Record {
1616 country: "US".into(),
1617 },
1618 )
1619 .unwrap();
1620 let mut buf = Vec::new();
1621 tree.write_to(&mut buf).unwrap();
1622 let reader = crate::Reader::from_source(buf).unwrap();
1623 #[derive(serde::Deserialize, Debug)]
1624 struct Record2 {
1625 country: String,
1626 }
1627 let result: Record2 = reader.lookup("1.0.0.1".parse().unwrap()).unwrap();
1628 assert_eq!(result.country, "US");
1629 }
1630
1631 #[test]
1632 fn test_insert_with_serialize_btreemap() {
1633 let opts = WriterOptions {
1634 database_type: "Test".to_string(),
1635 ip_version: 4,
1636 record_size: 24,
1637 ..Default::default()
1638 };
1639 let mut tree = Writer::new(opts).unwrap();
1640 let mut map = BTreeMap::new();
1641 map.insert("value".to_string(), "hello".to_string());
1642 tree.insert("10.0.0.0/8".parse().unwrap(), map).unwrap();
1643 let mut buf = Vec::new();
1644 tree.write_to(&mut buf).unwrap();
1645 let reader = crate::Reader::from_source(buf).unwrap();
1646 #[derive(serde::Deserialize, Debug)]
1647 struct R {
1648 value: String,
1649 }
1650 let result: R = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1651 assert_eq!(result.value, "hello");
1652 }
1653
1654 #[test]
1655 fn test_insert_rejects_negative_out_of_range_integer() {
1656 let opts = WriterOptions {
1657 database_type: "Test".to_string(),
1658 ip_version: 4,
1659 record_size: 24,
1660 ..Default::default()
1661 };
1662 let mut tree = Writer::new(opts).unwrap();
1663 let err = tree.insert("10.0.0.0/8".parse().unwrap(), i64::MIN).unwrap_err();
1664 assert!(err.contains("out of range"), "unexpected error: {err}");
1665 }
1666
1667 #[test]
1668 fn test_insert_accepts_large_positive_integer_as_u64() {
1669 let opts = WriterOptions {
1670 database_type: "Test".to_string(),
1671 ip_version: 4,
1672 record_size: 24,
1673 ..Default::default()
1674 };
1675 let mut tree = Writer::new(opts).unwrap();
1676 let big = i64::MAX;
1677 tree.insert("10.0.0.0/8".parse().unwrap(), big).unwrap();
1678 let mut buf = Vec::new();
1679 tree.write_to(&mut buf).unwrap();
1680
1681 let reader = crate::Reader::from_source(buf).unwrap();
1682 let decoded: u64 = reader.lookup("10.0.0.1".parse().unwrap()).unwrap();
1684 assert_eq!(decoded, big as u64);
1685 }
1686
1687 #[test]
1688 fn test_encode_rejects_oversized_payload() {
1689 let too_big = crate::encoder::Value::String("x".repeat(16_843_037));
1690 let err = crate::encoder::encode_value(&too_big).unwrap_err();
1691 assert!(err.contains("too large"), "unexpected error: {err}");
1692 }
1693
1694 #[test]
1695 fn test_metadata_size_limit_is_enforced() {
1696 let mut description = BTreeMap::new();
1697 description.insert("en".to_string(), "x".repeat(200 * 1024));
1699 let opts = WriterOptions {
1700 database_type: "Test".to_string(),
1701 ip_version: 4,
1702 record_size: 24,
1703 description,
1704 languages: vec!["en".to_string()],
1705 build_epoch: 1,
1706 };
1707 let mut tree = Writer::new(opts).unwrap();
1708 tree.insert("10.0.0.0/8".parse().unwrap(), "v").unwrap();
1709 let err = tree.write_to(&mut Vec::new()).unwrap_err();
1710 assert!(err.contains("metadata"), "unexpected error: {err}");
1711 }
1712}