punycode.rs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright 2013 The rust-url developers.
  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::char;
  15. use std::u32;
  16. // Bootstring parameters for Punycode
  17. static BASE: u32 = 36;
  18. static T_MIN: u32 = 1;
  19. static T_MAX: u32 = 26;
  20. static SKEW: u32 = 38;
  21. static DAMP: u32 = 700;
  22. static INITIAL_BIAS: u32 = 72;
  23. static INITIAL_N: u32 = 0x80;
  24. static DELIMITER: char = '-';
  25. #[inline]
  26. fn adapt(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
  27. delta /= if first_time { DAMP } else { 2 };
  28. delta += delta / num_points;
  29. let mut k = 0;
  30. while delta > ((BASE - T_MIN) * T_MAX) / 2 {
  31. delta /= BASE - T_MIN;
  32. k += BASE;
  33. }
  34. k + (((BASE - T_MIN + 1) * delta) / (delta + SKEW))
  35. }
  36. /// Convert Punycode to an Unicode `String`.
  37. ///
  38. /// This is a convenience wrapper around `decode`.
  39. #[inline]
  40. pub fn decode_to_string(input: &str) -> Option<String> {
  41. decode(input).map(|chars| chars.into_iter().collect())
  42. }
  43. /// Convert Punycode to Unicode.
  44. ///
  45. /// Return None on malformed input or overflow.
  46. /// Overflow can only happen on inputs that take more than
  47. /// 63 encoded bytes, the DNS limit on domain name labels.
  48. pub fn decode(input: &str) -> Option<Vec<char>> {
  49. // Handle "basic" (ASCII) code points.
  50. // They are encoded as-is before the last delimiter, if any.
  51. let (mut output, input) = match input.rfind(DELIMITER) {
  52. None => (Vec::new(), input),
  53. Some(position) => (
  54. input[..position].chars().collect(),
  55. if position > 0 {
  56. &input[position + 1..]
  57. } else {
  58. input
  59. },
  60. ),
  61. };
  62. let mut code_point = INITIAL_N;
  63. let mut bias = INITIAL_BIAS;
  64. let mut i = 0;
  65. let mut iter = input.bytes();
  66. loop {
  67. let previous_i = i;
  68. let mut weight = 1;
  69. let mut k = BASE;
  70. let mut byte = match iter.next() {
  71. None => break,
  72. Some(byte) => byte,
  73. };
  74. // Decode a generalized variable-length integer into delta,
  75. // which gets added to i.
  76. loop {
  77. let digit = match byte {
  78. byte @ b'0'..=b'9' => byte - b'0' + 26,
  79. byte @ b'A'..=b'Z' => byte - b'A',
  80. byte @ b'a'..=b'z' => byte - b'a',
  81. _ => return None,
  82. } as u32;
  83. if digit > (u32::MAX - i) / weight {
  84. return None; // Overflow
  85. }
  86. i += digit * weight;
  87. let t = if k <= bias {
  88. T_MIN
  89. } else if k >= bias + T_MAX {
  90. T_MAX
  91. } else {
  92. k - bias
  93. };
  94. if digit < t {
  95. break;
  96. }
  97. if weight > u32::MAX / (BASE - t) {
  98. return None; // Overflow
  99. }
  100. weight *= BASE - t;
  101. k += BASE;
  102. byte = match iter.next() {
  103. None => return None, // End of input before the end of this delta
  104. Some(byte) => byte,
  105. };
  106. }
  107. let length = output.len() as u32;
  108. bias = adapt(i - previous_i, length + 1, previous_i == 0);
  109. if i / (length + 1) > u32::MAX - code_point {
  110. return None; // Overflow
  111. }
  112. // i was supposed to wrap around from length+1 to 0,
  113. // incrementing code_point each time.
  114. code_point += i / (length + 1);
  115. i %= length + 1;
  116. let c = match char::from_u32(code_point) {
  117. Some(c) => c,
  118. None => return None,
  119. };
  120. output.insert(i as usize, c);
  121. i += 1;
  122. }
  123. Some(output)
  124. }
  125. /// Convert an Unicode `str` to Punycode.
  126. ///
  127. /// This is a convenience wrapper around `encode`.
  128. #[inline]
  129. pub fn encode_str(input: &str) -> Option<String> {
  130. encode(&input.chars().collect::<Vec<char>>())
  131. }
  132. /// Convert Unicode to Punycode.
  133. ///
  134. /// Return None on overflow, which can only happen on inputs that would take more than
  135. /// 63 encoded bytes, the DNS limit on domain name labels.
  136. pub fn encode(input: &[char]) -> Option<String> {
  137. // Handle "basic" (ASCII) code points. They are encoded as-is.
  138. let output_bytes = input
  139. .iter()
  140. .filter_map(|&c| if c.is_ascii() { Some(c as u8) } else { None })
  141. .collect();
  142. let mut output = unsafe { String::from_utf8_unchecked(output_bytes) };
  143. let basic_length = output.len() as u32;
  144. if basic_length > 0 {
  145. output.push_str("-")
  146. }
  147. let mut code_point = INITIAL_N;
  148. let mut delta = 0;
  149. let mut bias = INITIAL_BIAS;
  150. let mut processed = basic_length;
  151. let input_length = input.len() as u32;
  152. while processed < input_length {
  153. // All code points < code_point have been handled already.
  154. // Find the next larger one.
  155. let min_code_point = input
  156. .iter()
  157. .map(|&c| c as u32)
  158. .filter(|&c| c >= code_point)
  159. .min()
  160. .unwrap();
  161. if min_code_point - code_point > (u32::MAX - delta) / (processed + 1) {
  162. return None; // Overflow
  163. }
  164. // Increase delta to advance the decoder’s <code_point,i> state to <min_code_point,0>
  165. delta += (min_code_point - code_point) * (processed + 1);
  166. code_point = min_code_point;
  167. for &c in input {
  168. let c = c as u32;
  169. if c < code_point {
  170. delta += 1;
  171. if delta == 0 {
  172. return None; // Overflow
  173. }
  174. }
  175. if c == code_point {
  176. // Represent delta as a generalized variable-length integer:
  177. let mut q = delta;
  178. let mut k = BASE;
  179. loop {
  180. let t = if k <= bias {
  181. T_MIN
  182. } else if k >= bias + T_MAX {
  183. T_MAX
  184. } else {
  185. k - bias
  186. };
  187. if q < t {
  188. break;
  189. }
  190. let value = t + ((q - t) % (BASE - t));
  191. output.push(value_to_digit(value));
  192. q = (q - t) / (BASE - t);
  193. k += BASE;
  194. }
  195. output.push(value_to_digit(q));
  196. bias = adapt(delta, processed + 1, processed == basic_length);
  197. delta = 0;
  198. processed += 1;
  199. }
  200. }
  201. delta += 1;
  202. code_point += 1;
  203. }
  204. Some(output)
  205. }
  206. #[inline]
  207. fn value_to_digit(value: u32) -> char {
  208. match value {
  209. 0..=25 => (value as u8 + 'a' as u8) as char, // a..z
  210. 26..=35 => (value as u8 - 26 + '0' as u8) as char, // 0..9
  211. _ => panic!(),
  212. }
  213. }