encoding.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // Copyright 2013-2014 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. //! Abstraction that conditionally compiles either to rust-encoding,
  9. //! or to only support UTF-8.
  10. #[cfg(feature = "query_encoding")] extern crate encoding;
  11. use std::borrow::Cow;
  12. #[cfg(feature = "query_encoding")] use std::fmt::{self, Debug, Formatter};
  13. #[cfg(feature = "query_encoding")] use self::encoding::types::{DecoderTrap, EncoderTrap};
  14. #[cfg(feature = "query_encoding")] use self::encoding::label::encoding_from_whatwg_label;
  15. #[cfg(feature = "query_encoding")] pub use self::encoding::types::EncodingRef;
  16. #[cfg(feature = "query_encoding")]
  17. #[derive(Copy, Clone)]
  18. pub struct EncodingOverride {
  19. /// `None` means UTF-8.
  20. encoding: Option<EncodingRef>
  21. }
  22. #[cfg(feature = "query_encoding")]
  23. impl EncodingOverride {
  24. pub fn from_opt_encoding(encoding: Option<EncodingRef>) -> Self {
  25. encoding.map(Self::from_encoding).unwrap_or_else(Self::utf8)
  26. }
  27. pub fn from_encoding(encoding: EncodingRef) -> Self {
  28. EncodingOverride {
  29. encoding: if encoding.name() == "utf-8" { None } else { Some(encoding) }
  30. }
  31. }
  32. #[inline]
  33. pub fn utf8() -> Self {
  34. EncodingOverride { encoding: None }
  35. }
  36. pub fn lookup(label: &[u8]) -> Option<Self> {
  37. // Don't use String::from_utf8_lossy since no encoding label contains U+FFFD
  38. // https://encoding.spec.whatwg.org/#names-and-labels
  39. ::std::str::from_utf8(label)
  40. .ok()
  41. .and_then(encoding_from_whatwg_label)
  42. .map(Self::from_encoding)
  43. }
  44. /// https://encoding.spec.whatwg.org/#get-an-output-encoding
  45. pub fn to_output_encoding(self) -> Self {
  46. if let Some(encoding) = self.encoding {
  47. if matches!(encoding.name(), "utf-16le" | "utf-16be") {
  48. return Self::utf8()
  49. }
  50. }
  51. self
  52. }
  53. pub fn is_utf8(&self) -> bool {
  54. self.encoding.is_none()
  55. }
  56. pub fn name(&self) -> &'static str {
  57. match self.encoding {
  58. Some(encoding) => encoding.name(),
  59. None => "utf-8",
  60. }
  61. }
  62. pub fn decode<'a>(&self, input: Cow<'a, [u8]>) -> Cow<'a, str> {
  63. match self.encoding {
  64. // `encoding.decode` never returns `Err` when called with `DecoderTrap::Replace`
  65. Some(encoding) => encoding.decode(&input, DecoderTrap::Replace).unwrap().into(),
  66. None => decode_utf8_lossy(input),
  67. }
  68. }
  69. pub fn encode<'a>(&self, input: Cow<'a, str>) -> Cow<'a, [u8]> {
  70. match self.encoding {
  71. // `encoding.encode` never returns `Err` when called with `EncoderTrap::NcrEscape`
  72. Some(encoding) => Cow::Owned(encoding.encode(&input, EncoderTrap::NcrEscape).unwrap()),
  73. None => encode_utf8(input)
  74. }
  75. }
  76. }
  77. #[cfg(feature = "query_encoding")]
  78. impl Debug for EncodingOverride {
  79. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  80. write!(f, "EncodingOverride {{ encoding: ")?;
  81. match self.encoding {
  82. Some(e) => write!(f, "{} }}", e.name()),
  83. None => write!(f, "None }}")
  84. }
  85. }
  86. }
  87. #[cfg(not(feature = "query_encoding"))]
  88. #[derive(Copy, Clone, Debug)]
  89. pub struct EncodingOverride;
  90. #[cfg(not(feature = "query_encoding"))]
  91. impl EncodingOverride {
  92. #[inline]
  93. pub fn utf8() -> Self {
  94. EncodingOverride
  95. }
  96. pub fn decode<'a>(&self, input: Cow<'a, [u8]>) -> Cow<'a, str> {
  97. decode_utf8_lossy(input)
  98. }
  99. pub fn encode<'a>(&self, input: Cow<'a, str>) -> Cow<'a, [u8]> {
  100. encode_utf8(input)
  101. }
  102. }
  103. pub fn decode_utf8_lossy(input: Cow<[u8]>) -> Cow<str> {
  104. match input {
  105. Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
  106. Cow::Owned(bytes) => {
  107. let raw_utf8: *const [u8];
  108. match String::from_utf8_lossy(&bytes) {
  109. Cow::Borrowed(utf8) => raw_utf8 = utf8.as_bytes(),
  110. Cow::Owned(s) => return s.into(),
  111. }
  112. // from_utf8_lossy returned a borrow of `bytes` unchanged.
  113. debug_assert!(raw_utf8 == &*bytes as *const [u8]);
  114. // Reuse the existing `Vec` allocation.
  115. unsafe { String::from_utf8_unchecked(bytes) }.into()
  116. }
  117. }
  118. }
  119. pub fn encode_utf8(input: Cow<str>) -> Cow<[u8]> {
  120. match input {
  121. Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
  122. Cow::Owned(s) => Cow::Owned(s.into_bytes())
  123. }
  124. }