zalgo_codec_common/
lib.rs

1//! A crate for converting a string containing only printable ASCII and newlines
2//! into a single unicode grapheme cluster and back.
3//! Provides the non-macro functionality of the crate [`zalgo-codec`](https://docs.rs/zalgo-codec/latest/zalgo_codec/).
4//!
5//! There are two ways of interacting with the codec.
6//! The first is to call the encoding and decoding functions directly,
7//! and the second is to use the [`ZalgoString`] wrapper type.
8//!
9//! # Examples
10//!
11//! Encode a string to a grapheme cluster with [`zalgo_encode`]:
12//! ```
13//! # use zalgo_codec_common::{EncodeError, zalgo_encode};
14//! let s = "Zalgo";
15//! let encoded = zalgo_encode(s)?;
16//! assert_eq!(encoded, "É̺͇͌͏");
17//! # Ok::<(), EncodeError>(())
18//! ```
19//! Decode a grapheme cluster back into a string:
20//! ```
21//! # use zalgo_codec_common::{zalgo_decode, DecodeError};
22//! let encoded = "É̺͇͌͏";
23//! let s = zalgo_decode(encoded)?;
24//! assert_eq!(s, "Zalgo");
25//! # Ok::<(), DecodeError>(())
26//! ```
27//! The [`ZalgoString`] type can be used to encode a string and handle the result in various ways:
28//! ```
29//! # use zalgo_codec_common::{ZalgoString, EncodeError};
30//! let s = "Zalgo";
31//! let zstr = ZalgoString::new(s)?;
32//!
33//! // Implements PartialEq with common string types
34//! assert_eq!(zstr, "É̺͇͌͏");
35//!
36//! // Utility functions
37//! assert_eq!(zstr.len(), 2 * s.len() + 1);
38//! assert_eq!(zstr.decoded_len(), s.len());
39//!
40//! // Iterate over bytes and chars, in both encoded and decoded form
41//! assert_eq!(zstr.bytes().next(), Some(69));
42//! assert_eq!(zstr.decoded_bytes().nth_back(2), Some(b'l'));
43//! assert_eq!(zstr.chars().nth(1), Some('\u{33a}'));
44//! assert_eq!(zstr.decoded_chars().next_back(), Some('o'));
45//!
46//! // Decode inplace
47//! assert_eq!(zstr.into_decoded_string(), "Zalgo");
48//! # Ok::<(), EncodeError>(())
49//! ```
50//!
51//! # Feature flags
52//!
53//! `std`: enables [`EncodeError`] and [`DecodeError`] to capture a [`Backtrace`](std::backtrace::Backtrace).
54//! If this feature is not enabled the library is `no_std` compatible, but still uses the `alloc` crate.
55//!
56//! `serde`: derives the [`serde::Serialize`] and [`serde::Deserialize`] traits
57//! from [`serde`] for [`ZalgoString`].
58//!
59//! `rkyv`: derives the [`rkyv::Serialize`], [`rkyv::Deserialize`], and [`rkyv::Archive`] traits from [`rkyv`] for [`ZalgoString`].
60//!
61//! # Explanation
62//!
63//! Characters U+0300–U+036F are the combining characters for unicode Latin.
64//! The fun thing about combining characters is that you can add as many of these characters
65//! as you like to the original character and it does not create any new symbols,
66//! it only adds symbols on top of the character. It's supposed to be used in order to
67//! create characters such as `á` by taking a normal `a` and adding another character
68//! to give it the mark (U+301, in this case). Fun fact: Unicode doesn't specify
69//! any limit on the number of these characters.
70//! Conveniently, this gives us 112 different characters we can map to,
71//! which nicely maps to the ASCII character range 0x20 -> 0x7F, aka all the non-control characters.
72//! The only issue is that we can't have new lines in this system, so to fix that,
73//! we can simply map 0x7F (DEL) to 0x0A (LF).
74//! This can be represented as `(CHARACTER - 11) % 133 - 21`, and decoded with `(CHARACTER + 22) % 133 + 10`.
75//!
76//! <details><summary><b>Full conversion table</b></summary>
77//!
78//! | ASCII character | Encoded |  
79//! |---|---|  
80//! | A | U+321 |  
81//! | B | U+322 |  
82//! | C | U+323 |  
83//! | D | U+324 |  
84//! | E | U+325 |  
85//! | F | U+326 |  
86//! | G | U+327 |  
87//! | H | U+328 |  
88//! | I | U+329 |  
89//! | J | U+32A |  
90//! | K | U+32B |  
91//! | L | U+32C |  
92//! | M | U+32D |  
93//! | N | U+32E |  
94//! | O | U+32F |  
95//! | P | U+330 |  
96//! | Q | U+331 |  
97//! | R | U+332 |  
98//! | S | U+333 |  
99//! | T | U+334 |  
100//! | U | U+335 |  
101//! | V | U+336 |  
102//! | W | U+337 |  
103//! | X | U+338 |  
104//! | Y | U+339 |  
105//! | Z | U+33A |  
106//! | a | U+341 |  
107//! | b | U+342 |  
108//! | c | U+343 |  
109//! | d | U+344 |  
110//! | e | U+345 |  
111//! | f | U+346 |  
112//! | g | U+347 |  
113//! | h | U+348 |  
114//! | i | U+349 |  
115//! | j | U+34A |  
116//! | k | U+34B |  
117//! | l | U+34C |  
118//! | m | U+34D |  
119//! | n | U+34E |  
120//! | o | U+34F |  
121//! | p | U+350 |  
122//! | q | U+351 |  
123//! | r | U+352 |  
124//! | s | U+353 |  
125//! | t | U+354 |  
126//! | u | U+355 |  
127//! | v | U+356 |  
128//! | w | U+357 |  
129//! | x | U+358 |  
130//! | y | U+359 |  
131//! | z | U+35A |  
132//! | 1 | U+311 |  
133//! | 2 | U+312 |  
134//! | 3 | U+313 |  
135//! | 4 | U+314 |  
136//! | 5 | U+315 |  
137//! | 6 | U+316 |  
138//! | 7 | U+317 |  
139//! | 8 | U+318 |  
140//! | 9 | U+319 |  
141//! | 0 | U+310 |  
142//! |   | U+300 |  
143//! | ! | U+301 |  
144//! | " | U+302 |  
145//! | # | U+303 |  
146//! | $ | U+304 |  
147//! | % | U+305 |  
148//! | & | U+306 |  
149//! | ' | U+307 |  
150//! | ( | U+308 |  
151//! | ) | U+309 |  
152//! | * | U+30A |  
153//! | + | U+30B |  
154//! | , | U+30C |  
155//! | - | U+30D |  
156//! | \ | U+33C |  
157//! | . | U+30E |  
158//! | / | U+30F |  
159//! | : | U+31A |  
160//! | ; | U+31B |  
161//! | < | U+31C |  
162//! | = | U+31D |  
163//! | > | U+31E |  
164//! | ? | U+31F |  
165//! | @ | U+320 |  
166//! | \n| U+36F |  
167//!
168//! </details>
169//!
170//! # Experiment with the codec
171//!
172//! There is an executable available for experimenting with the codec on text and files.
173//! It can be installed with `cargo install zalgo-codec --features binary`.
174//! You can optionally enable the `gui` feature during installation to include a rudimentary GUI mode for the program.
175
176#![no_std]
177#![cfg_attr(docsrs, feature(doc_auto_cfg))]
178
179#[cfg(feature = "std")]
180extern crate std;
181
182extern crate alloc;
183use alloc::{format, string::String, vec, vec::Vec};
184use core::{fmt, str};
185
186mod error;
187pub mod zalgo_string;
188
189pub use error::{DecodeError, EncodeError};
190pub use zalgo_string::ZalgoString;
191
192/// Takes in a string slice that consists of only printable ACII and newline characters
193/// and encodes it into a single grapheme cluster using a reversible encoding scheme.
194///
195/// The resulting string is a single unicode grapheme cluster and should
196/// only take up a single character space horizontally when displayed
197/// (though this can vary between platforms depending on how they deal with unicode).
198/// The resulting string will be ~2 times larger than the original in terms of bytes, and it
199/// can be decoded to recover the original string with [`zalgo_decode`].
200///
201/// # Errors
202///
203/// Returns an error if the input contains a byte that does not correspond to a printable
204/// ASCII character or newline.
205/// Notably this means that this function can not encode tab or carriage return characters.
206/// Carriage returns are present in e.g. line endings on Windows.
207///
208/// # Example
209///
210/// Basic usage:
211/// ```
212/// # use zalgo_codec_common::{EncodeError, zalgo_encode};
213/// assert_eq!(zalgo_encode("Zalgo")?, "É̺͇͌͏");
214/// # Ok::<(), EncodeError>(())
215/// ```
216/// Can not encode non-ASCII characters or ASCII control characters except newlines:
217/// ```
218/// # use zalgo_codec_common::zalgo_encode;
219/// assert!(zalgo_encode("Windows line ending: \r\n").is_err());
220/// assert!(zalgo_encode("Zålgö").is_err());
221/// ```
222#[must_use = "the function returns a new value and does not modify the input"]
223pub fn zalgo_encode(string: &str) -> Result<String, EncodeError> {
224    // We will encode this many bytes at a time before pushing onto the result vector.
225    const BATCH_SIZE: usize = 16;
226
227    // The line we are currently encoding
228    let mut line = 1;
229    // The column on that line we are currently encoding
230    let mut column = 1;
231    // These are used for reporting a useful error if the encoding process fails.
232
233    // Every byte in the input will encode to two bytes. The extra byte is for the initial letter
234    // which is there in order for the output to be displayable in an intuitive way.
235    let mut result = Vec::with_capacity(2 * string.len() + 1);
236    result.push(b'E');
237
238    for (i, batch) in string.as_bytes().chunks(BATCH_SIZE).enumerate() {
239        let mut buffer = [0; 2 * BATCH_SIZE];
240        let mut encoded = 0;
241        for (j, byte) in batch.iter().enumerate() {
242            // Only encode ASCII bytes corresponding to printable characters or newlines.
243            if (32..127).contains(byte) || *byte == b'\n' {
244                if *byte == b'\n' {
245                    line += 1;
246                    // `column` is still 1-indexed since it gets incremented at the end of the current loop iteration.
247                    column = 0;
248                }
249
250                let v = ((i16::from(*byte) - 11).rem_euclid(133) - 21) as u8;
251                buffer[encoded] = (v >> 6) & 1 | 0b1100_1100;
252                buffer[encoded + 1] = (v & 63) | 0b1000_0000;
253                encoded += 2;
254                column += 1;
255            } else {
256                let index = i * BATCH_SIZE + j;
257                // The panic should never trigger since we know that string[i*BATCH_SIZE + j]
258                // has some value which is stored in `byte`, and that this value is the first
259                // byte of a non-ascii character and that Strings in Rust are valid utf-8.
260                // All of this means that the value that starts at this index is a utf-8 encoded
261                // character, which `chars.next()` will extract.
262                let unencodable_character = string[index..].chars().next()
263                // TODO: Find a way to get rid of the expect.
264                    .expect("i*BATCH_SIZE + j is within the string and on a char boundary, so string.chars().next() should find a char");
265                return Err(EncodeError::new(unencodable_character, line, column, index));
266            }
267        }
268        result.extend_from_slice(&buffer[..encoded]);
269    }
270
271    // Safety: the encoding process does not produce invalid UTF-8
272    // if given valid printable ASCII + newlines,
273    // which is checked before this point
274    Ok(unsafe { String::from_utf8_unchecked(result) })
275}
276
277/// Takes in a string that was encoded by [`zalgo_encode`] and decodes it back into an ASCII string.
278///
279/// # Errors
280///
281/// Returns an error if the decoded string is not valid UTF-8.
282/// This can happen if the input is empty, or if it is a string that was not encoded by [`zalgo_encode`],
283/// since the byte manipulations that this function does could result in invalid unicode in that case.
284/// Even if no error is returned in such a case the results are not meaningful.
285/// If you want to be able to decode without this check, consider using a [`ZalgoString`].
286///
287/// # Examples
288///
289/// Basic usage:
290/// ```
291/// # use zalgo_codec_common::{zalgo_decode, DecodeError};
292/// assert_eq!(zalgo_decode("É̺͇͌͏")?, "Zalgo");
293/// # Ok::<(), DecodeError>(())
294/// ```
295/// Decoding arbitrary strings that were not produced by [`zalgo_encode`] will most likely lead to errors:
296/// ```
297/// # use zalgo_codec_common::zalgo_decode;
298/// assert!(zalgo_decode("Zalgo").is_err());
299/// ```
300/// If it doesn't the results are not meaningful:
301/// ```
302/// # use zalgo_codec_common::{zalgo_decode, DecodeError};
303/// assert_eq!(zalgo_decode("awö")?, "c");
304/// # Ok::<(), DecodeError>(())
305/// ```
306#[must_use = "the function returns a new value and does not modify the input"]
307pub fn zalgo_decode(encoded: &str) -> Result<String, DecodeError> {
308    if encoded.is_empty() {
309        return Err(DecodeError::new(None));
310    }
311    let mut res = vec![0; (encoded.len() - 1) / 2];
312    let bytes = encoded.as_bytes();
313
314    for (write, read) in (1..encoded.len()).step_by(2).enumerate() {
315        match bytes.get(read + 1) {
316            Some(next) => res[write] = decode_byte_pair(bytes[read], *next),
317            None => break,
318        }
319    }
320
321    String::from_utf8(res).map_err(|e| DecodeError::new(Some(e)))
322}
323
324#[inline]
325#[must_use = "the function returns a new value and does not modify its inputs"]
326const fn decode_byte_pair(odd: u8, even: u8) -> u8 {
327    ((odd << 6 & 64 | even & 63) + 22) % 133 + 10
328}
329
330/// zalgo-encodes an ASCII string containing Python code and
331/// wraps it in a decoder that decodes and executes it.
332/// The resulting Python code should retain the functionality of the original.
333///
334/// # Example
335///
336/// Encode a simple hello world program in Python
337/// ```
338/// # use zalgo_codec_common::{EncodeError, zalgo_wrap_python};
339/// let py_hello_world = "print(\"Hello, world!\")\n";
340/// let py_hello_world_enc = zalgo_wrap_python(py_hello_world)?;
341/// assert_eq!(
342///     py_hello_world_enc,
343///     "b='Ę͉͎͔͐͒̈̂͌͌ͅ͏̌̀͗͏͒͌̈́́̂̉ͯ'.encode();exec(''.join(chr(((h<<6&64|c&63)+22)%133+10)for h,c in zip(b[1::2],b[2::2])))",
344/// );
345/// # Ok::<(), EncodeError>(())
346/// ```
347/// If the contents of the variable `py_hello_world_enc` in
348/// the above code snippet is saved to a file
349/// you can run it with python and it will produce the output
350/// that is expected of the code in the variable `py_hello_world`.
351/// In the example below the file is named `enc.py`.
352/// ```bash
353/// $ python enc.py
354/// Hello, world!
355/// ```
356///
357/// # Known issues
358///
359/// May not work correctly on python versions before 3.10,
360/// see [this github issue](https://github.com/DaCoolOne/DumbIdeas/issues/1) for more information.
361///
362/// # Errors
363///
364/// Returns an error if the input contains a byte that does not correspond to a printable
365/// ASCII character or newline.
366/// ```
367/// # use zalgo_codec_common::zalgo_wrap_python;
368/// let res = zalgo_wrap_python(r#"print("That will be 5€ please")"#);
369/// assert_eq!(
370///     res.map_err(|e| (e.char(), e.line(), e.column())),
371///     Err(('€', 1, 22)),
372/// );
373/// ```
374#[must_use = "the function returns a new value and does not modify the input"]
375pub fn zalgo_wrap_python(python: &str) -> Result<String, EncodeError> {
376    let encoded_string = zalgo_encode(python)?;
377    Ok(format!("b='{encoded_string}'.encode();exec(''.join(chr(((h<<6&64|c&63)+22)%133+10)for h,c in zip(b[1::2],b[2::2])))"))
378}
379
380#[cfg(test)]
381mod test {
382    use super::*;
383
384    #[test]
385    fn test_char() {
386        assert_eq!(zalgo_encode("Zalgo\r").map_err(|e| e.char()), Err('\r'));
387        assert_eq!(zalgo_encode("Zålgo").map_err(|e| e.char()), Err('å'));
388    }
389
390    #[test]
391    fn test_empty_decode() {
392        assert!(zalgo_decode("").is_err());
393    }
394
395    #[test]
396    fn verify_conversion_table() {
397        assert_eq!(zalgo_encode("A").unwrap(), "E\u{321}");
398        assert_eq!(zalgo_decode("E\u{321}").unwrap(), "A");
399        assert_eq!(zalgo_encode("B").unwrap(), "E\u{322}");
400        assert_eq!(zalgo_decode("E\u{322}").unwrap(), "B");
401        assert_eq!(zalgo_encode("C").unwrap(), "E\u{323}");
402        assert_eq!(zalgo_decode("E\u{323}").unwrap(), "C");
403        assert_eq!(zalgo_encode("D").unwrap(), "E\u{324}");
404        assert_eq!(zalgo_decode("E\u{324}").unwrap(), "D");
405        assert_eq!(zalgo_encode("E").unwrap(), "E\u{325}");
406        assert_eq!(zalgo_decode("E\u{325}").unwrap(), "E");
407        assert_eq!(zalgo_encode("F").unwrap(), "E\u{326}");
408        assert_eq!(zalgo_decode("E\u{326}").unwrap(), "F");
409        assert_eq!(zalgo_encode("G").unwrap(), "E\u{327}");
410        assert_eq!(zalgo_decode("E\u{327}").unwrap(), "G");
411        assert_eq!(zalgo_encode("H").unwrap(), "E\u{328}");
412        assert_eq!(zalgo_decode("E\u{328}").unwrap(), "H");
413        assert_eq!(zalgo_encode("I").unwrap(), "E\u{329}");
414        assert_eq!(zalgo_decode("E\u{329}").unwrap(), "I");
415        assert_eq!(zalgo_encode("J").unwrap(), "E\u{32a}");
416        assert_eq!(zalgo_decode("E\u{32a}").unwrap(), "J");
417        assert_eq!(zalgo_encode("K").unwrap(), "E\u{32b}");
418        assert_eq!(zalgo_decode("E\u{32b}").unwrap(), "K");
419        assert_eq!(zalgo_encode("L").unwrap(), "E\u{32c}");
420        assert_eq!(zalgo_decode("E\u{32c}").unwrap(), "L");
421        assert_eq!(zalgo_encode("M").unwrap(), "E\u{32d}");
422        assert_eq!(zalgo_decode("E\u{32d}").unwrap(), "M");
423        assert_eq!(zalgo_encode("N").unwrap(), "E\u{32e}");
424        assert_eq!(zalgo_decode("E\u{32e}").unwrap(), "N");
425        assert_eq!(zalgo_encode("O").unwrap(), "E\u{32f}");
426        assert_eq!(zalgo_decode("E\u{32f}").unwrap(), "O");
427        assert_eq!(zalgo_encode("P").unwrap(), "E\u{330}");
428        assert_eq!(zalgo_decode("E\u{330}").unwrap(), "P");
429        assert_eq!(zalgo_encode("Q").unwrap(), "E\u{331}");
430        assert_eq!(zalgo_decode("E\u{331}").unwrap(), "Q");
431        assert_eq!(zalgo_encode("R").unwrap(), "E\u{332}");
432        assert_eq!(zalgo_decode("E\u{332}").unwrap(), "R");
433        assert_eq!(zalgo_encode("S").unwrap(), "E\u{333}");
434        assert_eq!(zalgo_decode("E\u{333}").unwrap(), "S");
435        assert_eq!(zalgo_encode("T").unwrap(), "E\u{334}");
436        assert_eq!(zalgo_decode("E\u{334}").unwrap(), "T");
437        assert_eq!(zalgo_encode("U").unwrap(), "E\u{335}");
438        assert_eq!(zalgo_decode("E\u{335}").unwrap(), "U");
439        assert_eq!(zalgo_encode("V").unwrap(), "E\u{336}");
440        assert_eq!(zalgo_decode("E\u{336}").unwrap(), "V");
441        assert_eq!(zalgo_encode("W").unwrap(), "E\u{337}");
442        assert_eq!(zalgo_decode("E\u{337}").unwrap(), "W");
443        assert_eq!(zalgo_encode("X").unwrap(), "E\u{338}");
444        assert_eq!(zalgo_decode("E\u{338}").unwrap(), "X");
445        assert_eq!(zalgo_encode("Y").unwrap(), "E\u{339}");
446        assert_eq!(zalgo_decode("E\u{339}").unwrap(), "Y");
447        assert_eq!(zalgo_encode("Z").unwrap(), "E\u{33a}");
448        assert_eq!(zalgo_decode("E\u{33a}").unwrap(), "Z");
449        assert_eq!(zalgo_encode("a").unwrap(), "E\u{341}");
450        assert_eq!(zalgo_decode("E\u{341}").unwrap(), "a");
451        assert_eq!(zalgo_encode("b").unwrap(), "E\u{342}");
452        assert_eq!(zalgo_decode("E\u{342}").unwrap(), "b");
453        assert_eq!(zalgo_encode("c").unwrap(), "E\u{343}");
454        assert_eq!(zalgo_decode("E\u{343}").unwrap(), "c");
455        assert_eq!(zalgo_encode("d").unwrap(), "E\u{344}");
456        assert_eq!(zalgo_decode("E\u{344}").unwrap(), "d");
457        assert_eq!(zalgo_encode("e").unwrap(), "E\u{345}");
458        assert_eq!(zalgo_decode("E\u{345}").unwrap(), "e");
459        assert_eq!(zalgo_encode("f").unwrap(), "E\u{346}");
460        assert_eq!(zalgo_decode("E\u{346}").unwrap(), "f");
461        assert_eq!(zalgo_encode("g").unwrap(), "E\u{347}");
462        assert_eq!(zalgo_decode("E\u{347}").unwrap(), "g");
463        assert_eq!(zalgo_encode("h").unwrap(), "E\u{348}");
464        assert_eq!(zalgo_decode("E\u{348}").unwrap(), "h");
465        assert_eq!(zalgo_encode("i").unwrap(), "E\u{349}");
466        assert_eq!(zalgo_decode("E\u{349}").unwrap(), "i");
467        assert_eq!(zalgo_encode("j").unwrap(), "E\u{34a}");
468        assert_eq!(zalgo_decode("E\u{34a}").unwrap(), "j");
469        assert_eq!(zalgo_encode("k").unwrap(), "E\u{34b}");
470        assert_eq!(zalgo_decode("E\u{34b}").unwrap(), "k");
471        assert_eq!(zalgo_encode("l").unwrap(), "E\u{34c}");
472        assert_eq!(zalgo_decode("E\u{34c}").unwrap(), "l");
473        assert_eq!(zalgo_encode("m").unwrap(), "E\u{34d}");
474        assert_eq!(zalgo_decode("E\u{34d}").unwrap(), "m");
475        assert_eq!(zalgo_encode("n").unwrap(), "E\u{34e}");
476        assert_eq!(zalgo_decode("E\u{34e}").unwrap(), "n");
477        assert_eq!(zalgo_encode("o").unwrap(), "E\u{34f}");
478        assert_eq!(zalgo_decode("E\u{34f}").unwrap(), "o");
479        assert_eq!(zalgo_encode("p").unwrap(), "E\u{350}");
480        assert_eq!(zalgo_decode("E\u{350}").unwrap(), "p");
481        assert_eq!(zalgo_encode("q").unwrap(), "E\u{351}");
482        assert_eq!(zalgo_decode("E\u{351}").unwrap(), "q");
483        assert_eq!(zalgo_encode("r").unwrap(), "E\u{352}");
484        assert_eq!(zalgo_decode("E\u{352}").unwrap(), "r");
485        assert_eq!(zalgo_encode("s").unwrap(), "E\u{353}");
486        assert_eq!(zalgo_decode("E\u{353}").unwrap(), "s");
487        assert_eq!(zalgo_encode("t").unwrap(), "E\u{354}");
488        assert_eq!(zalgo_decode("E\u{354}").unwrap(), "t");
489        assert_eq!(zalgo_encode("u").unwrap(), "E\u{355}");
490        assert_eq!(zalgo_decode("E\u{355}").unwrap(), "u");
491        assert_eq!(zalgo_encode("v").unwrap(), "E\u{356}");
492        assert_eq!(zalgo_decode("E\u{356}").unwrap(), "v");
493        assert_eq!(zalgo_encode("w").unwrap(), "E\u{357}");
494        assert_eq!(zalgo_decode("E\u{357}").unwrap(), "w");
495        assert_eq!(zalgo_encode("x").unwrap(), "E\u{358}");
496        assert_eq!(zalgo_decode("E\u{358}").unwrap(), "x");
497        assert_eq!(zalgo_encode("y").unwrap(), "E\u{359}");
498        assert_eq!(zalgo_decode("E\u{359}").unwrap(), "y");
499        assert_eq!(zalgo_encode("z").unwrap(), "E\u{35a}");
500        assert_eq!(zalgo_decode("E\u{35a}").unwrap(), "z");
501        assert_eq!(zalgo_encode("1").unwrap(), "E\u{311}");
502        assert_eq!(zalgo_decode("E\u{311}").unwrap(), "1");
503        assert_eq!(zalgo_encode("2").unwrap(), "E\u{312}");
504        assert_eq!(zalgo_decode("E\u{312}").unwrap(), "2");
505        assert_eq!(zalgo_encode("3").unwrap(), "E\u{313}");
506        assert_eq!(zalgo_decode("E\u{313}").unwrap(), "3");
507        assert_eq!(zalgo_encode("4").unwrap(), "E\u{314}");
508        assert_eq!(zalgo_decode("E\u{314}").unwrap(), "4");
509        assert_eq!(zalgo_encode("5").unwrap(), "E\u{315}");
510        assert_eq!(zalgo_decode("E\u{315}").unwrap(), "5");
511        assert_eq!(zalgo_encode("6").unwrap(), "E\u{316}");
512        assert_eq!(zalgo_decode("E\u{316}").unwrap(), "6");
513        assert_eq!(zalgo_encode("7").unwrap(), "E\u{317}");
514        assert_eq!(zalgo_decode("E\u{317}").unwrap(), "7");
515        assert_eq!(zalgo_encode("8").unwrap(), "E\u{318}");
516        assert_eq!(zalgo_decode("E\u{318}").unwrap(), "8");
517        assert_eq!(zalgo_encode("9").unwrap(), "E\u{319}");
518        assert_eq!(zalgo_decode("E\u{319}").unwrap(), "9");
519        assert_eq!(zalgo_encode("0").unwrap(), "E\u{310}");
520        assert_eq!(zalgo_decode("E\u{310}").unwrap(), "0");
521        assert_eq!(zalgo_encode(" ").unwrap(), "E\u{300}");
522        assert_eq!(zalgo_decode("E\u{300}").unwrap(), " ");
523        assert_eq!(zalgo_encode("!").unwrap(), "E\u{301}");
524        assert_eq!(zalgo_decode("E\u{301}").unwrap(), "!");
525        assert_eq!(zalgo_encode("\"").unwrap(), "E\u{302}");
526        assert_eq!(zalgo_decode("E\u{302}").unwrap(), "\"");
527        assert_eq!(zalgo_encode("#").unwrap(), "E\u{303}");
528        assert_eq!(zalgo_decode("E\u{303}").unwrap(), "#");
529        assert_eq!(zalgo_encode("$").unwrap(), "E\u{304}");
530        assert_eq!(zalgo_decode("E\u{304}").unwrap(), "$");
531        assert_eq!(zalgo_encode("%").unwrap(), "E\u{305}");
532        assert_eq!(zalgo_decode("E\u{305}").unwrap(), "%");
533        assert_eq!(zalgo_encode("&").unwrap(), "E\u{306}");
534        assert_eq!(zalgo_decode("E\u{306}").unwrap(), "&");
535        assert_eq!(zalgo_encode("'").unwrap(), "E\u{307}");
536        assert_eq!(zalgo_decode("E\u{307}").unwrap(), "'");
537        assert_eq!(zalgo_encode("(").unwrap(), "E\u{308}");
538        assert_eq!(zalgo_decode("E\u{308}").unwrap(), "(");
539        assert_eq!(zalgo_encode(")").unwrap(), "E\u{309}");
540        assert_eq!(zalgo_decode("E\u{309}").unwrap(), ")");
541        assert_eq!(zalgo_encode("*").unwrap(), "E\u{30a}");
542        assert_eq!(zalgo_decode("E\u{30a}").unwrap(), "*");
543        assert_eq!(zalgo_encode("+").unwrap(), "E\u{30b}");
544        assert_eq!(zalgo_decode("E\u{30b}").unwrap(), "+");
545        assert_eq!(zalgo_encode(",").unwrap(), "E\u{30c}");
546        assert_eq!(zalgo_decode("E\u{30c}").unwrap(), ",");
547        assert_eq!(zalgo_encode("-").unwrap(), "E\u{30d}");
548        assert_eq!(zalgo_decode("E\u{30d}").unwrap(), "-");
549        assert_eq!(zalgo_encode("\\").unwrap(), "E\u{33c}");
550        assert_eq!(zalgo_decode("E\u{33c}").unwrap(), "\\");
551        assert_eq!(zalgo_encode(".").unwrap(), "E\u{30e}");
552        assert_eq!(zalgo_decode("E\u{30e}").unwrap(), ".");
553        assert_eq!(zalgo_encode("/").unwrap(), "E\u{30f}");
554        assert_eq!(zalgo_decode("E\u{30f}").unwrap(), "/");
555        assert_eq!(zalgo_encode(":").unwrap(), "E\u{31a}");
556        assert_eq!(zalgo_decode("E\u{31a}").unwrap(), ":");
557        assert_eq!(zalgo_encode(";").unwrap(), "E\u{31b}");
558        assert_eq!(zalgo_decode("E\u{31b}").unwrap(), ";");
559        assert_eq!(zalgo_encode("<").unwrap(), "E\u{31c}");
560        assert_eq!(zalgo_decode("E\u{31c}").unwrap(), "<");
561        assert_eq!(zalgo_encode("=").unwrap(), "E\u{31d}");
562        assert_eq!(zalgo_decode("E\u{31d}").unwrap(), "=");
563        assert_eq!(zalgo_encode(">").unwrap(), "E\u{31e}");
564        assert_eq!(zalgo_decode("E\u{31e}").unwrap(), ">");
565        assert_eq!(zalgo_encode("?").unwrap(), "E\u{31f}");
566        assert_eq!(zalgo_decode("E\u{31f}").unwrap(), "?");
567        assert_eq!(zalgo_encode("@").unwrap(), "E\u{320}");
568        assert_eq!(zalgo_decode("E\u{320}").unwrap(), "@");
569        assert_eq!(zalgo_encode("\n").unwrap(), "E\u{36f}");
570        assert_eq!(zalgo_decode("E\u{36f}").unwrap(), "\n");
571    }
572}