four_word_networking/
compression.rs

1//! Advanced compression module for IP addresses and ports
2//!
3//! This module implements sophisticated compression techniques to reduce
4//! IP addresses and ports to fit within the 42-bit limit of four words.
5
6use crate::error::FourWordError;
7use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
8
9/// Maximum bits available in four words (3 × 14 bits)
10const MAX_BITS: usize = 42;
11
12/// Address type prefixes (variable length encoding)
13#[repr(u8)]
14#[derive(Debug, Clone, Copy, PartialEq)]
15pub enum AddressType {
16    // IPv4 types (3-bit prefix)
17    Ipv4Localhost = 0b000,  // 127.0.0.0/8
18    Ipv4Private192 = 0b001, // 192.168.0.0/16
19    Ipv4Private10 = 0b010,  // 10.0.0.0/8
20    Ipv4Private172 = 0b011, // 172.16.0.0/12
21    Ipv4Public = 0b100,     // All other IPv4
22    // IPv6 types (4-bit prefix, starts with 0b11)
23    Ipv6Localhost = 0b1100,   // ::1
24    Ipv6LinkLocal = 0b1101,   // fe80::/10
25    Ipv6UniqueLocal = 0b1110, // fc00::/7
26    Ipv6Public = 0b1111,      // All other IPv6
27}
28
29/// Common port encoding (frequency-based)
30#[derive(Debug, Clone)]
31pub struct PortCompressor {
32    // Top 16 most common ports get 4-bit encoding
33    common_ports: Vec<(u16, u8)>,
34    // Next 240 ports get 8-bit encoding
35    frequent_ports: Vec<(u16, u8)>,
36}
37
38impl Default for PortCompressor {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl PortCompressor {
45    pub fn new() -> Self {
46        Self {
47            // Most common ports (4-bit encoding: 0x0-0xF)
48            common_ports: vec![
49                (80, 0x0),    // HTTP
50                (443, 0x1),   // HTTPS
51                (22, 0x2),    // SSH
52                (21, 0x3),    // FTP
53                (25, 0x4),    // SMTP
54                (53, 0x5),    // DNS
55                (8080, 0x6),  // HTTP alt
56                (3306, 0x7),  // MySQL
57                (5432, 0x8),  // PostgreSQL
58                (6379, 0x9),  // Redis
59                (27017, 0xA), // MongoDB
60                (8443, 0xB),  // HTTPS alt
61                (3000, 0xC),  // Dev server
62                (5000, 0xD),  // Dev server
63                (8000, 0xE),  // Dev server
64                (9000, 0xF),  // Various
65            ],
66            // Next most frequent ports (8-bit encoding: 0x10-0xFF)
67            frequent_ports: vec![
68                (23, 0x10),   // Telnet
69                (110, 0x11),  // POP3
70                (143, 0x12),  // IMAP
71                (445, 0x13),  // SMB
72                (1433, 0x14), // MSSQL
73                (1521, 0x15), // Oracle
74                (2049, 0x16), // NFS
75                (3389, 0x17), // RDP
76                (5900, 0x18), // VNC
77                (8081, 0x19), // HTTP alt
78                (8082, 0x1A), // HTTP alt
79                (8083, 0x1B), // HTTP alt
80                (8888, 0x1C), // HTTP alt
81                (9090, 0x1D), // Web admin
82                (9200, 0x1E), // Elasticsearch
83                (11211, 0x1F), // Memcached
84                              // ... could add more up to 0xFF
85            ],
86        }
87    }
88
89    pub fn compress(&self, port: Option<u16>) -> (Vec<u8>, usize) {
90        match port {
91            None => (vec![], 0), // No port = 0 bits
92            Some(p) => {
93                // Check common ports (4 bits)
94                if let Some((_, code)) = self.common_ports.iter().find(|(port, _)| *port == p) {
95                    (vec![*code], 4)
96                }
97                // Check frequent ports (8 bits)
98                else if let Some((_, code)) =
99                    self.frequent_ports.iter().find(|(port, _)| *port == p)
100                {
101                    (vec![*code], 8)
102                }
103                // Full port (16 bits)
104                else {
105                    (vec![(p >> 8) as u8, (p & 0xFF) as u8], 16)
106                }
107            }
108        }
109    }
110
111    pub fn decompress(&self, data: &[u8], bits: usize) -> Result<Option<u16>, FourWordError> {
112        match bits {
113            0 => Ok(None),
114            4 => {
115                let code = data[0] & 0x0F;
116                self.common_ports
117                    .iter()
118                    .find(|(_, c)| *c == code)
119                    .map(|(port, _)| Some(*port))
120                    .ok_or_else(|| {
121                        FourWordError::InvalidInput("Invalid common port code".to_string())
122                    })
123            }
124            8 => {
125                let code = data[0];
126                // First check if it's a common port with full byte
127                if code <= 0x0F {
128                    self.common_ports
129                        .iter()
130                        .find(|(_, c)| *c == code)
131                        .map(|(port, _)| Some(*port))
132                        .ok_or_else(|| FourWordError::InvalidInput("Invalid port code".to_string()))
133                } else {
134                    self.frequent_ports
135                        .iter()
136                        .find(|(_, c)| *c == code)
137                        .map(|(port, _)| Some(*port))
138                        .ok_or_else(|| {
139                            FourWordError::InvalidInput("Invalid frequent port code".to_string())
140                        })
141                }
142            }
143            16 => {
144                if data.len() >= 2 {
145                    Ok(Some(((data[0] as u16) << 8) | (data[1] as u16)))
146                } else {
147                    Err(FourWordError::InvalidInput(
148                        "Insufficient data for port".to_string(),
149                    ))
150                }
151            }
152            _ => Err(FourWordError::InvalidInput(format!(
153                "Invalid port bit count: {bits}"
154            ))),
155        }
156    }
157}
158
159/// Main compression engine
160pub struct IpCompressor {
161    port_compressor: PortCompressor,
162}
163
164impl Default for IpCompressor {
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170impl IpCompressor {
171    pub fn new() -> Self {
172        Self {
173            port_compressor: PortCompressor::new(),
174        }
175    }
176
177    /// Compress an IP address with optional port into minimal bits
178    pub fn compress(
179        &self,
180        ip: &IpAddr,
181        port: Option<u16>,
182    ) -> Result<CompressedAddress, FourWordError> {
183        let (addr_type, addr_data, addr_bits) = self.compress_address(ip)?;
184        let (port_data, port_bits) = self.port_compressor.compress(port);
185
186        let total_bits = addr_bits + port_bits;
187        if total_bits > MAX_BITS {
188            return Err(FourWordError::InvalidInput(format!(
189                "Compressed size {total_bits} bits exceeds maximum {MAX_BITS} bits"
190            )));
191        }
192
193        Ok(CompressedAddress {
194            addr_type,
195            addr_data,
196            addr_bits,
197            port_data,
198            port_bits,
199            total_bits,
200        })
201    }
202
203    /// Compress IP address based on type and pattern
204    fn compress_address(
205        &self,
206        ip: &IpAddr,
207    ) -> Result<(AddressType, Vec<u8>, usize), FourWordError> {
208        match ip {
209            IpAddr::V4(ipv4) => self.compress_ipv4(ipv4),
210            IpAddr::V6(ipv6) => self.compress_ipv6(ipv6),
211        }
212    }
213
214    fn compress_ipv4(
215        &self,
216        ipv4: &Ipv4Addr,
217    ) -> Result<(AddressType, Vec<u8>, usize), FourWordError> {
218        let octets = ipv4.octets();
219
220        // Localhost: 127.x.x.x (3-bit type + 8 bits for last octet = 11 bits)
221        if octets[0] == 127 {
222            Ok((AddressType::Ipv4Localhost, vec![octets[3]], 11))
223        }
224        // Private 192.168.x.x (3-bit type + 16 bits = 19 bits)
225        else if octets[0] == 192 && octets[1] == 168 {
226            Ok((AddressType::Ipv4Private192, vec![octets[2], octets[3]], 19))
227        }
228        // Private 10.x.x.x (3-bit type + 24 bits = 27 bits)
229        else if octets[0] == 10 {
230            Ok((
231                AddressType::Ipv4Private10,
232                vec![octets[1], octets[2], octets[3]],
233                27,
234            ))
235        }
236        // Private 172.16-31.x.x (3-bit type + 4 bits for range + 16 bits = 23 bits)
237        else if octets[0] == 172 && octets[1] >= 16 && octets[1] <= 31 {
238            let range_bits = octets[1] - 16; // 0-15 fits in 4 bits
239            Ok((
240                AddressType::Ipv4Private172,
241                vec![range_bits, octets[2], octets[3]],
242                23,
243            ))
244        }
245        // Public IPv4 (3-bit type + 32 bits = 35 bits)
246        else {
247            Ok((AddressType::Ipv4Public, octets.to_vec(), 35))
248        }
249    }
250
251    fn compress_ipv6(
252        &self,
253        ipv6: &Ipv6Addr,
254    ) -> Result<(AddressType, Vec<u8>, usize), FourWordError> {
255        let segments = ipv6.segments();
256
257        // Localhost ::1 (4-bit type only = 4 bits)
258        if ipv6.is_loopback() {
259            Ok((AddressType::Ipv6Localhost, vec![], 4))
260        }
261        // Link-local fe80::/10 (4-bit type + interface ID = variable)
262        else if segments[0] & 0xFFC0 == 0xFE80 {
263            // For link-local, we could store just the interface ID (last 64 bits)
264            // But that's still 64 bits, too large for our system
265            // Instead, we'll store a hash or truncated version
266            let interface_id = ((segments[6] as u32) << 16) | (segments[7] as u32);
267            Ok((
268                AddressType::Ipv6LinkLocal,
269                vec![
270                    (interface_id >> 24) as u8,
271                    (interface_id >> 16) as u8,
272                    (interface_id >> 8) as u8,
273                    interface_id as u8,
274                ],
275                36,
276            )) // 4-bit type + 32-bit truncated interface ID
277        }
278        // Unique local fc00::/7 (4-bit type + subnet ID = variable)
279        else if segments[0] & 0xFE00 == 0xFC00 {
280            // Store first 48 bits (prefix + global ID + partial subnet)
281            Ok((
282                AddressType::Ipv6UniqueLocal,
283                vec![
284                    (segments[0] >> 8) as u8,
285                    segments[0] as u8,
286                    (segments[1] >> 8) as u8,
287                    segments[1] as u8,
288                    (segments[2] >> 8) as u8,
289                    segments[2] as u8,
290                ],
291                52,
292            )) // 4-bit type + 48 bits
293        }
294        // Public IPv6 - too large to fit
295        else {
296            Err(FourWordError::InvalidInput(
297                "Public IPv6 addresses cannot be compressed to fit in 42 bits".to_string(),
298            ))
299        }
300    }
301
302    /// Decompress back to IP address and port
303    pub fn decompress(
304        &self,
305        compressed: &CompressedAddress,
306    ) -> Result<(IpAddr, Option<u16>), FourWordError> {
307        let ip = self.decompress_address(compressed.addr_type, &compressed.addr_data)?;
308        let port = self
309            .port_compressor
310            .decompress(&compressed.port_data, compressed.port_bits)?;
311        Ok((ip, port))
312    }
313
314    fn decompress_address(
315        &self,
316        addr_type: AddressType,
317        data: &[u8],
318    ) -> Result<IpAddr, FourWordError> {
319        match addr_type {
320            AddressType::Ipv4Localhost => {
321                if !data.is_empty() {
322                    Ok(IpAddr::V4(Ipv4Addr::new(127, 0, 0, data[0])))
323                } else {
324                    Ok(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))
325                }
326            }
327            AddressType::Ipv4Private192 => {
328                if data.len() >= 2 {
329                    Ok(IpAddr::V4(Ipv4Addr::new(192, 168, data[0], data[1])))
330                } else {
331                    Err(FourWordError::InvalidInput(
332                        "Insufficient data for 192.168.x.x".to_string(),
333                    ))
334                }
335            }
336            AddressType::Ipv4Private10 => {
337                if data.len() >= 3 {
338                    Ok(IpAddr::V4(Ipv4Addr::new(10, data[0], data[1], data[2])))
339                } else {
340                    Err(FourWordError::InvalidInput(
341                        "Insufficient data for 10.x.x.x".to_string(),
342                    ))
343                }
344            }
345            AddressType::Ipv4Private172 => {
346                if data.len() >= 3 {
347                    let second_octet = 16 + data[0]; // Restore range 16-31
348                    Ok(IpAddr::V4(Ipv4Addr::new(
349                        172,
350                        second_octet,
351                        data[1],
352                        data[2],
353                    )))
354                } else {
355                    Err(FourWordError::InvalidInput(
356                        "Insufficient data for 172.16-31.x.x".to_string(),
357                    ))
358                }
359            }
360            AddressType::Ipv4Public => {
361                if data.len() >= 4 {
362                    Ok(IpAddr::V4(Ipv4Addr::new(
363                        data[0], data[1], data[2], data[3],
364                    )))
365                } else {
366                    Err(FourWordError::InvalidInput(
367                        "Insufficient data for public IPv4".to_string(),
368                    ))
369                }
370            }
371            AddressType::Ipv6Localhost => Ok(IpAddr::V6(Ipv6Addr::LOCALHOST)),
372            AddressType::Ipv6LinkLocal => {
373                if data.len() >= 4 {
374                    // Reconstruct a link-local address with the interface ID
375                    let interface_id = ((data[0] as u32) << 24)
376                        | ((data[1] as u32) << 16)
377                        | ((data[2] as u32) << 8)
378                        | (data[3] as u32);
379                    Ok(IpAddr::V6(Ipv6Addr::new(
380                        0xfe80,
381                        0,
382                        0,
383                        0,
384                        0,
385                        0,
386                        (interface_id >> 16) as u16,
387                        (interface_id & 0xFFFF) as u16,
388                    )))
389                } else {
390                    Err(FourWordError::InvalidInput(
391                        "Insufficient data for link-local IPv6".to_string(),
392                    ))
393                }
394            }
395            AddressType::Ipv6UniqueLocal => {
396                if data.len() >= 6 {
397                    let seg0 = ((data[0] as u16) << 8) | (data[1] as u16);
398                    let seg1 = ((data[2] as u16) << 8) | (data[3] as u16);
399                    let seg2 = ((data[4] as u16) << 8) | (data[5] as u16);
400                    Ok(IpAddr::V6(Ipv6Addr::new(seg0, seg1, seg2, 0, 0, 0, 0, 0)))
401                } else {
402                    Err(FourWordError::InvalidInput(
403                        "Insufficient data for unique local IPv6".to_string(),
404                    ))
405                }
406            }
407            AddressType::Ipv6Public => Err(FourWordError::InvalidInput(
408                "Public IPv6 decompression not supported".to_string(),
409            )),
410        }
411    }
412}
413
414/// Compressed address representation
415#[derive(Debug, Clone)]
416pub struct CompressedAddress {
417    pub addr_type: AddressType,
418    pub addr_data: Vec<u8>,
419    pub addr_bits: usize,
420    pub port_data: Vec<u8>,
421    pub port_bits: usize,
422    pub total_bits: usize,
423}
424
425impl CompressedAddress {
426    /// Pack into a bit stream for encoding
427    pub fn pack(&self) -> Vec<u8> {
428        let mut bits = BitWriter::new();
429
430        // Write address type prefix
431        match self.addr_type {
432            // 3-bit prefixes
433            AddressType::Ipv4Localhost
434            | AddressType::Ipv4Private192
435            | AddressType::Ipv4Private10
436            | AddressType::Ipv4Private172
437            | AddressType::Ipv4Public => {
438                bits.write_bits(self.addr_type as u32, 3);
439            }
440            // 4-bit prefixes
441            AddressType::Ipv6Localhost
442            | AddressType::Ipv6LinkLocal
443            | AddressType::Ipv6UniqueLocal
444            | AddressType::Ipv6Public => {
445                bits.write_bits(self.addr_type as u32, 4);
446            }
447        }
448
449        // Write address data with appropriate bit sizes
450        match self.addr_type {
451            AddressType::Ipv4Localhost => {
452                // 8 bits for last octet
453                if !self.addr_data.is_empty() {
454                    bits.write_bits(self.addr_data[0] as u32, 8);
455                }
456            }
457            AddressType::Ipv4Private192 => {
458                // 8 bits + 8 bits for last two octets
459                for byte in &self.addr_data {
460                    bits.write_bits(*byte as u32, 8);
461                }
462            }
463            AddressType::Ipv4Private10 => {
464                // 8 bits + 8 bits + 8 bits for last three octets
465                for byte in &self.addr_data {
466                    bits.write_bits(*byte as u32, 8);
467                }
468            }
469            AddressType::Ipv4Private172 => {
470                // 4 bits for range + 8 bits + 8 bits for last two octets
471                if self.addr_data.len() >= 3 {
472                    bits.write_bits(self.addr_data[0] as u32, 4); // range_bits (4 bits)
473                    bits.write_bits(self.addr_data[1] as u32, 8); // third octet
474                    bits.write_bits(self.addr_data[2] as u32, 8); // fourth octet
475                }
476            }
477            AddressType::Ipv4Public => {
478                // 8 bits × 4 for all octets
479                for byte in &self.addr_data {
480                    bits.write_bits(*byte as u32, 8);
481                }
482            }
483            AddressType::Ipv6Localhost => {
484                // No additional data
485            }
486            AddressType::Ipv6LinkLocal => {
487                // 8 bits × 4 for interface ID
488                for byte in &self.addr_data {
489                    bits.write_bits(*byte as u32, 8);
490                }
491            }
492            AddressType::Ipv6UniqueLocal => {
493                // 8 bits × 6 for prefix data
494                for byte in &self.addr_data {
495                    bits.write_bits(*byte as u32, 8);
496                }
497            }
498            AddressType::Ipv6Public => {
499                // Should not reach here as it's rejected during compression
500            }
501        }
502
503        // Write port flag and data
504        if self.port_bits > 0 {
505            bits.write_bits(1, 1); // Has port
506            if self.port_bits == 4 {
507                bits.write_bits(0, 1); // Common port marker
508                bits.write_bits(self.port_data[0] as u32, 4);
509            } else if self.port_bits == 8 {
510                bits.write_bits(1, 1); // Frequent port marker
511                bits.write_bits(0, 1); // Not full port
512                bits.write_bits(self.port_data[0] as u32, 8);
513            } else {
514                bits.write_bits(1, 1); // Frequent port marker
515                bits.write_bits(1, 1); // Full port marker
516                bits.write_bits(
517                    ((self.port_data[0] as u32) << 8) | (self.port_data[1] as u32),
518                    16,
519                );
520            }
521        } else {
522            bits.write_bits(0, 1); // No port
523        }
524
525        bits.finish()
526    }
527
528    /// Unpack from a bit stream
529    pub fn unpack(
530        data: &[u8],
531        compressor: &IpCompressor,
532    ) -> Result<(IpAddr, Option<u16>), FourWordError> {
533        let mut bits = BitReader::new(data);
534
535        // Read address type prefix
536        let first_3_bits = bits.read_bits(3)? as u8;
537        let (addr_type, _type_bits) = if first_3_bits < 0b110 {
538            // IPv4 type (3-bit prefix)
539            (
540                match first_3_bits {
541                    0b000 => AddressType::Ipv4Localhost,
542                    0b001 => AddressType::Ipv4Private192,
543                    0b010 => AddressType::Ipv4Private10,
544                    0b011 => AddressType::Ipv4Private172,
545                    0b100 => AddressType::Ipv4Public,
546                    _ => unreachable!(),
547                },
548                3,
549            )
550        } else {
551            // IPv6 type (4-bit prefix)
552            let fourth_bit = bits.read_bits(1)? as u8;
553            let ipv6_type = (first_3_bits << 1) | fourth_bit;
554            (
555                match ipv6_type {
556                    0b1100 => AddressType::Ipv6Localhost,
557                    0b1101 => AddressType::Ipv6LinkLocal,
558                    0b1110 => AddressType::Ipv6UniqueLocal,
559                    0b1111 => AddressType::Ipv6Public,
560                    _ => unreachable!(),
561                },
562                4,
563            )
564        };
565
566        // Read address data based on type
567        let addr_data = match addr_type {
568            AddressType::Ipv4Localhost => vec![bits.read_bits(8)? as u8],
569            AddressType::Ipv4Private192 => vec![bits.read_bits(8)? as u8, bits.read_bits(8)? as u8],
570            AddressType::Ipv4Private10 => vec![
571                bits.read_bits(8)? as u8,
572                bits.read_bits(8)? as u8,
573                bits.read_bits(8)? as u8,
574            ],
575            AddressType::Ipv4Private172 => vec![
576                bits.read_bits(4)? as u8,
577                bits.read_bits(8)? as u8,
578                bits.read_bits(8)? as u8,
579            ],
580            AddressType::Ipv4Public => vec![
581                bits.read_bits(8)? as u8,
582                bits.read_bits(8)? as u8,
583                bits.read_bits(8)? as u8,
584                bits.read_bits(8)? as u8,
585            ],
586            AddressType::Ipv6Localhost => vec![],
587            AddressType::Ipv6LinkLocal => vec![
588                bits.read_bits(8)? as u8,
589                bits.read_bits(8)? as u8,
590                bits.read_bits(8)? as u8,
591                bits.read_bits(8)? as u8,
592            ],
593            AddressType::Ipv6UniqueLocal => vec![
594                bits.read_bits(8)? as u8,
595                bits.read_bits(8)? as u8,
596                bits.read_bits(8)? as u8,
597                bits.read_bits(8)? as u8,
598                bits.read_bits(8)? as u8,
599                bits.read_bits(8)? as u8,
600            ],
601            AddressType::Ipv6Public => {
602                return Err(FourWordError::InvalidInput(
603                    "Public IPv6 not supported".to_string(),
604                ));
605            }
606        };
607
608        // Read port if present
609        let port = if bits.read_bits(1)? == 1 {
610            if bits.read_bits(1)? == 0 {
611                // Common port (4 bits)
612                let code = bits.read_bits(4)? as u8;
613                compressor.port_compressor.decompress(&[code], 4)?
614            } else if bits.read_bits(1)? == 0 {
615                // Frequent port (8 bits)
616                let code = bits.read_bits(8)? as u8;
617                compressor.port_compressor.decompress(&[code], 8)?
618            } else {
619                // Full port (16 bits)
620                let port_value = bits.read_bits(16)?;
621                Some(port_value as u16)
622            }
623        } else {
624            None
625        };
626
627        let ip = compressor.decompress_address(addr_type, &addr_data)?;
628        Ok((ip, port))
629    }
630}
631
632/// Bit-level writer for packing data
633struct BitWriter {
634    data: Vec<u8>,
635    current_byte: u8,
636    bits_in_current: usize,
637}
638
639impl BitWriter {
640    fn new() -> Self {
641        Self {
642            data: Vec::new(),
643            current_byte: 0,
644            bits_in_current: 0,
645        }
646    }
647
648    fn write_bits(&mut self, value: u32, num_bits: usize) {
649        let mut bits_to_write = num_bits;
650
651        while bits_to_write > 0 {
652            let bits_available = 8 - self.bits_in_current;
653            let bits_this_round = bits_to_write.min(bits_available);
654
655            let mask = (1 << bits_this_round) - 1;
656            let bits = ((value >> (bits_to_write - bits_this_round)) & mask) as u8;
657
658            self.current_byte = (self.current_byte << bits_this_round) | bits;
659            self.bits_in_current += bits_this_round;
660
661            if self.bits_in_current == 8 {
662                self.data.push(self.current_byte);
663                self.current_byte = 0;
664                self.bits_in_current = 0;
665            }
666
667            bits_to_write -= bits_this_round;
668        }
669    }
670
671    fn finish(mut self) -> Vec<u8> {
672        if self.bits_in_current > 0 {
673            // Pad the remaining bits to complete the byte
674            self.current_byte <<= 8 - self.bits_in_current;
675            self.data.push(self.current_byte);
676        }
677        self.data
678    }
679
680    /// Get the total number of bits written
681    #[allow(dead_code)]
682    fn bit_count(&self) -> usize {
683        self.data.len() * 8 + self.bits_in_current
684    }
685}
686
687/// Bit-level reader for unpacking data
688struct BitReader<'a> {
689    data: &'a [u8],
690    byte_index: usize,
691    bit_index: usize,
692}
693
694impl<'a> BitReader<'a> {
695    fn new(data: &'a [u8]) -> Self {
696        Self {
697            data,
698            byte_index: 0,
699            bit_index: 0,
700        }
701    }
702
703    fn read_bits(&mut self, num_bits: usize) -> Result<u32, FourWordError> {
704        let mut result = 0u32;
705        let mut bits_read = 0;
706
707        while bits_read < num_bits {
708            if self.byte_index >= self.data.len() {
709                return Err(FourWordError::InvalidInput(
710                    "Insufficient data for bit reading".to_string(),
711                ));
712            }
713
714            let bits_available = 8 - self.bit_index;
715            let bits_to_read = (num_bits - bits_read).min(bits_available);
716
717            let mask = ((1 << bits_to_read) - 1) as u8;
718            let bits = (self.data[self.byte_index] >> (bits_available - bits_to_read)) & mask;
719
720            result = (result << bits_to_read) | (bits as u32);
721            bits_read += bits_to_read;
722
723            self.bit_index += bits_to_read;
724            if self.bit_index == 8 {
725                self.byte_index += 1;
726                self.bit_index = 0;
727            }
728        }
729
730        Ok(result)
731    }
732}
733
734#[cfg(test)]
735mod tests {
736    use super::*;
737
738    #[test]
739    fn test_ipv4_compression() {
740        let compressor = IpCompressor::new();
741
742        // Test localhost
743        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
744        let compressed = compressor.compress(&ip, Some(80)).unwrap();
745        assert!(compressed.total_bits <= 15); // 11 bits for IP + 4 bits for common port
746
747        // Test private network
748        let ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100));
749        let compressed = compressor.compress(&ip, Some(443)).unwrap();
750        assert!(compressed.total_bits <= 23); // 19 bits for IP + 4 bits for common port
751
752        // Round trip test
753        let (decompressed_ip, decompressed_port) = compressor.decompress(&compressed).unwrap();
754        assert_eq!(decompressed_ip, ip);
755        assert_eq!(decompressed_port, Some(443));
756    }
757
758    #[test]
759    fn test_ipv6_compression() {
760        let compressor = IpCompressor::new();
761
762        // Test localhost
763        let ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
764        let compressed = compressor.compress(&ip, Some(22)).unwrap();
765        assert!(compressed.total_bits <= 8); // 4 bits for type + 4 bits for common port
766
767        // Round trip test
768        let (decompressed_ip, decompressed_port) = compressor.decompress(&compressed).unwrap();
769        assert_eq!(decompressed_ip, ip);
770        assert_eq!(decompressed_port, Some(22));
771    }
772
773    #[test]
774    fn test_port_compression() {
775        let compressor = PortCompressor::new();
776
777        // Common port
778        let (data, bits) = compressor.compress(Some(80));
779        assert_eq!(bits, 4);
780        assert_eq!(compressor.decompress(&data, bits).unwrap(), Some(80));
781
782        // Frequent port
783        let (data, bits) = compressor.compress(Some(3389));
784        assert_eq!(bits, 8);
785        assert_eq!(compressor.decompress(&data, bits).unwrap(), Some(3389));
786
787        // Arbitrary port
788        let (data, bits) = compressor.compress(Some(12345));
789        assert_eq!(bits, 16);
790        assert_eq!(compressor.decompress(&data, bits).unwrap(), Some(12345));
791    }
792
793    #[test]
794    fn test_bit_packing() {
795        let compressor = IpCompressor::new();
796
797        let ip = IpAddr::V4(Ipv4Addr::new(10, 20, 30, 40));
798        let compressed = compressor.compress(&ip, Some(8080)).unwrap();
799
800        // Pack and unpack
801        let packed = compressed.pack();
802        assert!(packed.len() <= 6); // Should fit in 6 bytes max
803
804        let (unpacked_ip, unpacked_port) = CompressedAddress::unpack(&packed, &compressor).unwrap();
805        assert_eq!(unpacked_ip, ip);
806        assert_eq!(unpacked_port, Some(8080));
807    }
808}