hex.rs 5.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Hex binary-to-text encoding
use std::str;
use std::vec;

/// A trait for converting a value to hexadecimal encoding
pub trait ToHex {
    /// Converts the value of `self` to a hex value, returning the owned
    /// string.
    fn to_hex(&self) -> ~str;
}

22
static CHARS: &'static[u8] = bytes!("0123456789abcdef");
23 24 25 26 27 28 29

impl<'self> ToHex for &'self [u8] {
    /**
     * Turn a vector of `u8` bytes into a hexadecimal string.
     *
     * # Example
     *
30
     * ```rust
31 32 33 34 35
     * extern mod extra;
     * use extra::hex::ToHex;
     *
     * fn main () {
     *     let str = [52,32].to_hex();
36
     *     println!("{}", str);
37
     * }
38
     * ```
39 40
     */
    fn to_hex(&self) -> ~str {
41
        let mut v = vec::with_capacity(self.len() * 2);
42
        for &byte in self.iter() {
43 44
            v.push(CHARS[byte >> 4]);
            v.push(CHARS[byte & 0xf]);
45 46
        }

47
        unsafe {
48
            str::raw::from_utf8_owned(v)
49
        }
50 51 52 53 54
    }
}

/// A trait for converting hexadecimal encoded values
pub trait FromHex {
S
Steven Fackler 已提交
55 56
    /// Converts the value of `self`, interpreted as hexadecimal encoded data,
    /// into an owned vector of bytes, returning the vector.
57 58 59
    fn from_hex(&self) -> Result<~[u8], ~str>;
}

60
impl<'self> FromHex for &'self str {
61
    /**
62 63 64
     * Convert any hexadecimal encoded string (literal, `@`, `&`, or `~`)
     * to the byte values it encodes.
     *
65
     * You can use the `from_utf8` function in `std::str`
66 67
     * to turn a `[u8]` into a string with characters corresponding to those
     * values.
68 69 70
     *
     * # Example
     *
71 72
     * This converts a string literal to hexadecimal and back.
     *
73
     * ```rust
74
     * extern mod extra;
75 76
     * use extra::hex::{FromHex, ToHex};
     * use std::str;
77 78
     *
     * fn main () {
79
     *     let hello_str = "Hello, World".to_hex();
80
     *     println!("{}", hello_str);
S
Steven Fackler 已提交
81
     *     let bytes = hello_str.from_hex().unwrap();
82
     *     println!("{:?}", bytes);
83
     *     let result_str = str::from_utf8(bytes);
84
     *     println!("{}", result_str);
85
     * }
86
     * ```
87 88 89 90 91 92 93
     */
    fn from_hex(&self) -> Result<~[u8], ~str> {
        // This may be an overestimate if there is any whitespace
        let mut b = vec::with_capacity(self.len() / 2);
        let mut modulus = 0;
        let mut buf = 0u8;

94
        for (idx, byte) in self.byte_iter().enumerate() {
95 96 97 98 99 100
            buf <<= 4;

            match byte as char {
                'A'..'F' => buf |= byte - ('A' as u8) + 10,
                'a'..'f' => buf |= byte - ('a' as u8) + 10,
                '0'..'9' => buf |= byte - ('0' as u8),
S
Steven Fackler 已提交
101
                ' '|'\r'|'\n'|'\t' => {
102 103 104
                    buf >>= 4;
                    loop
                }
105 106
                _ => return Err(fmt!("Invalid character '%c' at position %u",
                                     self.char_at(idx), idx))
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
            }

            modulus += 1;
            if modulus == 2 {
                modulus = 0;
                b.push(buf);
            }
        }

        match modulus {
            0 => Ok(b),
            _ => Err(~"Invalid input length")
        }
    }
}

#[cfg(test)]
mod tests {
    use test::BenchHarness;
    use hex::*;

    #[test]
    pub fn test_to_hex() {
130
        assert_eq!("foobar".as_bytes().to_hex(), ~"666f6f626172");
131 132 133 134
    }

    #[test]
    pub fn test_from_hex_okay() {
S
Steven Fackler 已提交
135
        assert_eq!("666f6f626172".from_hex().unwrap(),
136
                   "foobar".as_bytes().to_owned());
S
Steven Fackler 已提交
137
        assert_eq!("666F6F626172".from_hex().unwrap(),
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
                   "foobar".as_bytes().to_owned());
    }

    #[test]
    pub fn test_from_hex_odd_len() {
        assert!("666".from_hex().is_err());
        assert!("66 6".from_hex().is_err());
    }

    #[test]
    pub fn test_from_hex_invalid_char() {
        assert!("66y6".from_hex().is_err());
    }

    #[test]
    pub fn test_from_hex_ignores_whitespace() {
S
Steven Fackler 已提交
154
        assert_eq!("666f 6f6\r\n26172 ".from_hex().unwrap(),
155 156 157 158 159 160 161 162 163 164 165 166 167
                   "foobar".as_bytes().to_owned());
    }

    #[test]
    pub fn test_to_hex_all_bytes() {
        for i in range(0, 256) {
            assert_eq!([i as u8].to_hex(), fmt!("%02x", i as uint));
        }
    }

    #[test]
    pub fn test_from_hex_all_bytes() {
        for i in range(0, 256) {
S
Steven Fackler 已提交
168 169
            assert_eq!(fmt!("%02x", i as uint).from_hex().unwrap(), ~[i as u8]);
            assert_eq!(fmt!("%02X", i as uint).from_hex().unwrap(), ~[i as u8]);
170 171 172 173 174 175 176 177
        }
    }

    #[bench]
    pub fn bench_to_hex(bh: & mut BenchHarness) {
        let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
                 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
        do bh.iter {
178
            s.as_bytes().to_hex();
179 180 181 182 183 184 185 186
        }
        bh.bytes = s.len() as u64;
    }

    #[bench]
    pub fn bench_from_hex(bh: & mut BenchHarness) {
        let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
                 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
187
        let b = s.as_bytes().to_hex();
188 189 190 191 192 193
        do bh.iter {
            b.from_hex();
        }
        bh.bytes = b.len() as u64;
    }
}