punycode.rs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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. //! Punycode ([RFC 3492](http://tools.ietf.org/html/rfc3492)) implementation.
  9. //!
  10. //! Since Punycode fundamentally works on unicode code points,
  11. //! `encode` and `decode` take and return slices and vectors of `char`.
  12. //! `encode_str` and `decode_to_string` provide convenience wrappers
  13. //! that convert from and to Rust’s UTF-8 based `str` and `String` types.
  14. use std::u32;
  15. use std::char;
  16. use std::ascii::AsciiExt;
  17. // Bootstring parameters for Punycode
  18. static BASE: u32 = 36;
  19. static T_MIN: u32 = 1;
  20. static T_MAX: u32 = 26;
  21. static SKEW: u32 = 38;
  22. static DAMP: u32 = 700;
  23. static INITIAL_BIAS: u32 = 72;
  24. static INITIAL_N: u32 = 0x80;
  25. static DELIMITER: char = '-';
  26. #[inline]
  27. fn adapt(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
  28. delta /= if first_time { DAMP } else { 2 };
  29. delta += delta / num_points;
  30. let mut k = 0;
  31. while delta > ((BASE - T_MIN) * T_MAX) / 2 {
  32. delta /= BASE - T_MIN;
  33. k += BASE;
  34. }
  35. k + (((BASE - T_MIN + 1) * delta) / (delta + SKEW))
  36. }
  37. /// Convert Punycode to an Unicode `String`.
  38. ///
  39. /// This is a convenience wrapper around `decode`.
  40. #[inline]
  41. pub fn decode_to_string(input: &str) -> Option<String> {
  42. decode(input).map(|chars| chars.into_iter().collect())
  43. }
  44. /// Convert Punycode to Unicode.
  45. ///
  46. /// Return None on malformed input or overflow.
  47. /// Overflow can only happen on inputs that take more than
  48. /// 63 encoded bytes, the DNS limit on domain name labels.
  49. pub fn decode(input: &str) -> Option<Vec<char>> {
  50. // Handle "basic" (ASCII) code points.
  51. // They are encoded as-is befor the last delimiter, if any.
  52. let (mut output, input) = match input.rfind(DELIMITER) {
  53. None => (Vec::new(), input),
  54. Some(position) => (
  55. input[..position].chars().collect(),
  56. if position > 0 { &input[position + 1..] } else { input }
  57. )
  58. };
  59. let mut code_point = INITIAL_N;
  60. let mut bias = INITIAL_BIAS;
  61. let mut i = 0;
  62. let mut iter = input.bytes();
  63. loop {
  64. let previous_i = i;
  65. let mut weight = 1;
  66. let mut k = BASE;
  67. let mut byte = match iter.next() {
  68. None => break,
  69. Some(byte) => byte,
  70. };
  71. // Decode a generalized variable-length integer into delta,
  72. // which gets added to i.
  73. loop {
  74. let digit = match byte {
  75. byte @ b'0' ... b'9' => byte - b'0' + 26,
  76. byte @ b'A' ... b'Z' => byte - b'A',
  77. byte @ b'a' ... b'z' => byte - b'a',
  78. _ => return None
  79. } as u32;
  80. if digit > (u32::MAX - i) / weight {
  81. return None // Overflow
  82. }
  83. i += digit * weight;
  84. let t = if k <= bias { T_MIN }
  85. else if k >= bias + T_MAX { T_MAX }
  86. else { k - bias };
  87. if digit < t {
  88. break
  89. }
  90. if weight > u32::MAX / (BASE - t) {
  91. return None // Overflow
  92. }
  93. weight *= BASE - t;
  94. k += BASE;
  95. byte = match iter.next() {
  96. None => return None, // End of input before the end of this delta
  97. Some(byte) => byte,
  98. };
  99. }
  100. let length = output.len() as u32;
  101. bias = adapt(i - previous_i, length + 1, previous_i == 0);
  102. if i / (length + 1) > u32::MAX - code_point {
  103. return None // Overflow
  104. }
  105. // i was supposed to wrap around from length+1 to 0,
  106. // incrementing code_point each time.
  107. code_point += i / (length + 1);
  108. i %= length + 1;
  109. let c = match char::from_u32(code_point) {
  110. Some(c) => c,
  111. None => return None
  112. };
  113. output.insert(i as usize, c);
  114. i += 1;
  115. }
  116. Some(output)
  117. }
  118. /// Convert an Unicode `str` to Punycode.
  119. ///
  120. /// This is a convenience wrapper around `encode`.
  121. #[inline]
  122. pub fn encode_str(input: &str) -> Option<String> {
  123. encode(input.chars().collect::<Vec<char>>().as_slice())
  124. }
  125. /// Convert Unicode to Punycode.
  126. ///
  127. /// Return None on overflow, which can only happen on inputs that would take more than
  128. /// 63 encoded bytes, the DNS limit on domain name labels.
  129. pub fn encode(input: &[char]) -> Option<String> {
  130. // Handle "basic" (ASCII) code points. They are encoded as-is.
  131. let output_bytes = input.iter().filter_map(|&c|
  132. if c.is_ascii() { Some(c as u8) } else { None }
  133. ).collect();
  134. let mut output = unsafe { String::from_utf8_unchecked(output_bytes) };
  135. let basic_length = output.len() as u32;
  136. if basic_length > 0 {
  137. output.push_str("-")
  138. }
  139. let mut code_point = INITIAL_N;
  140. let mut delta = 0;
  141. let mut bias = INITIAL_BIAS;
  142. let mut processed = basic_length;
  143. let input_length = input.len() as u32;
  144. while processed < input_length {
  145. // All code points < code_point have been handled already.
  146. // Find the next larger one.
  147. let min_code_point = input.iter().map(|&c| c as u32)
  148. .filter(|&c| c >= code_point).min().unwrap();
  149. if min_code_point - code_point > (u32::MAX - delta) / (processed + 1) {
  150. return None // Overflow
  151. }
  152. // Increase delta to advance the decoder’s <code_point,i> state to <min_code_point,0>
  153. delta += (min_code_point - code_point) * (processed + 1);
  154. code_point = min_code_point;
  155. for &c in input.iter() {
  156. let c = c as u32;
  157. if c < code_point {
  158. delta += 1;
  159. if delta == 0 {
  160. return None // Overflow
  161. }
  162. }
  163. if c == code_point {
  164. // Represent delta as a generalized variable-length integer:
  165. let mut q = delta;
  166. let mut k = BASE;
  167. loop {
  168. let t = if k <= bias { T_MIN }
  169. else if k >= bias + T_MAX { T_MAX }
  170. else { k - bias };
  171. if q < t {
  172. break
  173. }
  174. let value = t + ((q - t) % (BASE - t));
  175. value_to_digit(value, &mut output);
  176. q = (q - t) / (BASE - t);
  177. k += BASE;
  178. }
  179. value_to_digit(q, &mut output);
  180. bias = adapt(delta, processed + 1, processed == basic_length);
  181. delta = 0;
  182. processed += 1;
  183. }
  184. }
  185. delta += 1;
  186. code_point += 1;
  187. }
  188. Some(output)
  189. }
  190. #[inline]
  191. fn value_to_digit(value: u32, output: &mut String) {
  192. let code_point = match value {
  193. 0 ... 25 => value + 0x61, // a..z
  194. 26 ... 35 => value - 26 + 0x30, // 0..9
  195. _ => panic!()
  196. };
  197. unsafe { output.as_mut_vec().push(code_point as u8) }
  198. }
  199. #[cfg(test)]
  200. mod tests {
  201. use super::{decode, encode_str};
  202. use rustc_serialize::json::{Json, Object};
  203. fn one_test(description: &str, decoded: &str, encoded: &str) {
  204. match decode(encoded) {
  205. None => panic!("Decoding {} failed.", encoded),
  206. Some(result) => {
  207. let result = result.into_iter().collect::<String>();
  208. assert!(result.as_slice() == decoded,
  209. format!("Incorrect decoding of {}:\n {}\n!= {}\n{}",
  210. encoded, result.as_slice(), decoded, description))
  211. }
  212. }
  213. match encode_str(decoded) {
  214. None => panic!("Encoding {} failed.", decoded),
  215. Some(result) => {
  216. assert!(result.as_slice() == encoded,
  217. format!("Incorrect encoding of {}:\n {}\n!= {}\n{}",
  218. decoded, result.as_slice(), encoded, description))
  219. }
  220. }
  221. }
  222. fn get_string<'a>(map: &'a Object, key: &str) -> &'a str {
  223. match map.get(&key.to_string()) {
  224. Some(&Json::String(ref s)) => s.as_slice(),
  225. None => "",
  226. _ => panic!(),
  227. }
  228. }
  229. #[test]
  230. fn test_punycode() {
  231. match Json::from_str(include_str!("punycode_tests.json")) {
  232. Ok(Json::Array(tests)) => for test in tests.iter() {
  233. match test {
  234. &Json::Object(ref o) => one_test(
  235. get_string(o, "description"),
  236. get_string(o, "decoded"),
  237. get_string(o, "encoded")
  238. ),
  239. _ => panic!(),
  240. }
  241. },
  242. other => panic!("{:?}", other)
  243. }
  244. }
  245. }