punycode.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // Copyright 2013 Simon Sapin.
  2. //
  3. // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
  4. // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
  5. // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
  6. // option. This file may not be copied, modified, or distributed
  7. // except according to those terms.
  8. use std::u32;
  9. use std::char;
  10. use std::ascii::Ascii;
  11. static BASE: u32 = 36;
  12. static T_MIN: u32 = 1;
  13. static T_MAX: u32 = 26;
  14. static SKEW: u32 = 38;
  15. static DAMP: u32 = 700;
  16. static INITIAL_BIAS: u32 = 72;
  17. static INITIAL_N: u32 = 0x80;
  18. static DELIMITER: char = '-';
  19. #[inline]
  20. fn adapt(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
  21. delta /= if first_time { DAMP } else { 2 };
  22. delta += delta / num_points;
  23. let mut k = 0;
  24. while delta > ((BASE - T_MIN) * T_MAX) / 2 {
  25. delta /= BASE - T_MIN;
  26. k += BASE;
  27. }
  28. k + (((BASE - T_MIN + 1) * delta) / (delta + SKEW))
  29. }
  30. pub fn decode(input: &[Ascii]) -> Option<~[char]> {
  31. let (mut output, input) = match input.rposition_elem(&DELIMITER.to_ascii()) {
  32. None => (~[], input),
  33. Some(position) => (
  34. input.slice_to(position).map(|a| a.to_char()),
  35. if position > 0 { input.slice_from(position + 1) } else { input }
  36. )
  37. };
  38. let mut n = INITIAL_N;
  39. let mut bias = INITIAL_BIAS;
  40. let mut i = 0;
  41. let mut iter = input.iter();
  42. loop {
  43. let previous_i = i;
  44. let mut weight = 1;
  45. let mut k = BASE;
  46. let mut ascii = match iter.next() {
  47. None => break,
  48. Some(ascii) => ascii,
  49. };
  50. loop {
  51. let digit = match ascii.to_byte() {
  52. byte @ 0x30 .. 0x39 => byte - 0x30 + 26, // 0..9
  53. byte @ 0x41 .. 0x5A => byte - 0x41, // A..Z
  54. byte @ 0x61 .. 0x7A => byte - 0x61, // a..z
  55. _ => return None
  56. } as u32;
  57. if digit > (u32::max_value - i) / weight {
  58. return None // Overflow
  59. }
  60. i += digit * weight;
  61. let t = if k <= bias { T_MIN }
  62. else if k >= bias + T_MAX { T_MAX }
  63. else { k - bias };
  64. if digit < t {
  65. break
  66. }
  67. if weight > u32::max_value / (BASE - t) {
  68. return None // Overflow
  69. }
  70. weight *= BASE - t;
  71. k += BASE;
  72. ascii = match iter.next() {
  73. None => return None, // End of input before the end of this delta
  74. Some(ascii) => ascii,
  75. };
  76. }
  77. let length = output.len() as u32;
  78. bias = adapt(i - previous_i, length + 1, previous_i == 0);
  79. if i / (length + 1) > u32::max_value - n {
  80. return None // Overflow
  81. }
  82. n += i / (length + 1);
  83. i %= length + 1;
  84. let c = match char::from_u32(n) {
  85. Some(c) => c,
  86. None => return None
  87. };
  88. output.insert(i as uint, c);
  89. i += 1;
  90. }
  91. Some(output)
  92. }
  93. pub fn encode(input: &[char]) -> Option<~[Ascii]> {
  94. let mut output = ~[];
  95. for &c in input.iter() {
  96. if c.is_ascii() {
  97. output.push(unsafe { c.to_ascii_nocheck() })
  98. }
  99. }
  100. let b = output.len() as u32;
  101. if b > 0 {
  102. output.push('-'.to_ascii())
  103. }
  104. let mut n = INITIAL_N;
  105. let mut delta = 0;
  106. let mut bias = INITIAL_BIAS;
  107. let mut h = b;
  108. let input_length = input.len() as u32;
  109. while h < input_length {
  110. let m = input.iter().map(|&c| c as u32).filter(|&c| c >= n).min().unwrap();
  111. if m - n > (u32::max_value - delta) / (h + 1) {
  112. return None // Overflow
  113. }
  114. delta += (m - n) * (h + 1);
  115. n = m;
  116. for &c in input.iter() {
  117. let c = c as u32;
  118. if c < n {
  119. delta += 1;
  120. if delta == 0 {
  121. return None // Overflow
  122. }
  123. }
  124. if c == n {
  125. let mut q = delta;
  126. let mut k = BASE;
  127. loop {
  128. let t = if k <= bias { T_MIN }
  129. else if k >= bias + T_MAX { T_MAX }
  130. else { k - bias };
  131. if q < t {
  132. break
  133. }
  134. let value = t + ((q - t) % (BASE - t));
  135. output.push(value_to_digit(value));
  136. q = (q - t) / (BASE - t);
  137. k += BASE;
  138. }
  139. output.push(value_to_digit(q));
  140. bias = adapt(delta, h + 1, h == b);
  141. delta = 0;
  142. h += 1;
  143. }
  144. }
  145. delta += 1;
  146. n += 1;
  147. }
  148. Some(output)
  149. }
  150. fn value_to_digit(value: u32) -> Ascii {
  151. let code_point = match value {
  152. 0 .. 25 => value + 0x61, // a..z
  153. 26 .. 35 => value - 26 + 0x30, // 0..9
  154. _ => fail!()
  155. };
  156. unsafe { (code_point as u8).to_ascii_nocheck() }
  157. }
  158. #[cfg(test)]
  159. mod tests {
  160. use super::{decode, encode};
  161. use std::ascii::AsciiCast;
  162. use std::str::from_chars;
  163. use extra::json::{from_str, List, Object, String};
  164. fn one_test(description: &str, decoded: &str, encoded: &str) {
  165. match decode(encoded.to_ascii()) {
  166. None => fail!("Decoding {:?} failed.", encoded),
  167. Some(result) => {
  168. let result = from_chars(result);
  169. assert!(result.as_slice() == decoded,
  170. format!("Incorrect decoding of {:?}:\n {:?}\n!= {:?}\n{}",
  171. encoded, result.as_slice(), decoded, description))
  172. }
  173. }
  174. match encode(decoded.iter().to_owned_vec()) {
  175. None => fail!("Encoding {:?} failed.", decoded),
  176. Some(result) => {
  177. let result = result.to_str_ascii();
  178. assert!(result.as_slice() == encoded,
  179. format!("Incorrect encoding of {:?}:\n {:?}\n!= {:?}\n{}",
  180. decoded, result.as_slice(), encoded, description))
  181. }
  182. }
  183. }
  184. fn get_string<'a>(map: &'a ~Object, key: &~str) -> &'a str {
  185. match map.find(key) {
  186. Some(&String(ref s)) => s.as_slice(),
  187. None => "",
  188. _ => fail!(),
  189. }
  190. }
  191. #[test]
  192. fn test_punycode() {
  193. match from_str(include_str!("punycode_tests.json")) {
  194. Ok(List(tests)) => for test in tests.iter() {
  195. match test {
  196. &Object(ref o) => one_test(
  197. get_string(o, &~"description"),
  198. get_string(o, &~"decoded"),
  199. get_string(o, &~"encoded")
  200. ),
  201. _ => fail!(),
  202. }
  203. },
  204. other => fail!("{:?}", other)
  205. }
  206. }
  207. }