use std::u32;
use std::char;
use std::ascii::AsciiExt;
static BASE: u32 = 36;
static T_MIN: u32 = 1;
static T_MAX: u32 = 26;
static SKEW: u32 = 38;
static DAMP: u32 = 700;
static INITIAL_BIAS: u32 = 72;
static INITIAL_N: u32 = 0x80;
static DELIMITER: char = '-';
#[inline]
fn adapt(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
delta /= if first_time { DAMP } else { 2 };
delta += delta / num_points;
let mut k = 0;
while delta > ((BASE - T_MIN) * T_MAX) / 2 {
delta /= BASE - T_MIN;
k += BASE;
}
k + (((BASE - T_MIN + 1) * delta) / (delta + SKEW))
}
#[inline]
pub fn decode_to_string(input: &str) -> Option<String> {
decode(input).map(|chars| chars.into_iter().collect())
}
pub fn decode(input: &str) -> Option<Vec<char>> {
let (mut output, input) = match input.rfind(DELIMITER) {
None => (Vec::new(), input),
Some(position) => (
input[..position].chars().collect(),
if position > 0 { &input[position + 1..] } else { input }
)
};
let mut code_point = INITIAL_N;
let mut bias = INITIAL_BIAS;
let mut i = 0;
let mut iter = input.bytes();
loop {
let previous_i = i;
let mut weight = 1;
let mut k = BASE;
let mut byte = match iter.next() {
None => break,
Some(byte) => byte,
};
loop {
let digit = match byte {
byte @ b'0' ... b'9' => byte - b'0' + 26,
byte @ b'A' ... b'Z' => byte - b'A',
byte @ b'a' ... b'z' => byte - b'a',
_ => return None
} as u32;
if digit > (u32::MAX - i) / weight {
return None }
i += digit * weight;
let t = if k <= bias { T_MIN }
else if k >= bias + T_MAX { T_MAX }
else { k - bias };
if digit < t {
break
}
if weight > u32::MAX / (BASE - t) {
return None }
weight *= BASE - t;
k += BASE;
byte = match iter.next() {
None => return None, Some(byte) => byte,
};
}
let length = output.len() as u32;
bias = adapt(i - previous_i, length + 1, previous_i == 0);
if i / (length + 1) > u32::MAX - code_point {
return None }
code_point += i / (length + 1);
i %= length + 1;
let c = match char::from_u32(code_point) {
Some(c) => c,
None => return None
};
output.insert(i as usize, c);
i += 1;
}
Some(output)
}
#[inline]
pub fn encode_str(input: &str) -> Option<String> {
encode(&input.chars().collect::<Vec<char>>())
}
pub fn encode(input: &[char]) -> Option<String> {
let output_bytes = input.iter().filter_map(|&c|
if c.is_ascii() { Some(c as u8) } else { None }
).collect();
let mut output = unsafe { String::from_utf8_unchecked(output_bytes) };
let basic_length = output.len() as u32;
if basic_length > 0 {
output.push_str("-")
}
let mut code_point = INITIAL_N;
let mut delta = 0;
let mut bias = INITIAL_BIAS;
let mut processed = basic_length;
let input_length = input.len() as u32;
while processed < input_length {
let min_code_point = input.iter().map(|&c| c as u32)
.filter(|&c| c >= code_point).min().unwrap();
if min_code_point - code_point > (u32::MAX - delta) / (processed + 1) {
return None }
delta += (min_code_point - code_point) * (processed + 1);
code_point = min_code_point;
for &c in input {
let c = c as u32;
if c < code_point {
delta += 1;
if delta == 0 {
return None }
}
if c == code_point {
let mut q = delta;
let mut k = BASE;
loop {
let t = if k <= bias { T_MIN }
else if k >= bias + T_MAX { T_MAX }
else { k - bias };
if q < t {
break
}
let value = t + ((q - t) % (BASE - t));
value_to_digit(value, &mut output);
q = (q - t) / (BASE - t);
k += BASE;
}
value_to_digit(q, &mut output);
bias = adapt(delta, processed + 1, processed == basic_length);
delta = 0;
processed += 1;
}
}
delta += 1;
code_point += 1;
}
Some(output)
}
#[inline]
fn value_to_digit(value: u32, output: &mut String) {
let code_point = match value {
0 ... 25 => value + 0x61, 26 ... 35 => value - 26 + 0x30, _ => panic!()
};
unsafe { output.as_mut_vec().push(code_point as u8) }
}