punycode.rs 8.1 KB

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