compressed_string/
lib.rs

1//! ```rust
2//! use compressed_string::ComprString;
3//!
4//! let raw = "It uses the deflate algorithm, which has a small header overhead, \
5//! so it's suitable even for short-ish strings";
6//!
7//! let compr = ComprString::new(raw);
8//!
9//! assert_eq!(109, raw.len());
10//! assert_eq!(84, compr.compressed_len());
11//!
12//! println!("{}", compr);
13//! let string = compr.to_string();
14//! ```
15
16use std::io::Read;
17use std::io::Write;
18use std::fmt;
19use flate2::Compression;
20use flate2::write::DeflateEncoder;
21use flate2::read::DeflateDecoder;
22
23/// DEFLATE-compressed `String`
24///
25/// Note that conversion from and to the compressed string has a CPU cost, so plan accordingly.
26#[derive(Clone, Eq, PartialEq, Hash)]
27#[cfg_attr(feature = "with_serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
28pub struct ComprString {
29    gz: Box<[u8]>
30}
31
32impl ComprString {
33    /// Compress the given string.
34    ///
35    /// You can also use `.into()`
36    pub fn new(s: &str) -> Self {
37        let mut e = DeflateEncoder::new(Vec::with_capacity(s.len()/2), Compression::best());
38        e.write_all(s.as_bytes()).unwrap();
39        ComprString{ gz: e.finish().unwrap().into_boxed_slice() }
40    }
41
42    /// Decompress the string
43    ///
44    /// You can also use `.into()`
45    pub fn to_string(&self) -> String {
46        let mut deflater = DeflateDecoder::new(&self.gz[..]);
47        let mut s = String::with_capacity(self.gz.len()*2);
48        deflater.read_to_string(&mut s).unwrap();
49        s
50    }
51
52    /// Length of just the DEFLATE-compressed data
53    /// (there's a bit of extra RAM overhead on top of that).
54    ///
55    /// There's no way to get the original length without decompressing the string first.
56    pub fn compressed_len(&self) -> usize {
57        self.gz.len()
58    }
59}
60
61impl fmt::Display for ComprString {
62    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63        f.write_str(&self.to_string())
64    }
65}
66
67impl fmt::Debug for ComprString {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        self.to_string().fmt(f)?;
70        write!(f, "@{}B", self.gz.len())
71    }
72}
73
74impl From<String> for ComprString {
75    fn from(o: String) -> Self {
76        Self::new(&o)
77    }
78}
79
80impl Into<String> for ComprString {
81    fn into(self) -> String {
82        self.to_string()
83    }
84}
85
86impl From<Box<str>> for ComprString {
87    fn from(o: Box<str>) -> Self {
88        Self::new(&o)
89    }
90}
91
92impl Into<Box<str>> for ComprString {
93    fn into(self) -> Box<str> {
94        self.to_string().into_boxed_str()
95    }
96}
97
98impl<'a> From<&'a str> for ComprString {
99    fn from(o: &'a str) -> Self {
100        Self::new(o)
101    }
102}
103
104#[test]
105fn test() {
106    let s = ComprString::new("hęllo world");
107    assert_eq!("hęllo world", &s.to_string());
108    assert_eq!("hęllo world", &format!("{}", s));
109    assert_eq!("\"hęllo world\"@17B", &format!("{:?}", s));
110
111    let s2 = ComprString::new("hęllo world");
112    assert_eq!(s, s2);
113
114    let s = ComprString::new("");
115    assert_eq!("", &s.to_string());
116    assert_eq!(2, s.compressed_len());
117
118    let l = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
119    let s = ComprString::new(l);
120    assert_eq!(l, &s.to_string());
121    assert!(s.compressed_len() < l.len());
122}