Skip to main content

rupg_types/
network.rs

1//! `inet` and `cidr`, an IPv4 or IPv6 address with the length of its network part.
2//!
3//! The text input is a port of `network_in` in `src/backend/utils/adt/network.c` with `inet_net_pton.c`. The text output is a port of `network_out` with `pg_inet_net_ntop` in `src/port/inet_net_ntop.c`, and [`cidr_abbrev`] is a port of `inet_cidr_ntop.c`. The binary forms are `network_recv` and `network_send`, and [`network_cmp`] is `network_cmp_internal`.
4//!
5//! The two types have the same value. The type of the expression tells if the value is a `cidr`: the output of a `cidr` always has the mask length, and the input and the receive function of a `cidr` refuse a value with bits set to the right of the mask.
6
7use std::cmp::Ordering;
8
9use rupg_common::SqlState;
10
11use crate::binary::Recv;
12use crate::error::TypeError;
13
14/// The family of an address. IPv4 sorts before IPv6.
15#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub enum NetFamily {
17    V4,
18    V6,
19}
20
21impl NetFamily {
22    /// The bits of an address, 32 or 128.
23    pub fn max_bits(self) -> u8 {
24        match self {
25            NetFamily::V4 => 32,
26            NetFamily::V6 => 128,
27        }
28    }
29
30    /// The bytes of an address, 4 or 16.
31    pub fn size(self) -> usize {
32        match self {
33            NetFamily::V4 => 4,
34            NetFamily::V6 => 16,
35        }
36    }
37
38    /// `PGSQL_AF_INET` or `PGSQL_AF_INET6`, the first byte of the binary form.
39    fn code(self) -> u8 {
40        match self {
41            NetFamily::V4 => 2,
42            NetFamily::V6 => 3,
43        }
44    }
45}
46
47/// A value of `inet` or `cidr`. The bytes of `addr` after the size of the family are 0.
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
49pub struct Inet {
50    pub family: NetFamily,
51    /// The length of the network part, up to [`NetFamily::max_bits`].
52    pub bits: u8,
53    /// The address in network byte order.
54    pub addr: [u8; 16],
55}
56
57impl Inet {
58    /// The bytes of the address.
59    pub fn bytes(&self) -> &[u8] {
60        &self.addr[..self.family.size()]
61    }
62
63    /// `cidr_set_masklen_internal`: the value with the mask length `bits` and the bits to the right of the mask set to 0. `bits` must be valid for the family.
64    #[must_use]
65    pub fn masked(&self, bits: u8) -> Inet {
66        let mut addr = [0u8; 16];
67        let whole = usize::from(bits / 8);
68        addr[..whole].copy_from_slice(&self.addr[..whole]);
69        if !bits.is_multiple_of(8) {
70            addr[whole] = self.addr[whole] & !(0xff >> (bits % 8));
71        }
72        Inet { family: self.family, bits, addr }
73    }
74}
75
76/// The name of the type in the errors.
77fn type_name(cidr: bool) -> &'static str {
78    if cidr { "cidr" } else { "inet" }
79}
80
81/// The text input of `inet` and `cidr`. A value with a colon is IPv6. The input of `inet` takes an IPv4 address with no mask length only when it has the four octets, and the input of `cidr` takes fewer octets and finds the mask length from the class of the address.
82///
83/// # Errors
84///
85/// `22P02` for a value that is not an address, and for a `cidr` value with bits set to the right of the mask.
86pub fn inet_in(text: &str, cidr: bool) -> Result<Inet, TypeError> {
87    // The parsers read a C string: a 0 byte ends it.
88    let mut src = text.as_bytes().to_vec();
89    src.push(0);
90    let family = if text.contains(':') { NetFamily::V6 } else { NetFamily::V4 };
91    let mut addr = [0u8; 16];
92    let bits = match (family, cidr) {
93        (NetFamily::V4, false) => net_pton_v4(&src, &mut addr),
94        (NetFamily::V4, true) => cidr_pton_v4(&src, &mut addr),
95        (NetFamily::V6, _) => pton_v6(&src, &mut addr),
96    };
97    let Some(bits) = bits.and_then(|b| u8::try_from(b).ok()).filter(|b| *b <= family.max_bits())
98    else {
99        return Err(TypeError::syntax(type_name(cidr), text));
100    };
101    if cidr && !address_ok(&addr, bits, family) {
102        let mut error = TypeError::new(
103            SqlState::INVALID_TEXT_REPRESENTATION,
104            format!("invalid cidr value: \"{text}\""),
105        );
106        error.detail = Some("Value has bits set to right of mask.".into());
107        return Err(error);
108    }
109    Ok(Inet { family, bits, addr })
110}
111
112/// The value of an ASCII digit.
113fn digit(ch: u8) -> Option<i32> {
114    ch.is_ascii_digit().then(|| i32::from(ch - b'0'))
115}
116
117/// The value of an ASCII hex digit in either case.
118fn xdigit(ch: u8) -> Option<u32> {
119    char::from(ch).to_digit(16)
120}
121
122/// Reads the digits of a mask length from `src[i]` up to the first byte that is not a digit, and gives the length and the index after that byte. A length above 32 stays above 32.
123fn mask_length(src: &[u8], mut i: usize) -> (i32, u8, usize) {
124    let mut bits = 0;
125    loop {
126        let ch = src[i];
127        i += 1;
128        match digit(ch) {
129            Some(n) => bits = (bits * 10 + n).min(1000),
130            None => return (bits, ch, i),
131        }
132    }
133}
134
135/// `inet_cidr_pton_ipv4`: the input of an IPv4 `cidr`. It takes hex octets after `0x`, or 1 to 4 decimal octets, and an optional mask length. With no mask length, the class of the address gives it.
136fn cidr_pton_v4(src: &[u8], dst: &mut [u8; 16]) -> Option<i32> {
137    let mut written = 0usize;
138    let mut i = 0usize;
139    let mut ch = src[i];
140    i += 1;
141    if ch == b'0' && (src[i] == b'x' || src[i] == b'X') && src[i + 1].is_ascii_hexdigit() {
142        let mut dirty = 0;
143        let mut tmp = 0u32;
144        i += 1;
145        loop {
146            ch = src[i];
147            i += 1;
148            let Some(n) = xdigit(ch) else { break };
149            tmp = if dirty == 0 { n } else { (tmp << 4) | n };
150            dirty += 1;
151            if dirty == 2 {
152                if written == 4 {
153                    return None;
154                }
155                dst[written] = u8::try_from(tmp).ok()?;
156                written += 1;
157                dirty = 0;
158            }
159        }
160        if dirty != 0 {
161            if written == 4 {
162                return None;
163            }
164            dst[written] = u8::try_from(tmp << 4).ok()?;
165            written += 1;
166        }
167    } else if ch.is_ascii_digit() {
168        loop {
169            let mut tmp = 0;
170            while let Some(n) = digit(ch) {
171                tmp = tmp * 10 + n;
172                if tmp > 255 {
173                    return None;
174                }
175                ch = src[i];
176                i += 1;
177            }
178            if written == 4 {
179                return None;
180            }
181            dst[written] = u8::try_from(tmp).ok()?;
182            written += 1;
183            if ch == 0 || ch == b'/' {
184                break;
185            }
186            if ch != b'.' {
187                return None;
188            }
189            ch = src[i];
190            i += 1;
191            if !ch.is_ascii_digit() {
192                return None;
193            }
194        }
195    } else {
196        return None;
197    }
198    let mut bits = -1;
199    if ch == b'/' && src[i].is_ascii_digit() && written > 0 {
200        let (n, end, _) = mask_length(src, i);
201        if end != 0 || n > 32 {
202            return None;
203        }
204        bits = n;
205        ch = end;
206    }
207    if ch != 0 || written == 0 {
208        return None;
209    }
210    let octets = i32::try_from(written).ok()? * 8;
211    if bits == -1 {
212        bits = match dst[0] {
213            240.. => 32,
214            224.. => 8,
215            192.. => 24,
216            128.. => 16,
217            _ => 8,
218        };
219        bits = bits.max(octets);
220        if bits == 8 && dst[0] == 224 {
221            bits = 4;
222        }
223    }
224    // The bytes after the octets are 0 already.
225    Some(bits)
226}
227
228/// `inet_net_pton_ipv4`: the input of an IPv4 `inet`, 1 to 4 decimal octets and an optional mask length. With no mask length, the address must have the four octets and the length is 32.
229fn net_pton_v4(src: &[u8], dst: &mut [u8; 16]) -> Option<i32> {
230    let mut written = 0usize;
231    let mut i = 0usize;
232    let mut ch;
233    loop {
234        ch = src[i];
235        i += 1;
236        if !ch.is_ascii_digit() {
237            break;
238        }
239        let mut tmp = 0;
240        while let Some(n) = digit(ch) {
241            tmp = tmp * 10 + n;
242            if tmp > 255 {
243                return None;
244            }
245            ch = src[i];
246            i += 1;
247        }
248        if written == 4 {
249            return None;
250        }
251        dst[written] = u8::try_from(tmp).ok()?;
252        written += 1;
253        if ch == 0 || ch == b'/' {
254            break;
255        }
256        if ch != b'.' {
257            return None;
258        }
259    }
260    let mut bits = -1;
261    if ch == b'/' && src[i].is_ascii_digit() && written > 0 {
262        let (n, end, _) = mask_length(src, i);
263        if end != 0 || n > 32 {
264            return None;
265        }
266        bits = n;
267        ch = end;
268    }
269    if ch != 0 {
270        return None;
271    }
272    if bits == -1 {
273        if written != 4 {
274            return None;
275        }
276        bits = 32;
277    }
278    if written == 0 || usize::try_from(bits / 8).ok()? > written {
279        return None;
280    }
281    Some(bits)
282}
283
284/// `getbits`: a mask length from 0 to 128 with no leading zero, up to the end of the string.
285fn getbits(src: &[u8]) -> Option<i32> {
286    let mut val = 0;
287    let mut n = 0;
288    for &ch in src.iter().take_while(|ch| **ch != 0) {
289        let d = digit(ch)?;
290        if n != 0 && val == 0 {
291            return None;
292        }
293        n += 1;
294        val = val * 10 + d;
295        if val > 128 {
296            return None;
297        }
298    }
299    (n != 0).then_some(val)
300}
301
302/// `getv4`: the IPv4 part at the end of an IPv6 address, with an optional mask length. It writes up to 4 octets.
303fn getv4(src: &[u8], dst: &mut [u8], bits: &mut i32) -> bool {
304    let mut written = 0usize;
305    let mut val = 0;
306    let mut n = 0;
307    for (i, &ch) in src.iter().enumerate() {
308        if ch == 0 {
309            break;
310        }
311        if let Some(d) = digit(ch) {
312            if n != 0 && val == 0 {
313                return false;
314            }
315            n += 1;
316            val = val * 10 + d;
317            if val > 255 {
318                return false;
319            }
320            continue;
321        }
322        if ch == b'.' || ch == b'/' {
323            if written > 3 {
324                return false;
325            }
326            dst[written] = u8::try_from(val).unwrap_or(0);
327            written += 1;
328            if ch == b'/' {
329                return match getbits(&src[i + 1..]) {
330                    Some(b) => {
331                        *bits = b;
332                        true
333                    }
334                    None => false,
335                };
336            }
337            val = 0;
338            n = 0;
339            continue;
340        }
341        return false;
342    }
343    if n == 0 || written > 3 {
344        return false;
345    }
346    dst[written] = u8::try_from(val).unwrap_or(0);
347    true
348}
349
350/// `inet_cidr_pton_ipv6`: an IPv6 address of up to 8 groups of hex digits, with `::` for a run of zero groups, an optional IPv4 part at the end and an optional mask length. With no mask length, the length is 128.
351fn pton_v6(src: &[u8], dst: &mut [u8; 16]) -> Option<i32> {
352    const END: usize = 16;
353    let mut tmp = [0u8; 16];
354    let mut tp = 0usize;
355    let mut colon: Option<usize> = None;
356    let mut i = 0usize;
357    if src[0] == b':' {
358        i = 1;
359        if src[1] != b':' {
360            return None;
361        }
362    }
363    let mut curtok = i;
364    let mut saw_xdigit = false;
365    let mut val = 0u32;
366    let mut digits = 0;
367    let mut bits = -1;
368    loop {
369        let ch = src[i];
370        i += 1;
371        if ch == 0 {
372            break;
373        }
374        if let Some(n) = xdigit(ch) {
375            val = (val << 4) | n;
376            digits += 1;
377            if digits > 4 {
378                return None;
379            }
380            saw_xdigit = true;
381            continue;
382        }
383        if ch == b':' {
384            curtok = i;
385            if !saw_xdigit {
386                if colon.is_some() {
387                    return None;
388                }
389                colon = Some(tp);
390                continue;
391            } else if src[i] == 0 || tp + 2 > END {
392                return None;
393            }
394            tmp[tp..tp + 2].copy_from_slice(&u16::try_from(val).ok()?.to_be_bytes());
395            tp += 2;
396            saw_xdigit = false;
397            digits = 0;
398            val = 0;
399            continue;
400        }
401        if ch == b'.' && tp + 4 <= END && getv4(&src[curtok..], &mut tmp[tp..tp + 4], &mut bits) {
402            tp += 4;
403            saw_xdigit = false;
404            break;
405        }
406        if ch == b'/'
407            && let Some(b) = getbits(&src[i..])
408        {
409            bits = b;
410            break;
411        }
412        return None;
413    }
414    if saw_xdigit {
415        if tp + 2 > END {
416            return None;
417        }
418        tmp[tp..tp + 2].copy_from_slice(&u16::try_from(val).ok()?.to_be_bytes());
419        tp += 2;
420    }
421    if bits == -1 {
422        bits = 128;
423    }
424    if let Some(colon) = colon {
425        if tp == END {
426            return None;
427        }
428        let n = tp - colon;
429        for k in 1..=n {
430            tmp[END - k] = tmp[colon + n - k];
431            tmp[colon + n - k] = 0;
432        }
433        tp = END;
434    }
435    if tp != END {
436        return None;
437    }
438    *dst = tmp;
439    Some(bits)
440}
441
442/// `addressOK`: true when no bit to the right of the mask is set.
443fn address_ok(addr: &[u8; 16], bits: u8, family: NetFamily) -> bool {
444    if bits == family.max_bits() {
445        return true;
446    }
447    let mut byte = usize::from(bits / 8);
448    let mut mask = if bits == 0 { 0xff } else { 0xffu8 >> (bits % 8) };
449    while byte < family.size() {
450        if addr[byte] & mask != 0 {
451            return false;
452        }
453        mask = 0xff;
454        byte += 1;
455    }
456    true
457}
458
459/// `pg_inet_net_ntop`: the address, then `/bits` when `bits` is not the length of the whole address. IPv4 always has the four octets.
460pub fn net_ntop(value: &Inet, bits: u8, out: &mut String) {
461    use std::fmt::Write;
462    match value.family {
463        NetFamily::V4 => {
464            let [a, b, c, d, ..] = value.addr;
465            let _ = write!(out, "{a}.{b}.{c}.{d}");
466            if bits != 32 {
467                let _ = write!(out, "/{bits}");
468            }
469        }
470        NetFamily::V6 => {
471            ntop_v6(&value.addr, out);
472            if bits != 128 {
473                let _ = write!(out, "/{bits}");
474            }
475        }
476    }
477}
478
479/// `inet_net_ntop_ipv6` with no mask length: the groups in hex, the longest run of two or more zero groups as `::`, and the last 4 bytes as an IPv4 address when the address is a mapped or compatible IPv4 address.
480fn ntop_v6(src: &[u8; 16], out: &mut String) {
481    use std::fmt::Write;
482    let mut words = [0u32; 8];
483    for (i, byte) in src.iter().enumerate() {
484        words[i / 2] |= u32::from(*byte) << ((1 - (i % 2)) * 8);
485    }
486    // The start and the length of the longest run of zero words.
487    let mut best: Option<(usize, usize)> = None;
488    let mut cur: Option<(usize, usize)> = None;
489    for (i, word) in words.iter().enumerate() {
490        if *word == 0 {
491            cur = Some(cur.map_or((i, 1), |(base, len)| (base, len + 1)));
492        } else if let Some(run) = cur.take()
493            && best.is_none_or(|b| run.1 > b.1)
494        {
495            best = Some(run);
496        }
497    }
498    if let Some(run) = cur
499        && best.is_none_or(|b| run.1 > b.1)
500    {
501        best = Some(run);
502    }
503    if best.is_some_and(|b| b.1 < 2) {
504        best = None;
505    }
506    for (i, word) in words.iter().enumerate() {
507        if let Some((base, len)) = best
508            && i >= base
509            && i < base + len
510        {
511            if i == base {
512                out.push(':');
513            }
514            continue;
515        }
516        if i != 0 {
517            out.push(':');
518        }
519        if i == 6
520            && let Some((0, len)) = best
521            && (len == 6 || (len == 7 && words[7] != 1) || (len == 5 && words[5] == 0xffff))
522        {
523            let _ = write!(out, "{}.{}.{}.{}", src[12], src[13], src[14], src[15]);
524            break;
525        }
526        let _ = write!(out, "{word:x}");
527    }
528    if best.is_some_and(|(base, len)| base + len == 8) {
529        out.push(':');
530    }
531}
532
533/// `inet_out` and `cidr_out`: the text of `net_ntop`, and for a `cidr` the mask length when the text has none.
534pub fn inet_out(value: &Inet, cidr: bool, out: &mut Vec<u8>) {
535    let mut text = String::new();
536    net_ntop(value, value.bits, &mut text);
537    if cidr && !text.contains('/') {
538        text.push('/');
539        text.push_str(&value.bits.to_string());
540    }
541    out.extend_from_slice(text.as_bytes());
542}
543
544/// `pg_inet_cidr_ntop`, the text of `abbrev(cidr)`: only the octets or the groups of the network part, then the mask length.
545pub fn cidr_abbrev(value: &Inet, text: &mut String) {
546    use std::fmt::Write;
547    let bits = value.bits;
548    let mut out = String::new();
549    match value.family {
550        NetFamily::V4 => {
551            if bits == 0 {
552                out.push('0');
553            }
554            let whole = usize::from(bits / 8);
555            for (n, octet) in value.addr[..whole].iter().enumerate() {
556                let _ = write!(out, "{octet}");
557                if n + 1 < whole {
558                    out.push('.');
559                }
560            }
561            let b = bits % 8;
562            if b > 0 {
563                if !out.is_empty() {
564                    out.push('.');
565                }
566                let m = 0xffu8 << (8 - b);
567                let _ = write!(out, "{}", value.addr[whole] & m);
568            }
569        }
570        NetFamily::V6 => cidr_ntop_v6(&value.masked(bits).addr, bits, &mut out),
571    }
572    let _ = write!(text, "{out}/{bits}");
573}
574
575/// `inet_cidr_ntop_ipv6` with no mask length, into an empty `out`. `s` has the bits to the right of the mask set to 0.
576fn cidr_ntop_v6(s: &[u8; 16], bits: u8, out: &mut String) {
577    use std::fmt::Write;
578    if bits == 0 {
579        out.push_str("::");
580        return;
581    }
582    let words = usize::from(bits).div_ceil(16).max(2);
583    // The longest run of zero words, as the loop of PostgreSQL finds it.
584    let (mut zero_s, mut zero_l, mut tmp_s, mut tmp_l) = (0, 0, 0, 0);
585    for i in 0..words {
586        if s[2 * i] | s[2 * i + 1] == 0 {
587            if tmp_l == 0 {
588                tmp_s = i;
589            }
590            tmp_l += 1;
591        } else if tmp_l != 0 && zero_l < tmp_l {
592            zero_s = tmp_s;
593            zero_l = tmp_l;
594            tmp_l = 0;
595        }
596    }
597    if tmp_l != 0 && zero_l < tmp_l {
598        zero_s = tmp_s;
599        zero_l = tmp_l;
600    }
601    let ipv4 = zero_l != words
602        && zero_s == 0
603        && (zero_l == 6
604            || (zero_l == 5 && s[10] == 0xff && s[11] == 0xff)
605            || (zero_l == 7 && s[14] != 0 && s[15] != 1));
606    for p in 0..words {
607        if zero_l != 0 && p >= zero_s && p < zero_s + zero_l {
608            if p == zero_s {
609                out.push(':');
610            }
611            if p == words - 1 {
612                out.push(':');
613            }
614            continue;
615        }
616        if ipv4 && p > 5 {
617            out.push(if p == 6 { ':' } else { '.' });
618            let _ = write!(out, "{}", s[2 * p]);
619            if p != 7 || bits > 120 {
620                let _ = write!(out, ".{}", s[2 * p + 1]);
621            }
622        } else {
623            if !out.is_empty() {
624                out.push(':');
625            }
626            let _ = write!(out, "{:x}", u32::from(s[2 * p]) * 256 + u32::from(s[2 * p + 1]));
627        }
628    }
629}
630
631/// The error of `network_recv`.
632fn recv_error(message: String) -> TypeError {
633    TypeError::new(SqlState::INVALID_BINARY_REPRESENTATION, message)
634}
635
636/// `inet_recv` and `cidr_recv`: the family, the mask length, a flag of `cidr` that the function does not read, the length of the address and the address.
637///
638/// # Errors
639///
640/// `22P03` for a bad family, mask length or address length, and for a `cidr` value with bits set to the right of the mask.
641pub fn inet_recv(recv: &mut Recv<'_>, cidr: bool) -> Result<Inet, TypeError> {
642    let name = type_name(cidr);
643    let family = match recv.byte()? {
644        2 => NetFamily::V4,
645        3 => NetFamily::V6,
646        _ => {
647            return Err(recv_error(format!("invalid address family in external \"{name}\" value")));
648        }
649    };
650    let bits = recv.byte()?;
651    if bits > family.max_bits() {
652        return Err(recv_error(format!("invalid bits in external \"{name}\" value")));
653    }
654    recv.byte()?;
655    let size = recv.byte()?;
656    if usize::from(size) != family.size() {
657        return Err(recv_error(format!("invalid length in external \"{name}\" value")));
658    }
659    let mut addr = [0u8; 16];
660    addr[..family.size()].copy_from_slice(recv.bytes(family.size())?);
661    if cidr && !address_ok(&addr, bits, family) {
662        let mut error = recv_error("invalid external \"cidr\" value".into());
663        error.detail = Some("Value has bits set to right of mask.".into());
664        return Err(error);
665    }
666    Ok(Inet { family, bits, addr })
667}
668
669/// `inet_send` and `cidr_send`.
670pub fn inet_send(value: &Inet, cidr: bool, out: &mut Vec<u8>) {
671    let size = value.family.size();
672    out.extend_from_slice(&[value.family.code(), value.bits, u8::from(cidr)]);
673    out.push(u8::try_from(size).unwrap_or(16));
674    out.extend_from_slice(value.bytes());
675}
676
677/// `bitncmp`: the order of the first `n` bits of two addresses. PostgreSQL compares the whole bytes with `memcmp`, which gives the difference of the first bytes that differ on the reference server, and then the bits of the last byte, which give 1 or -1.
678pub fn bitncmp(l: &[u8; 16], r: &[u8; 16], n: u8) -> i32 {
679    let whole = usize::from(n / 8);
680    if let Some((a, b)) = l[..whole].iter().zip(&r[..whole]).find(|(a, b)| a != b) {
681        return i32::from(*a) - i32::from(*b);
682    }
683    if n.is_multiple_of(8) {
684        return 0;
685    }
686    let mask = 0xffu8 << (8 - n % 8);
687    match (l[whole] & mask).cmp(&(r[whole] & mask)) {
688        Ordering::Less => -1,
689        Ordering::Equal => 0,
690        Ordering::Greater => 1,
691    }
692}
693
694/// `bitncommon`: the number of leading bits, up to `n`, that two addresses have in common.
695pub fn bitncommon(l: &[u8; 16], r: &[u8; 16], n: u8) -> u8 {
696    let mut nbits = n % 8;
697    let mut byte = 0usize;
698    while byte < usize::from(n / 8) {
699        if l[byte] != r[byte] {
700            nbits = 7;
701            break;
702        }
703        byte += 1;
704    }
705    if nbits != 0 {
706        let diff = u32::from(l[byte] ^ r[byte]);
707        while diff >> (8 - nbits) != 0 {
708            nbits -= 1;
709        }
710    }
711    u8::try_from(byte * 8).unwrap_or(128) + nbits
712}
713
714/// `network_cmp_internal`, the result of `network_cmp`: the common bits of the network parts, then the mask lengths, then the whole addresses. IPv4 comes before IPv6. Only the sign of the result is the order.
715pub fn network_cmp(a: &Inet, b: &Inet) -> i32 {
716    if a.family != b.family {
717        return i32::from(a.family.code()) - i32::from(b.family.code());
718    }
719    let order = bitncmp(&a.addr, &b.addr, a.bits.min(b.bits));
720    if order != 0 {
721        return order;
722    }
723    let order = i32::from(a.bits) - i32::from(b.bits);
724    if order != 0 {
725        return order;
726    }
727    bitncmp(&a.addr, &b.addr, a.family.max_bits())
728}
729
730#[cfg(test)]
731mod tests {
732    use super::*;
733
734    fn text(input: &str, cidr: bool) -> String {
735        let mut out = Vec::new();
736        inet_out(&inet_in(input, cidr).unwrap(), cidr, &mut out);
737        String::from_utf8(out).unwrap()
738    }
739
740    #[test]
741    fn inet_keeps_the_host_bits_and_omits_a_full_mask() {
742        assert_eq!(text("192.168.1.5", false), "192.168.1.5");
743        assert_eq!(text("192.168.1.5/24", false), "192.168.1.5/24");
744        assert_eq!(text("::ffff:1.2.3.4", false), "::ffff:1.2.3.4");
745        assert_eq!(text("2001:db8:0:0:1:0:0:1", false), "2001:db8::1:0:0:1");
746        assert_eq!(text("::1", false), "::1");
747        assert_eq!(text("1::/64", false), "1::/64");
748    }
749
750    #[test]
751    fn cidr_finds_the_mask_from_the_class() {
752        assert_eq!(text("10", true), "10.0.0.0/8");
753        assert_eq!(text("172.16", true), "172.16.0.0/16");
754        assert_eq!(text("192.168.1", true), "192.168.1.0/24");
755        assert_eq!(text("224", true), "224.0.0.0/4");
756        assert_eq!(text("224.0.0.0", true), "224.0.0.0/32");
757        assert_eq!(text("0x0a", true), "10.0.0.0/8");
758    }
759
760    #[test]
761    fn bad_values_have_the_errors_of_postgres() {
762        assert_eq!(
763            inet_in("1.2.3", false).unwrap_err().message,
764            "invalid input syntax for type inet: \"1.2.3\""
765        );
766        assert_eq!(
767            inet_in("1.2.3.4/33", false).unwrap_err().message,
768            "invalid input syntax for type inet: \"1.2.3.4/33\""
769        );
770        let error = inet_in("192.168.1.5/24", true).unwrap_err();
771        assert_eq!(error.message, "invalid cidr value: \"192.168.1.5/24\"");
772        assert_eq!(error.detail.as_deref(), Some("Value has bits set to right of mask."));
773    }
774
775    #[test]
776    fn abbrev_of_cidr_drops_the_zero_octets() {
777        let mut out = String::new();
778        cidr_abbrev(&inet_in("10.1.0.0/16", true).unwrap(), &mut out);
779        assert_eq!(out, "10.1/16");
780    }
781
782    #[test]
783    fn the_binary_form_reads_back() {
784        let value = inet_in("2001:db8::/32", true).unwrap();
785        let mut out = Vec::new();
786        inet_send(&value, true, &mut out);
787        assert_eq!(&out[..4], &[3, 32, 1, 16]);
788        let mut recv = Recv::new(&out);
789        assert_eq!(inet_recv(&mut recv, true), Ok(value));
790    }
791
792    #[test]
793    fn the_network_part_sorts_first() {
794        let a = inet_in("10.0.0.0/8", false).unwrap();
795        let b = inet_in("10.0.0.1/32", false).unwrap();
796        let c = inet_in("::1", false).unwrap();
797        assert_eq!(network_cmp(&a, &b), -24);
798        assert_eq!(network_cmp(&b, &c), -1);
799        let d = inet_in("10.0.0.9", false).unwrap();
800        assert_eq!(network_cmp(&d, &b), 8);
801        assert_eq!(bitncommon(&a.addr, &b.addr, 8), 8);
802    }
803}