tick-encoding 0.1.4

A simple encoding scheme to encode binary data into ASCII strings
Documentation
#![cfg(feature = "alloc")]

use std::borrow::Cow;

use assert_matches::assert_matches;
use proptest::prelude::*;

proptest! {
    #[test]
    fn encode_any_bytes(bytes in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);
        assert!(encoded.len() >= bytes.len());
    }

    #[test]
    fn decode_any_valid_encoding(encoded_string in "([\t\n\r -_a-~]|`(`|0[0-8]|1[124-9A-F]|[89A-F][0-9A-F]))*") {
        let decoded = tick_encoding::decode(encoded_string.as_bytes()).unwrap();
        assert!(decoded.len() <= encoded_string.len());
    }

    #[test]
    fn decode_invalid_encoding_error(non_encoded_string in "([\t\n\r -_a-~]|`(`|0[0-8]|1[124-9A-F]|[89A-F][0-9A-F]))*([^\t\n\r -~])([\t\n\r -_a-~]|`(`|0[0-8]|1[124-9A-F]|[89A-F][0-9A-F]))*") {
        let result = tick_encoding::decode(non_encoded_string.as_bytes());
        assert_matches!(result, Err(_));
    }

    #[test]
    fn encode_then_decode(bytes in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);
        let decoded = tick_encoding::decode(encoded.as_bytes()).unwrap();

        assert_eq!(decoded, bytes);
    }

    #[test]
    fn decode_then_encode(encoded_string in "([\t\n\r -_a-~]|`(`|0[0-8]|1[124-9A-F]|[89A-F][0-9A-F]))*") {
        let decoded = tick_encoding::decode(encoded_string.as_bytes()).unwrap();
        let re_encoded = tick_encoding::encode(&decoded);
        assert_eq!(encoded_string, re_encoded);
    }

    #[test]
    fn decode_then_encode_canonical(possibly_encoded_string in "[\t\n\r -~]*") {
        // Iterate over the string until we hit a decoding error. Then, get
        // the index of the iterator (the index where we hit the error).
        let mut possibly_encoded_iter = possibly_encoded_string.as_bytes().iter();
        let _decode_error = tick_encoding::decode_iter(possibly_encoded_iter.by_ref().copied()).find_map(std::result::Result::err);
        let first_invalid_index = possibly_encoded_string.len() - possibly_encoded_iter.as_slice().len();

        // Because we may have hit an `UnexpectedEnd` error, we might need to
        // cut a few more bytes off the end of the string to get a valid
        // encoding
        let mut encoded = &possibly_encoded_string[..first_invalid_index];
        while tick_encoding::decode(encoded.as_bytes()).is_err() {
            encoded = &encoded[..encoded.len() - 1];
        }

        let decoded = tick_encoding::decode(encoded.as_bytes()).unwrap();
        let re_encoded = tick_encoding::encode(&decoded);
        let re_decoded = tick_encoding::decode(re_encoded.as_bytes()).unwrap();

        assert_eq!(encoded, re_encoded);
        assert_eq!(decoded, re_decoded);
    }

    #[test]
    fn encode_unescaped_borrows(unescaped_string in "[\t\n\r -_a-~]*") {
        let encoded = tick_encoding::encode(unescaped_string.as_bytes());
        assert_matches!(encoded, Cow::Borrowed(_));
    }

    #[test]
    fn decode_unescaped_borrows(unescaped_string in "[\t\n\r -_a-~]*") {
        let decoded = tick_encoding::decode(unescaped_string.as_bytes()).unwrap();
        assert_matches!(decoded, Cow::Borrowed(_));
    }

    #[test]
    fn encode_to_string(bytes in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);

        let mut buffer = String::new();
        let count = tick_encoding::encode_to_string(&bytes, &mut buffer);
        assert_eq!(buffer, encoded);
        assert_eq!(count, encoded.len());
    }

    #[test]
    fn encode_to_string_append(bytes in any::<Vec<u8>>(), prefix in any::<String>()) {
        let encoded = tick_encoding::encode(&bytes);

        let mut buffer = prefix.clone();
        let count = tick_encoding::encode_to_string(&bytes, &mut buffer);

        let buffer_prefix = &buffer[..prefix.len()];
        let buffer_appended = &buffer[prefix.len()..];

        assert_eq!(buffer_prefix, prefix);
        assert_eq!(buffer_appended, encoded);
        assert_eq!(count, encoded.len());
    }

    #[test]
    fn encode_to_vec(bytes in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);

        let mut buffer = vec![];
        let count = tick_encoding::encode_to_vec(&bytes, &mut buffer);
        assert_eq!(buffer, encoded.as_bytes());
        assert_eq!(count, encoded.len());
    }

    #[test]
    fn encode_to_vec_append(bytes in any::<Vec<u8>>(), prefix in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);

        let mut buffer = prefix.clone();
        let count = tick_encoding::encode_to_vec(&bytes, &mut buffer);

        let buffer_prefix = &buffer[..prefix.len()];
        let buffer_appended = &buffer[prefix.len()..];

        assert_eq!(buffer_prefix, prefix);
        assert_eq!(buffer_appended, encoded.as_bytes());
        assert_eq!(count, encoded.len());
    }

    #[test]
    fn decode_to_vec(bytes in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);

        let mut buffer = vec![];
        let count = tick_encoding::decode_to_vec(encoded.as_bytes(), &mut buffer).unwrap();
        assert_eq!(buffer, bytes);
        assert_eq!(count, bytes.len());
    }

    #[test]
    fn decode_to_vec_appended(bytes in any::<Vec<u8>>(), prefix in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);

        let mut buffer = prefix.clone();
        let count = tick_encoding::decode_to_vec(encoded.as_bytes(), &mut buffer).unwrap();

        let buffer_prefix = &buffer[..prefix.len()];
        let buffer_appended = &buffer[prefix.len()..];

        assert_eq!(buffer_prefix, prefix);
        assert_eq!(buffer_appended, bytes);
        assert_eq!(count, bytes.len());
    }

    #[test]
    fn decode_in_place(bytes in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);

        let mut buffer = encoded.clone().into_owned().into_bytes();
        let decoded = tick_encoding::decode_in_place(&mut buffer).unwrap();
        assert_eq!(decoded, bytes);
    }

    #[test]
    fn encode_iter(bytes in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);

        let encode_iter = tick_encoding::encode_iter(bytes.iter().copied());
        let iter_encoded = encode_iter.collect::<String>();

        assert_eq!(encoded, iter_encoded);
    }

    #[test]
    fn decode_iter(bytes in any::<Vec<u8>>()) {
        let encoded = tick_encoding::encode(&bytes);

        let decode_iter = tick_encoding::decode_iter(encoded.bytes());
        let iter_decoded = decode_iter.collect::<Result<Vec<u8>, tick_encoding::DecodeError>>().unwrap();

        assert_eq!(bytes, iter_decoded);
    }
}