cheetah-string 3.0.0

An immutable, clone-cheap UTF-8 string with explicit construction and byte interoperability
Documentation
use cheetah_string::CheetahString;

const SIZES: [usize; 15] = [0, 1, 2, 7, 15, 16, 17, 22, 23, 24, 31, 32, 64, 1024, 4096];

fn deterministic_ascii(len: usize) -> String {
    (0..len)
        .map(|index| char::from(b'a' + ((index * 17 + 11) % 26) as u8))
        .collect()
}

#[test]
fn equality_matches_slice_semantics_across_sizes_and_mismatches() {
    for size in SIZES {
        let source = deterministic_ascii(size);
        let value = CheetahString::from(source.as_str());
        let same = CheetahString::from(source.as_str());
        assert_eq!(value == same, source.as_bytes() == source.as_bytes());

        if size > 0 {
            for index in [0, size / 2, size - 1] {
                let mut mismatch = source.clone().into_bytes();
                mismatch[index] = if mismatch[index] == b'~' { b'!' } else { b'~' };
                let mismatch = String::from_utf8(mismatch).expect("ASCII replacement");
                let other = CheetahString::from(mismatch.as_str());
                assert_eq!(value == other, source.as_bytes() == mismatch.as_bytes());
            }
        }
    }
}

#[test]
fn prefix_and_suffix_match_str_semantics_across_boundaries() {
    for size in SIZES {
        let source = deterministic_ascii(size);
        let value = CheetahString::from(source.as_str());

        for needle_len in [0, size / 2, size, size.saturating_add(1)] {
            let prefix_len = needle_len.min(size);
            let prefix = &source[..prefix_len];
            assert_eq!(value.starts_with(prefix), source.starts_with(prefix));
            assert_eq!(value.ends_with(prefix), source.ends_with(prefix));

            let absent = "~".repeat(needle_len);
            assert_eq!(
                value.starts_with(absent.as_str()),
                source.starts_with(absent.as_str())
            );
            assert_eq!(
                value.ends_with(absent.as_str()),
                source.ends_with(absent.as_str())
            );
        }
    }
}

#[test]
fn unicode_and_unaligned_inputs_keep_identical_behavior() {
    for offset in 0..16 {
        let source = format!("{}火水風{}", "x".repeat(offset), "".repeat(64));
        let value = CheetahString::from(source.as_str());
        for needle in ["", "", "火水風", "", "不存在"] {
            assert_eq!(value.starts_with(needle), source.starts_with(needle));
            assert_eq!(value.ends_with(needle), source.ends_with(needle));
        }
        assert_eq!(value, source);
    }
}