Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/development/std/str/transform.nct

transform.nct

//! Owned transformations over borrowed UTF-8 text.

see ./index.nct
see ./search.nct
see ./views.nct

use /internal/mem as internal_mem
use /string.String

func repeat(text: &str, count: usize): String {
    if count == 0 || text.len() == 0 {
        return String.empty()
    }
    let required_capacity = internal_mem.checked_multiply(text.len(), count) otherwise {
        return internal_mem.allocation_abort()
    }
    var result = String.with_capacity(required_capacity)
    var index: usize = 0
    while index < count {
        result.push_str(text)
        index += 1
    }
    return move result
}

noalloc func replacement_capacity(
    text: &str,
    pattern: &str,
    replacement: &str,
): usize {
    var capacity: usize = text.len()
    var search_start: usize = 0
    while search_start <= text.len() {
        let match_start = find_from(text, pattern, search_start) otherwise {
            return capacity
        }
        if replacement.len() >= pattern.len() {
            let growth = replacement.len() - pattern.len()
            capacity = internal_mem.checked_add(capacity, growth) otherwise {
                return internal_mem.allocation_abort()
            }
        } else {
            capacity -= pattern.len() - replacement.len()
        }
        search_start = match_start + pattern.len()
    }
    return capacity
}

func replace_all(text: &str, pattern: &str, replacement: &str): String! {
    if pattern.len() == 0 {
        return error.new(
            "std.str.empty_pattern",
            "replacement pattern must not be empty",
        )
    }

    let capacity = replacement_capacity(text, pattern, replacement)
    var result = String.with_capacity(capacity)
    var copied_until: usize = 0
    while copied_until <= text.len() {
        let match_start = find_from(text, pattern, copied_until) otherwise {
            result.push_str(str_subview_unchecked(
                text,
                copied_until,
                text.len() - copied_until,
            ))
            return move result
        }
        result.push_str(str_subview_unchecked(
            text,
            copied_until,
            match_start - copied_until,
        ))
        result.push_str(replacement)
        copied_until = match_start + pattern.len()
    }
    return move result
}

instance str {
    method &self.repeat(count: usize): String {
        return repeat(self, count)
    }

    method &self.replace_all(pattern: &str, replacement: &str): String! {
        return replace_all(self, pattern, replacement)?
    }
}