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(""), "");
}
}