pub fn dupstring(s: &str) -> String {
s.to_string()
}
pub fn dupstring_wlen(s: &str, len: usize) -> String {
let bytes = s.as_bytes();
let n = len.min(bytes.len());
String::from_utf8_lossy(&bytes[..n]).into_owned()
}
pub fn ztrdup(s: &str) -> String {
s.to_string()
}
pub fn wcs_ztrdup(s: &str) -> String {
s.to_string()
}
pub fn tricat(s1: &str, s2: &str, s3: &str) -> String {
let mut result = String::with_capacity(s1.len() + s2.len() + s3.len());
result.push_str(s1);
result.push_str(s2);
result.push_str(s3);
result
}
pub fn zhtricat(s1: &str, s2: &str, s3: &str) -> String {
tricat(s1, s2, s3)
}
pub fn dyncat(s1: &str, s2: &str) -> String {
let mut result = String::with_capacity(s1.len() + s2.len());
result.push_str(s1);
result.push_str(s2);
result
}
pub fn bicat(s1: &str, s2: &str) -> String {
let mut result = String::with_capacity(s1.len() + s2.len());
result.push_str(s1);
result.push_str(s2);
result
}
pub fn dupstrpfx(s: &str, len: usize) -> String {
let bytes = s.as_bytes();
let n = len.min(bytes.len());
String::from_utf8_lossy(&bytes[..n]).into_owned()
}
pub fn ztrduppfx(s: &str, len: usize) -> String {
dupstrpfx(s, len)
}
pub fn appstr(base: &mut String, append: &str) {
base.push_str(append);
}
pub fn strend(str: &str) -> &str {
if str.is_empty() {
return str;
}
let bytes = str.as_bytes();
let mut i = bytes.len();
while i > 0 {
i -= 1;
if bytes[i] & 0xC0 != 0x80 {
return &str[i..];
}
}
str
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dupstring() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dupstring("hello"), "hello");
assert_eq!(dupstring(""), "");
}
#[test]
fn test_dupstring_wlen() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dupstring_wlen("hello world", 5), "hello");
assert_eq!(dupstring_wlen("hi", 50), "hi");
assert_eq!(dupstring_wlen("hello", 0), "");
}
#[test]
fn test_dupstring_wlen_byte_safe_at_codepoint_boundary() {
let _g = crate::test_util::global_state_lock();
let s = "café";
let r = dupstring_wlen(s, 4);
assert!(r.starts_with("caf"));
}
#[test]
fn test_ztrdup() {
let _g = crate::test_util::global_state_lock();
assert_eq!(ztrdup("permanent"), "permanent");
}
#[test]
fn test_wcs_ztrdup() {
let _g = crate::test_util::global_state_lock();
assert_eq!(wcs_ztrdup("ünicode"), "ünicode");
}
#[test]
fn test_tricat() {
let _g = crate::test_util::global_state_lock();
assert_eq!(tricat("a", "b", "c"), "abc");
assert_eq!(tricat("", "", ""), "");
assert_eq!(tricat("foo", "", "bar"), "foobar");
}
#[test]
fn test_zhtricat() {
let _g = crate::test_util::global_state_lock();
assert_eq!(zhtricat("x", "y", "z"), "xyz");
}
#[test]
fn test_bicat() {
let _g = crate::test_util::global_state_lock();
assert_eq!(bicat("hello", " world"), "hello world");
assert_eq!(bicat("", ""), "");
}
#[test]
fn test_dyncat() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dyncat("foo", "bar"), "foobar");
}
#[test]
fn test_appstr() {
let _g = crate::test_util::global_state_lock();
let mut s = "hello".to_string();
appstr(&mut s, " world");
assert_eq!(s, "hello world");
}
#[test]
fn test_dupstrpfx() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dupstrpfx("hello world", 5), "hello");
assert_eq!(dupstrpfx("hi", 50), "hi");
assert_eq!(dupstrpfx("hi", 0), "");
}
#[test]
fn test_dupstrpfx_byte_safe() {
let _g = crate::test_util::global_state_lock();
let _ = dupstrpfx("é", 1);
}
#[test]
fn test_ztrduppfx() {
let _g = crate::test_util::global_state_lock();
assert_eq!(ztrduppfx("hello", 3), "hel");
}
#[test]
fn test_strend_returns_last_codepoint() {
let _g = crate::test_util::global_state_lock();
assert_eq!(strend("hello"), "o");
assert_eq!(strend(""), "");
assert_eq!(strend("café"), "é");
assert_eq!(strend("a"), "a");
}
#[test]
fn tricat_concatenates_three_segments_in_order() {
let _g = crate::test_util::global_state_lock();
assert_eq!(tricat("a", "b", "c"), "abc");
assert_eq!(tricat("", "b", "c"), "bc");
assert_eq!(tricat("a", "", "c"), "ac");
assert_eq!(tricat("a", "b", ""), "ab");
}
#[test]
fn dyncat_concatenates_two_segments() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dyncat("hello", " world"), "hello world");
assert_eq!(dyncat("", "x"), "x");
}
#[test]
fn ztrdup_returns_independent_owned_copy() {
let _g = crate::test_util::global_state_lock();
let mut src = String::from("original");
let dup = ztrdup(&src);
src.clear();
assert_eq!(dup, "original", "dup must survive source-side clear");
}
#[test]
fn dupstrpfx_handles_len_larger_than_input() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dupstrpfx("ab", 100), "ab");
assert_eq!(dupstrpfx("hello", 0), "");
assert_eq!(dupstrpfx("hello", 3), "hel");
}
#[test]
fn dyncat_empty_inputs_return_empty() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dyncat("", ""), "");
}
#[test]
fn bicat_concatenates_in_order_with_either_empty() {
let _g = crate::test_util::global_state_lock();
assert_eq!(bicat("foo", "bar"), "foobar");
assert_eq!(
bicat("", "bar"),
"bar",
"c:152 — strcpy(ptr, \"\") writes only the NUL, ptr+0 starts s2"
);
assert_eq!(
bicat("foo", ""),
"foo",
"c:153 — strcpy(ptr+3, \"\") writes only the NUL"
);
assert_eq!(bicat("", ""), "");
}
#[test]
fn ztrduppfx_matches_dupstrpfx_byte_for_byte() {
let _g = crate::test_util::global_state_lock();
for (s, len) in [("hello", 3usize), ("ab", 100), ("hello", 0), ("", 5)] {
assert_eq!(
ztrduppfx(s, len),
dupstrpfx(s, len),
"ztrduppfx/dupstrpfx divergence at ({:?}, {})",
s,
len
);
}
}
#[test]
fn appstr_appends_in_place() {
let _g = crate::test_util::global_state_lock();
let mut b = String::from("foo");
appstr(&mut b, "bar");
assert_eq!(b, "foobar");
appstr(&mut b, "");
assert_eq!(b, "foobar", "appending empty must leave base unchanged");
let mut e = String::new();
appstr(&mut e, "xyz");
assert_eq!(e, "xyz");
}
#[test]
fn strend_returns_only_last_character_for_multichar_input() {
let _g = crate::test_util::global_state_lock();
assert_eq!(strend("hello"), "o");
assert_eq!(strend("ab"), "b");
assert_eq!(strend(""), "");
}
#[test]
fn dupstring_returns_owned_copy_with_identity_content() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dupstring("hello"), "hello");
assert_eq!(
dupstring(""),
"",
"c:39 — empty input → len 0+1, strcpy copies NUL"
);
assert_eq!(dupstring("café"), "café");
assert_eq!(dupstring("字"), "字");
}
#[test]
fn dupstring_wlen_respects_byte_length_and_clamps_overflow() {
let _g = crate::test_util::global_state_lock();
assert_eq!(dupstring_wlen("hello world", 5), "hello");
assert_eq!(dupstring_wlen("hello", 0), "");
assert_eq!(dupstring_wlen("ab", 100), "ab");
assert_eq!(dupstring_wlen("foo", 3), "foo");
}
#[test]
fn wcs_ztrdup_returns_independent_copy() {
let _g = crate::test_util::global_state_lock();
let mut src = String::from("widechar");
let dup = wcs_ztrdup(&src);
src.clear();
assert_eq!(
dup, "widechar",
"wide-char dup must survive source-side mutation"
);
assert_eq!(wcs_ztrdup("éàü字"), "éàü字");
}
#[test]
fn zhtricat_matches_tricat_byte_for_byte() {
let _g = crate::test_util::global_state_lock();
for (a, b, c) in [
("foo", "bar", "baz"),
("", "x", ""),
("a", "", "z"),
("", "", ""),
] {
assert_eq!(
zhtricat(a, b, c),
tricat(a, b, c),
"lane divergence at ({:?}, {:?}, {:?})",
a,
b,
c
);
}
}
#[test]
fn ztrduppfx_clamps_oversize_len_safely() {
let _g = crate::test_util::global_state_lock();
assert_eq!(ztrduppfx("hi", 100), "hi");
assert_eq!(ztrduppfx("", 5), "");
assert_eq!(ztrduppfx("abc", 2), "ab");
}
#[test]
fn tricat_three_non_empty_strings() {
assert_eq!(tricat("foo", "bar", "baz"), "foobarbaz");
}
#[test]
fn tricat_empty_first_keeps_others() {
assert_eq!(tricat("", "bar", "baz"), "barbaz");
}
#[test]
fn tricat_empty_middle_keeps_others() {
assert_eq!(tricat("foo", "", "baz"), "foobaz");
}
#[test]
fn tricat_empty_last_keeps_others() {
assert_eq!(tricat("foo", "bar", ""), "foobar");
}
#[test]
fn tricat_all_empty_yields_empty() {
assert_eq!(tricat("", "", ""), "");
}
#[test]
fn tricat_multibyte_utf8_concatenates_correctly() {
assert_eq!(tricat("日", "本", "語"), "日本語");
}
#[test]
fn bicat_two_non_empty_strings() {
assert_eq!(bicat("hello", " world"), "hello world");
}
#[test]
fn bicat_empty_first_returns_second() {
assert_eq!(bicat("", "world"), "world");
}
#[test]
fn bicat_empty_second_returns_first() {
assert_eq!(bicat("hello", ""), "hello");
}
#[test]
fn bicat_both_empty_yields_empty() {
assert_eq!(bicat("", ""), "");
}
#[test]
fn dyncat_two_strings() {
assert_eq!(dyncat("abc", "xyz"), "abcxyz");
}
#[test]
fn dyncat_empties() {
assert_eq!(dyncat("", "x"), "x");
assert_eq!(dyncat("x", ""), "x");
assert_eq!(dyncat("", ""), "");
}
#[test]
fn appstr_appends_to_existing_string() {
let mut s = String::from("hello");
appstr(&mut s, " world");
assert_eq!(s, "hello world");
}
#[test]
fn appstr_to_empty_buffer_yields_argument() {
let mut s = String::new();
appstr(&mut s, "value");
assert_eq!(s, "value");
}
#[test]
fn appstr_empty_argument_is_noop() {
let mut s = String::from("preserved");
appstr(&mut s, "");
assert_eq!(s, "preserved");
}
#[test]
fn appstr_repeated_calls_accumulate() {
let mut s = String::new();
appstr(&mut s, "a");
appstr(&mut s, "b");
appstr(&mut s, "c");
assert_eq!(s, "abc");
}
#[test]
fn strend_returns_slice_starting_at_last_codepoint() {
assert_eq!(strend("hello"), "o");
}
#[test]
fn strend_empty_input_returns_empty() {
assert_eq!(strend(""), "");
}
#[test]
fn strend_single_char_returns_self() {
assert_eq!(strend("a"), "a");
}
#[test]
fn strend_multibyte_returns_last_codepoint_only() {
assert_eq!(strend("日本"), "本");
}
#[test]
fn dupstring_returns_identical_content() {
assert_eq!(dupstring("hello world"), "hello world");
assert_eq!(dupstring(""), "");
assert_eq!(dupstring("日本語"), "日本語");
}
#[test]
fn ztrdup_identity_for_ascii_and_utf8() {
assert_eq!(ztrdup("hello"), "hello");
assert_eq!(ztrdup(""), "");
}
#[test]
fn wcs_ztrdup_ascii_round_trip() {
assert_eq!(wcs_ztrdup("hello"), "hello");
assert_eq!(wcs_ztrdup(""), "");
}
#[test]
fn zhtricat_joins_three_strings() {
assert_eq!(zhtricat("a", "b", "c"), "abc");
assert_eq!(zhtricat("", "middle", ""), "middle");
assert_eq!(zhtricat("pre", "", "suf"), "presuf");
}
#[test]
fn zhtricat_matches_tricat() {
for (a, b, c) in &[("x", "y", "z"), ("", "", ""), ("foo", "bar", "baz")] {
assert_eq!(zhtricat(a, b, c), tricat(a, b, c));
}
}
#[test]
fn dyncat_matches_bicat() {
for (a, b) in &[("foo", "bar"), ("", ""), ("hello", "")] {
assert_eq!(dyncat(a, b), bicat(a, b));
}
}
#[test]
fn dupstrpfx_takes_byte_prefix() {
assert_eq!(dupstrpfx("hello", 3), "hel");
assert_eq!(dupstrpfx("hello", 0), "");
assert_eq!(dupstrpfx("hi", 100), "hi");
}
#[test]
fn ztrduppfx_matches_dupstrpfx() {
for (s, n) in &[("hello", 3), ("", 0), ("foo", 100)] {
assert_eq!(ztrduppfx(s, *n), dupstrpfx(s, *n));
}
}
#[test]
fn appstr_accumulates_multiple_pushes() {
let mut s = String::from("a");
appstr(&mut s, "b");
appstr(&mut s, "c");
appstr(&mut s, "d");
assert_eq!(s, "abcd");
}
#[test]
fn appstr_empty_append_is_noop() {
let mut s = String::from("hello");
appstr(&mut s, "");
assert_eq!(s, "hello");
}
#[test]
fn strend_empty_returns_empty() {
assert_eq!(strend(""), "");
}
#[test]
fn strend_single_char_returns_self_pin() {
assert_eq!(strend("a"), "a");
}
#[test]
fn strend_returns_last_ascii_char() {
assert_eq!(strend("abc"), "c");
assert_eq!(strend("hello"), "o");
}
#[test]
fn dupstring_wlen_overlong_clamps() {
assert_eq!(dupstring_wlen("hi", 100), "hi");
assert_eq!(dupstring_wlen("", 100), "");
}
#[test]
fn dupstring_returns_owned_independent_copy() {
let src = "hello";
let mut dup = dupstring(src);
dup.push_str("_mut");
assert_eq!(src, "hello", "source unchanged");
assert_eq!(dup, "hello_mut");
}
#[test]
fn dupstring_empty_returns_empty_pin() {
assert_eq!(dupstring(""), "");
}
#[test]
fn ztrdup_empty_returns_empty() {
assert_eq!(ztrdup(""), "");
}
#[test]
fn ztrdup_is_pure() {
for s in ["", "abc", "hello world", "日本"] {
let first = ztrdup(s);
for _ in 0..3 {
assert_eq!(ztrdup(s), first, "ztrdup({:?}) must be pure", s);
}
}
}
#[test]
fn wcs_ztrdup_empty_returns_empty() {
assert_eq!(wcs_ztrdup(""), "");
}
#[test]
fn tricat_all_empty_returns_empty() {
assert_eq!(tricat("", "", ""), "");
}
#[test]
fn tricat_concatenates_three_parts() {
assert_eq!(tricat("a", "b", "c"), "abc");
}
#[test]
fn dyncat_both_empty_returns_empty() {
assert_eq!(dyncat("", ""), "");
}
#[test]
fn bicat_both_empty_returns_empty() {
assert_eq!(bicat("", ""), "");
}
#[test]
fn bicat_concatenates_two_parts() {
assert_eq!(bicat("a", "b"), "ab");
}
#[test]
fn dupstrpfx_empty_zero_len_returns_empty() {
assert_eq!(dupstrpfx("", 0), "");
}
#[test]
fn dupstrpfx_zero_len_returns_empty() {
assert_eq!(dupstrpfx("abc", 0), "");
}
#[test]
fn strend_returns_last_char_slice() {
let s = "abc";
let e = strend(s);
assert_eq!(e.len(), 1, "strend returns 1-char str");
assert_eq!(e.chars().next(), Some('c'));
}
#[test]
fn dupstring_returns_string_type() {
let _: String = dupstring("anything");
}
#[test]
fn ztrdup_returns_string_type() {
let _: String = ztrdup("anything");
}
#[test]
fn tricat_returns_string_type() {
let _: String = tricat("a", "b", "c");
}
#[test]
fn bicat_returns_string_type() {
let _: String = bicat("a", "b");
}
#[test]
fn strend_returns_str_type() {
let s = "abc";
let _: &str = strend(s);
}
#[test]
fn dupstring_is_deterministic() {
for s in ["", "a", "hello world", "café"] {
let first = dupstring(s);
for _ in 0..3 {
assert_eq!(
dupstring(s),
first,
"dupstring({:?}) must be deterministic",
s
);
}
}
}
#[test]
fn tricat_is_deterministic() {
let first = tricat("foo", "bar", "baz");
for _ in 0..3 {
assert_eq!(
tricat("foo", "bar", "baz"),
first,
"tricat must be deterministic"
);
}
}
#[test]
fn zhtricat_matches_tricat_byte_for_byte_pin() {
for (a, b, c) in [
("", "", ""),
("foo", "bar", "baz"),
("x", "", "y"),
("a", "b", ""),
("café", "日", "中"),
] {
assert_eq!(
zhtricat(a, b, c),
tricat(a, b, c),
"zhtricat({:?},{:?},{:?}) must match tricat",
a,
b,
c
);
}
}
#[test]
fn dupstring_wlen_returns_string_type() {
let _: String = dupstring_wlen("hello", 3);
}
#[test]
fn dupstring_wlen_exact_len_returns_whole_string() {
assert_eq!(
dupstring_wlen("hello", 5),
"hello",
"len == s.len() must return whole string"
);
}
#[test]
fn appstr_empty_append_leaves_base_unchanged() {
let mut s = "base".to_string();
appstr(&mut s, "");
assert_eq!(s, "base", "empty append is no-op");
}
#[test]
fn appstr_to_empty_yields_appendage() {
let mut s = String::new();
appstr(&mut s, "added");
assert_eq!(s, "added", "append to empty base = appendage");
}
#[test]
fn ztrdup_returns_string_pin_alt() {
let _: String = ztrdup("");
}
#[test]
fn ztrdup_preserves_content() {
for s in ["", "x", "hello world", "日本語", "café"] {
assert_eq!(ztrdup(s), s, "ztrdup must preserve {:?}", s);
}
}
#[test]
fn wcs_ztrdup_returns_string_type() {
let _: String = wcs_ztrdup("");
}
#[test]
fn dyncat_both_empty_returns_empty_alt() {
assert_eq!(dyncat("", ""), "", "empty + empty → empty");
}
#[test]
fn dyncat_right_identity_empty() {
for s in ["", "x", "hello"] {
assert_eq!(dyncat(s, ""), s, "dyncat({:?}, '') must equal {:?}", s, s);
}
}
#[test]
fn dyncat_left_identity_empty() {
for s in ["", "x", "hello"] {
assert_eq!(dyncat("", s), s, "dyncat('', {:?}) must equal {:?}", s, s);
}
}
#[test]
fn bicat_both_empty_returns_empty_alt() {
assert_eq!(bicat("", ""), "");
}
#[test]
fn bicat_returns_string_pin_alt() {
let _: String = bicat("a", "b");
}
#[test]
fn dupstrpfx_zero_returns_empty() {
for s in ["", "x", "hello"] {
assert_eq!(dupstrpfx(s, 0), "", "dupstrpfx({:?}, 0) must be empty", s);
}
}
#[test]
fn ztrduppfx_zero_returns_empty() {
for s in ["", "x", "hello"] {
assert_eq!(ztrduppfx(s, 0), "");
}
}
#[test]
fn strend_empty_returns_empty_alt() {
assert_eq!(strend(""), "", "empty → empty end");
}
#[test]
fn strend_is_deterministic() {
for s in ["", "x", "abc", "hello"] {
let first = strend(s);
for _ in 0..3 {
assert_eq!(strend(s), first, "strend({:?}) must be pure", s);
}
}
}
}