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}