&Vec<u8> to u128

Hi,

I need to convert a &Vec<u8> to u128. The use case is converting IPs (v4 or v6) that are encoded as a byte vector of length 4 or 16 respectively.

I came up with the following solution, but I guess its a bad one as I may loose portability to systems with a different endianness. Is there a safer solution?

    pub(crate) fn ip_to_u128(input: &Vec<u8>) -> u128 {
        let mut result: u128 = 0;
        for b in input {
            result = (result << 8) | *b as u128;
        }
        result
    }
1 Like

IpAddr has From implementations for both [u8; 16] and [u8; 4].

Convert your Vec to an array (either of 4 or 16 bytes):

let array: [u8; 16] = slice.try_into().unwrap();

And convert to IpAddr:

let ip = IpAddr::from(array);
2 Likes

Thank you, but I am not flexible regarding the output type, it needs to u128.

1 Like

How do you differentiate v4 and v6 addresses then ? Only IPv6 addresses are 128 bits.

Any IPv4 address can be encoded in an IPv6 address: first 10 bytes are 0x00 followed by two bytes 0xFFFF. The remaining four bytes are the IPv4 address. There is a standard on this but admittedly I would have to look up the RFC. I'm omitting the 0xFFFF encoding above, but it is trivial to add once you have the u128.

(BTW this is how dual stack works)

Well, just pad the array with that then. And then choose your endianess and use one of the method for u128.

1 Like

Looks like you want this: fn from_le_bytes(bytes: [u8; 16]) -> u128 from the standard library u128 - Rust

11 Likes

Sample usage:

fn ip_to_u128(input: &[u8]) -> u128 {
    // If the input is always exactly the right length you can remove the next line.
    let input = &input[..16];
    let ip_bytes: &[u8; 16] = input.try_into().unwrap();

    u128::from_le_bytes(*ip_bytes)
}
3 Likes

Great solution, but what if input has len 4 for IPv4 and len 16 for IPv6? In this case the code fails and I am not sure how to pad input without modifying it.

If it were me, Iā€™d construct an IpV6Addr first:

  • Using from([u8;16]) for native v6 addresses
  • Via IpV4Addr::to_ipv6_mapped for v4 addresses

Then, use Ipv6Addr::octets to get a [u8;16] that you can convert to a u128 as @semicoleon shows above.

4 Likes

Maybe this does what you want

fn ip_to_u128(input: &[u8]) -> Option<u128> {
    let ipv6 = if let Ok(sixteen_bytes) = <[u8; 16]>::try_from(input) {
        Ipv6Addr::from(sixteen_bytes)
    } else if let Ok(four_bytes) = <[u8; 4]>::try_from(input) {
        Ipv4Addr::from(four_bytes).to_ipv6_mapped()
    } else {
        return None;
    };
    Some(u128::from(ipv6))
}
3 Likes