diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/minifier/src/json | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/minifier/src/json')
-rw-r--r-- | vendor/minifier/src/json/json_minifier.rs | 50 | ||||
-rw-r--r-- | vendor/minifier/src/json/mod.rs | 114 | ||||
-rw-r--r-- | vendor/minifier/src/json/read/byte_to_char.rs | 132 | ||||
-rw-r--r-- | vendor/minifier/src/json/read/internal_buffer.rs | 44 | ||||
-rw-r--r-- | vendor/minifier/src/json/read/internal_reader.rs | 63 | ||||
-rw-r--r-- | vendor/minifier/src/json/read/json_read.rs | 106 | ||||
-rw-r--r-- | vendor/minifier/src/json/string.rs | 100 |
7 files changed, 609 insertions, 0 deletions
diff --git a/vendor/minifier/src/json/json_minifier.rs b/vendor/minifier/src/json/json_minifier.rs new file mode 100644 index 000000000..ad9fae2ce --- /dev/null +++ b/vendor/minifier/src/json/json_minifier.rs @@ -0,0 +1,50 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +#[derive(Debug, Clone)] +pub struct JsonMinifier { + pub is_string: bool, + pub escaped_quotation: u8, +} + +impl Default for JsonMinifier { + fn default() -> Self { + Self::new() + } +} + +impl JsonMinifier { + pub fn new() -> Self { + JsonMinifier { + is_string: false, + escaped_quotation: 0, + } + } +} + +#[inline] +pub fn keep_element(minifier: &mut JsonMinifier, item1: &char, item2: Option<&char>) -> bool { + let remove_element = + item1.is_ascii_control() || is_whitespace_outside_string(minifier, item1, item2); + !remove_element +} + +#[inline] +fn is_whitespace_outside_string( + minifier: &mut JsonMinifier, + item1: &char, + item2: Option<&char>, +) -> bool { + if !minifier.is_string && item1.eq(&'"') { + minifier.is_string = true; + } else if minifier.is_string { + if item1.eq(&'\\') && item2.eq(&Some(&'"')) { + minifier.escaped_quotation = 4; + } + if minifier.escaped_quotation > 0 { + minifier.escaped_quotation -= 1; + } else if item1.eq(&'"') { + minifier.is_string = false; + } + } + !minifier.is_string && item1.is_whitespace() +} diff --git a/vendor/minifier/src/json/mod.rs b/vendor/minifier/src/json/mod.rs new file mode 100644 index 000000000..44c8a1c92 --- /dev/null +++ b/vendor/minifier/src/json/mod.rs @@ -0,0 +1,114 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::json::{ + json_minifier::{keep_element, JsonMinifier}, + read::json_read::JsonRead, + string::JsonMultiFilter, +}; +use std::fmt; +use std::io::{self, Read}; + +mod read { + mod byte_to_char; + mod internal_buffer; + mod internal_reader; + pub mod json_read; +} + +mod json_minifier; +mod string; + +type JsonMethod = fn(&mut JsonMinifier, &char, Option<&char>) -> bool; + +/// Minifies a given String by JSON minification rules +/// +/// # Example +/// +/// ```rust +/// use minifier::json::minify; +/// +/// let json = r#" +/// { +/// "test": "test", +/// "test2": 2 +/// } +/// "#.into(); +/// let json_minified = minify(json); +/// assert_eq!(&json_minified.to_string(), r#"{"test":"test","test2":2}"#); +/// ``` +#[inline] +pub fn minify(json: &str) -> Minified<'_> { + Minified(JsonMultiFilter::new(json.chars(), keep_element)) +} + +#[derive(Debug)] +pub struct Minified<'a>(JsonMultiFilter<'a, JsonMethod>); + +impl<'a> Minified<'a> { + pub fn write<W: io::Write>(self, w: W) -> io::Result<()> { + self.0.write(w) + } +} + +impl<'a> fmt::Display for Minified<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// Minifies a given Read by JSON minification rules +/// +/// # Example +/// +/// ```rust +/// extern crate minifier; +/// use std::fs::File; +/// use std::io::Read; +/// use minifier::json::minify_from_read; +/// +/// fn main() { +/// let mut html_minified = String::new(); +/// let mut file = File::open("tests/files/test.json").expect("file not found"); +/// minify_from_read(file).read_to_string(&mut html_minified); +/// } +/// ``` +#[inline] +pub fn minify_from_read<R: Read>(json: R) -> JsonRead<JsonMethod, R> { + JsonRead::new(json, keep_element) +} + +#[test] +fn removal_from_read() { + use std::fs::File; + + let input = File::open("tests/files/test.json").expect("file not found"); + let expected: String = "{\"test\":\"\\\" test2\",\"test2\":\"\",\"test3\":\" \"}".into(); + let mut actual = String::new(); + minify_from_read(input) + .read_to_string(&mut actual) + .expect("error at read"); + assert_eq!(actual, expected); +} + +#[test] +fn removal_of_control_characters() { + let input = "\n".into(); + let expected: String = "".into(); + let actual = minify(input); + assert_eq!(actual.to_string(), expected); +} + +#[test] +fn removal_of_whitespace_outside_of_tags() { + let input = r#" + { + "test": "\" test2", + "test2": "", + "test3": " " + } + "# + .into(); + let expected: String = "{\"test\":\"\\\" test2\",\"test2\":\"\",\"test3\":\" \"}".into(); + let actual = minify(input); + assert_eq!(actual.to_string(), expected); +} diff --git a/vendor/minifier/src/json/read/byte_to_char.rs b/vendor/minifier/src/json/read/byte_to_char.rs new file mode 100644 index 000000000..d3618b9cb --- /dev/null +++ b/vendor/minifier/src/json/read/byte_to_char.rs @@ -0,0 +1,132 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::json::read::internal_reader::InternalReader; +use std::{ + error, fmt, + io::{Error, Read}, + str::from_utf8, +}; + +pub struct ByteToChar<R> { + iter: InternalReader<R>, +} + +impl<R: Read> ByteToChar<R> { + #[inline] + pub fn new(read: R, buffer_size: usize) -> Result<Self, Error> { + Ok(ByteToChar { + iter: InternalReader::new(read, buffer_size)?, + }) + } + + fn get_next(&mut self) -> Result<Option<u8>, CharsError> { + match self.iter.next() { + None => Ok(None), + Some(item) => match item { + Ok(item) => Ok(Some(item)), + Err(err) => Err(CharsError::Other(err)), + }, + } + } +} + +impl<R: Read + fmt::Debug> fmt::Debug for ByteToChar<R> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Filter").field("iter", &self.iter).finish() + } +} + +impl<R: Read> Iterator for ByteToChar<R> { + type Item = Result<char, CharsError>; + + fn next(&mut self) -> Option<Result<char, CharsError>> { + let first_byte = match self.get_next() { + Err(err) => return Some(Err(err)), + Ok(item) => match item { + Some(item) => item, + None => return None, + }, + }; + + let width = utf8_char_width(first_byte); + if width == 1 { + return Some(Ok(first_byte as char)); + } + if width == 0 { + return Some(Err(CharsError::NotUtf8)); + } + let mut buf = [first_byte, 0, 0, 0]; + { + let mut start = 1; + while start < width { + let byte = match self.get_next() { + Err(err) => return Some(Err(err)), + Ok(item) => match item { + Some(item) => item, + None => return Some(Err(CharsError::NotUtf8)), + }, + }; + buf[start] = byte; + start += 1; + } + } + Some(match from_utf8(&buf[..width]).ok() { + Some(s) => Ok(s.chars().next().unwrap()), + None => Err(CharsError::NotUtf8), + }) + } +} + +fn utf8_char_width(b: u8) -> usize { + UTF8_CHAR_WIDTH[b as usize] as usize +} + +// https://tools.ietf.org/html/rfc3629 +static UTF8_CHAR_WIDTH: [u8; 256] = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x1F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x3F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x5F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x7F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 0x9F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 0xBF + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, // 0xDF + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF +]; + +/// An enumeration of possible errors that can be generated from the `Chars` +/// adapter. +#[derive(Debug)] +pub enum CharsError { + /// Variant representing that the underlying stream was read successfully + /// but it did not contain valid utf8 data. + NotUtf8, + + /// Variant representing that an I/O error occurred. + Other(Error), +} + +impl error::Error for CharsError { + fn cause(&self) -> Option<&dyn error::Error> { + match *self { + CharsError::NotUtf8 => None, + CharsError::Other(ref e) => e.source(), + } + } +} + +impl fmt::Display for CharsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CharsError::NotUtf8 => "byte stream did not contain valid utf8".fmt(f), + CharsError::Other(ref e) => e.fmt(f), + } + } +} diff --git a/vendor/minifier/src/json/read/internal_buffer.rs b/vendor/minifier/src/json/read/internal_buffer.rs new file mode 100644 index 000000000..90eebcd73 --- /dev/null +++ b/vendor/minifier/src/json/read/internal_buffer.rs @@ -0,0 +1,44 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +const ARRAY_DEFAULT: u8 = 0; + +#[derive(Debug)] +pub struct Buffer { + buffer: Vec<u8>, + read_pos: usize, + buffer_size: usize, + data_size: usize, +} + +impl Buffer { + pub fn new(size: usize) -> Buffer { + Buffer { + buffer: vec![ARRAY_DEFAULT; size], + read_pos: 0, + buffer_size: size, + data_size: 0, + } + } + + pub fn as_mut(&mut self) -> &mut [u8] { + self.buffer.as_mut() + } + + pub fn update_metadata(&mut self, size: usize) { + self.read_pos = 0; + self.data_size = size; + } + + pub fn next(&mut self) -> Option<u8> { + if self.read_pos >= self.data_size { + return None; + } + let item = self.buffer.get(self.read_pos); + self.read_pos += 1; + item.copied() + } + + pub fn cont(&self) -> bool { + self.data_size == self.buffer_size + } +} diff --git a/vendor/minifier/src/json/read/internal_reader.rs b/vendor/minifier/src/json/read/internal_reader.rs new file mode 100644 index 000000000..45f178f08 --- /dev/null +++ b/vendor/minifier/src/json/read/internal_reader.rs @@ -0,0 +1,63 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use super::internal_buffer::Buffer; +use std::{ + fmt, + io::{Error, Read}, +}; + +pub struct InternalReader<R> { + read: R, + buffer_size: usize, + buffer: Buffer, +} + +impl<R: Read> InternalReader<R> { + pub fn new(mut read: R, buffer_size: usize) -> Result<Self, Error> { + let mut buffer = Buffer::new(buffer_size); + InternalReader::read_data(&mut read, &mut buffer)?; + Ok(InternalReader { + read, + buffer_size, + buffer, + }) + } + + fn read_data(read: &mut R, buffer: &mut Buffer) -> Result<(), Error> { + let size = read.read(buffer.as_mut())?; + buffer.update_metadata(size); + Ok(()) + } +} + +impl<R: Read + fmt::Debug> fmt::Debug for InternalReader<R> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("JsonReader") + .field("read", &self.read) + .field("buffer_size", &self.buffer_size) + .field("buffer", &self.buffer) + .finish() + } +} + +impl<R: Read> Iterator for InternalReader<R> { + type Item = Result<u8, Error>; + + #[inline] + fn next(&mut self) -> Option<Result<u8, Error>> { + if self.buffer_size == 0 { + return None; + } + loop { + if let Some(item) = self.buffer.next() { + return Some(Ok(item)); + } else if self.buffer.cont() { + if let Err(err) = InternalReader::read_data(&mut self.read, &mut self.buffer) { + return Some(Err(err)); + }; + } else { + return None; + } + } + } +} diff --git a/vendor/minifier/src/json/read/json_read.rs b/vendor/minifier/src/json/read/json_read.rs new file mode 100644 index 000000000..338db13c8 --- /dev/null +++ b/vendor/minifier/src/json/read/json_read.rs @@ -0,0 +1,106 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::json::{ + json_minifier::JsonMinifier, + read::byte_to_char::{ByteToChar, CharsError}, +}; +use std::{ + fmt, + io::{Error, ErrorKind, Read}, + vec::IntoIter, +}; + +pub struct JsonRead<P, R> { + minifier: JsonMinifier, + read: Option<R>, + iter: Option<ByteToChar<R>>, + predicate: P, + initialized: bool, + item_iter: Option<IntoIter<u8>>, + item1: Option<char>, +} + +impl<P, R: Read> JsonRead<P, R> { + #[inline] + pub fn new(read: R, predicate: P) -> Self { + JsonRead { + minifier: JsonMinifier::default(), + read: Some(read), + iter: None, + predicate, + initialized: false, + item_iter: None, + item1: None, + } + } + + fn get_next(&mut self) -> Result<Option<char>, CharsError> { + match self.iter.as_mut().unwrap().next() { + None => Ok(None), + Some(item) => match item { + Ok(item) => Ok(Some(item)), + Err(err) => Err(err), + }, + } + } + + fn add_char_to_buffer(&mut self, buf: &mut [u8], buf_pos: &mut usize) { + if let Some(ref mut iter) = self.item_iter { + while *buf_pos < buf.len() { + if let Some(byte) = iter.next() { + buf[*buf_pos] = byte; + *buf_pos += 1; + } else { + break; + } + } + } + } +} + +impl<P, R: Read + fmt::Debug> fmt::Debug for JsonRead<P, R> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Filter") + .field("iter", &self.iter) + .field("initialized", &self.initialized) + .finish() + } +} + +impl<P, R> Read for JsonRead<P, R> +where + R: Read, + P: FnMut(&mut JsonMinifier, &char, Option<&char>) -> bool, +{ + fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + let mut buf_pos: usize = 0; + + if buf.is_empty() { + return Ok(0); + } + + if !self.initialized { + self.iter = Some(ByteToChar::new(self.read.take().unwrap(), buf.len())?); + self.item1 = self.get_next()?; + self.initialized = true; + } + + while let Some(item) = self.item1.take() { + self.item1 = self.get_next()?; + if (self.predicate)(&mut self.minifier, &item, self.item1.as_ref()) { + self.item_iter = Some(item.to_string().into_bytes().into_iter()); + self.add_char_to_buffer(buf, &mut buf_pos); + } + if buf_pos >= buf.len() { + break; + } + } + Ok(buf_pos) + } +} + +impl From<CharsError> for Error { + fn from(_: CharsError) -> Self { + Error::from(ErrorKind::InvalidData) + } +} diff --git a/vendor/minifier/src/json/string.rs b/vendor/minifier/src/json/string.rs new file mode 100644 index 000000000..071bf0012 --- /dev/null +++ b/vendor/minifier/src/json/string.rs @@ -0,0 +1,100 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::json::json_minifier::JsonMinifier; + +use std::fmt; +use std::str::Chars; + +#[derive(Clone)] +pub struct JsonMultiFilter<'a, P: Clone> { + minifier: JsonMinifier, + iter: Chars<'a>, + predicate: P, + initialized: bool, + item1: Option<<Chars<'a> as Iterator>::Item>, +} + +impl<'a, P: Clone> JsonMultiFilter<'a, P> { + #[inline] + pub fn new(iter: Chars<'a>, predicate: P) -> Self { + JsonMultiFilter { + minifier: JsonMinifier::default(), + iter, + predicate, + initialized: false, + item1: None, + } + } +} + +impl<'a, P: Clone> fmt::Debug for JsonMultiFilter<'a, P> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Filter") + .field("minifier", &self.minifier) + .field("iter", &self.iter) + .field("initialized", &self.initialized) + .finish() + } +} + +impl<'a, P: Clone> Iterator for JsonMultiFilter<'a, P> +where + P: FnMut( + &mut JsonMinifier, + &<Chars<'a> as Iterator>::Item, + Option<&<Chars<'a> as Iterator>::Item>, + ) -> bool, +{ + type Item = <Chars<'a> as Iterator>::Item; + + #[inline] + fn next(&mut self) -> Option<<Chars<'a> as Iterator>::Item> { + if !self.initialized { + self.item1 = self.iter.next(); + self.initialized = true; + } + + while let Some(item) = self.item1.take() { + self.item1 = self.iter.next(); + if (self.predicate)(&mut self.minifier, &item, self.item1.as_ref()) { + return Some(item); + } + } + None + } +} + +impl<'a, P> JsonMultiFilter<'a, P> +where + P: FnMut( + &mut JsonMinifier, + &<Chars<'a> as Iterator>::Item, + Option<&<Chars<'a> as Iterator>::Item>, + ) -> bool + + Clone, +{ + pub(super) fn write<W: std::io::Write>(self, mut w: W) -> std::io::Result<()> { + for token in self { + write!(w, "{}", token)?; + } + Ok(()) + } +} + +impl<'a, P> fmt::Display for JsonMultiFilter<'a, P> +where + P: FnMut( + &mut JsonMinifier, + &<Chars<'a> as Iterator>::Item, + Option<&<Chars<'a> as Iterator>::Item>, + ) -> bool + + Clone, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = (*self).clone(); + for token in s { + write!(f, "{}", token)?; + } + Ok(()) + } +} |