std/string.zc
Rama main · réplica de lectura del repositorio original.
import "./core.zc"
import "./vec.zc"
import "./option.zc"
fn _z_internal_str_case_cmp(s1: char*, s2: char*) -> int {
let i = 0;
while (true) {
let c1 = (int)((byte)s1[i]);
let c2 = (int)((byte)s2[i]);
// Convert to lowercase for comparison
let lc1 = if (c1 >= (int)'A' && c1 <= (int)'Z') { c1 + 32 } else { c1 };
let lc2 = if (c2 >= (int)'A' && c2 <= (int)'Z') { c2 + 32 } else { c2 };
if (lc1 != lc2) {
return lc1 - lc2;
}
if (c1 == 0) {
break;
}
i = i + 1;
}
return 0;
}
struct String {
vec: Vec<char>;
}
struct StringCharsIter {
data: char*;
len: usize;
pos: usize;
}
impl StringCharsIter {
fn next(self) -> Option<rune> {
if (self.pos >= self.len) {
return Option<rune>::None();
}
let c = (byte)self.data[self.pos];
let seq = String::_utf8_seq_len((char)c);
if (self.pos + seq > self.len) {
self.pos = self.len;
return Option<rune>::None();
}
let val: rune = 0;
if (seq == 1) {
val = (rune)c;
} else if (seq == 2) {
val = (rune)(((int)(c & 0x1F) << 6) | ((int)(byte)self.data[self.pos+1] & 0x3F));
} else if (seq == 3) {
val = (rune)(((int)(c & 0x0F) << 12) | (((int)(byte)self.data[self.pos+1] & 0x3F) << 6) | ((int)(byte)self.data[self.pos+2] & 0x3F));
} else if (seq == 4) {
val = (rune)(((int)(c & 0x07) << 18) | (((int)(byte)self.data[self.pos+1] & 0x3F) << 12) | (((int)(byte)self.data[self.pos+2] & 0x3F) << 6) | ((int)(byte)self.data[self.pos+3] & 0x3F));
}
self.pos = self.pos + seq;
return Option<rune>::Some(val);
}
/// Returns an iterator over the code points.
fn iterator(self) -> StringCharsIter {
return self;
}
}
impl String {
/// Creates a new String from a C string.
fn new(s: char*) -> String {
let len = strlen(s);
let v = Vec<char>::with_capacity(len + 1);
if (len > 0) {
memcpy(v.data, s, len * sizeof(char));
}
v.len = len + 1;
v.data[len] = 0;
let d = v.data;
let l = v.len;
let c = v.cap;
v.forget();
return String { vec: Vec<char> { data: d, len: l, cap: c } };
}
/// Creates a new String from a C string (alias for `new`).
fn from(s: char*) -> String {
return String::new(s);
}
/// Creates a String from a single rune.
fn from_rune(r: rune) -> String {
let s = String::new("");
s.push_rune(r);
return s;
}
/// Creates a String from an array of runes.
fn from_runes(runes: rune*, count: usize) -> String {
let s = String::new("");
for (let i: usize = 0; i < count; i = i + 1) {
s.push_rune(runes[i]);
}
return s;
}
/// Returns the underlying NUL-terminated C string.
fn c_str(self) -> char* {
return self.vec.data;
}
/// Returns a `char*` view of the string.
fn to_string(self) -> char* {
return self.c_str();
}
/// Returns an independent copy with its own buffer.
fn clone(self) -> String {
return String { vec: self.vec.clone() };
}
/// Frees the string memory (deprecated; use `free`).
fn destroy(self) {
self.free();
}
/// Detaches the internal buffer without freeing it.
fn forget(self) {
self.vec.forget();
}
/// Appends another String in place.
fn append(self, other: String*) {
if (self.vec.len > 0) {
self.vec.len = self.vec.len - 1;
}
let other_len = (*other).vec.len;
let new_len = self.vec.len + other_len;
self.vec.grow_to_fit(new_len);
memcpy(self.vec.data + self.vec.len, (*other).vec.data, other_len * sizeof(char));
self.vec.len = new_len;
}
/// Appends a C string in place.
fn append_c(self, s: char*) {
if (self.vec.len > 0) {
self.vec.len = self.vec.len - 1;
}
let slen = strlen(s);
let new_len = self.vec.len + slen + 1;
self.vec.grow_to_fit(new_len);
memcpy(self.vec.data + self.vec.len, s, slen * sizeof(char));
self.vec.len = new_len;
self.vec.data[new_len - 1] = 0;
}
/// Appends a rune (UTF-8 encoded).
fn push_rune(self, r: rune) {
if (self.vec.len > 0) {
self.vec.len = self.vec.len - 1;
}
let val = (uint32_t)r;
if (val < 0x80) {
self.vec.push((char)val);
} else if (val < 0x800) {
self.vec.push((char)(0xC0 | (val >> 6)));
self.vec.push((char)(0x80 | (val & 0x3F)));
} else if (val < 0x10000) {
self.vec.push((char)(0xE0 | (val >> 12)));
self.vec.push((char)(0x80 | ((val >> 6) & 0x3F)));
self.vec.push((char)(0x80 | (val & 0x3F)));
} else {
self.vec.push((char)(0xF0 | (val >> 18)));
self.vec.push((char)(0x80 | ((val >> 12) & 0x3F)));
self.vec.push((char)(0x80 | ((val >> 6) & 0x3F)));
self.vec.push((char)(0x80 | (val & 0x3F)));
}
self.vec.push(0);
}
fn append_c_ptr(ptr: String*, s: char*) {
if (ptr.vec.len > 0) {
ptr.vec.len = ptr.vec.len - 1;
}
let slen = strlen(s);
let new_len = ptr.vec.len + slen + 1;
ptr.vec.grow_to_fit(new_len);
memcpy(ptr.vec.data + ptr.vec.len, s, slen * sizeof(char));
ptr.vec.len = new_len;
ptr.vec.data[new_len - 1] = 0;
}
/// Returns a new String concatenating self and other.
fn add(self, other: String*) -> String {
let new_s = String::from(self.c_str());
(&new_s).append(other);
let d = new_s.vec.data;
let l = new_s.vec.len;
let c = new_s.vec.cap;
new_s.forget();
return String { vec: Vec<char> { data: d, len: l, cap: c } };
}
/// Appends other in place.
fn add_assign(self, other: String*) {
self.append(other);
}
/// Returns whether this String equals another.
fn eq(self, other: String*) -> bool {
let zero: c_int = 0;
return strcmp(self.c_str(), (*other).c_str()) == zero;
}
/// Returns whether this String differs from another.
fn neq(self, other: String*) -> bool {
return !self.eq(other);
}
/// Lexicographic comparison: returns <0, 0, or >0.
fn compare(self, other: String*) -> int {
return strcmp(self.c_str(), (*other).c_str());
}
/// Returns whether this String is less than another.
fn lt(self, other: String*) -> bool {
return self.compare(other) < 0;
}
/// Returns whether this String is greater than another.
fn gt(self, other: String*) -> bool {
return self.compare(other) > 0;
}
/// Returns whether this String is <= another.
fn le(self, other: String*) -> bool {
return self.compare(other) <= 0;
}
/// Returns whether this String is >= another.
fn ge(self, other: String*) -> bool {
return self.compare(other) >= 0;
}
/// Case-insensitive lexicographic comparison.
fn compare_ignore_case(self, other: String*) -> int {
return _z_internal_str_case_cmp(self.vec.data, (*other).vec.data);
}
/// Case-insensitive equality.
fn eq_ignore_case(self, other: String*) -> bool {
let zero: int = 0;
return self.compare_ignore_case(other) == zero;
}
/// Returns whether this String equals a C string.
fn eq_str(self, s: char*) -> bool {
let zero: c_int = 0;
return strcmp(self.c_str(), s) == zero;
}
/// Returns the length in bytes (not code points).
fn length(self) -> usize {
if (self.vec.len == 0) { return 0; }
return self.vec.len - 1;
}
/// Returns a new String of `len` bytes starting at `start`.
fn substring(self, start: usize, len: usize) -> String {
if (start + len > self.length()) {
panic("substring out of bounds");
}
let v = Vec<char>::with_capacity(len + 1);
if (len > 0) {
memcpy(v.data, self.vec.data + start, len * sizeof(char));
}
v.len = len + 1;
v.data[len] = 0;
let d = v.data;
let l = v.len;
let c = v.cap;
v.forget();
return String { vec: Vec<char> { data: d, len: l, cap: c } };
}
/// Returns whether the substring `target` occurs.
fn contains_str(self, target: char*) -> bool {
return self.find_str(target).is_some();
}
/// Returns the first index of `target`, or `None`.
fn find_str(self, target: char*) -> Option<usize> {
let t_len = strlen(target);
if t_len == 0 { return Option<usize>::Some(0); }
let s_len = self.length();
if t_len > s_len { return Option<usize>::None(); }
for (let i: usize = 0; i <= s_len - t_len; i = i + 1) {
let is_match = true;
for (let k: usize = 0; k < t_len; k = k + 1) {
if self.vec.get(i + k) != target[k] {
is_match = false;
break;
}
}
if is_match { return Option<usize>::Some(i); }
}
return Option<usize>::None();
}
/// Returns all indices where `target` occurs.
fn find_all_str(self, target: char*) -> Vec<usize> {
let indices = Vec<usize>::new();
let t_len = strlen(target);
if t_len == 0 { return indices; }
let s_len = self.length();
if t_len > s_len { return indices; }
for (let i: usize = 0; i <= s_len - t_len; i = i + 1) {
let is_match = true;
for (let k: usize = 0; k < t_len; k = k + 1) {
if self.vec.get(i + k) != target[k] {
is_match = false;
break;
}
}
if is_match {
indices.push(i);
// Optional: i = i + t_len - 1; (Un-comment to avoid overlapping matches)
}
}
return indices;
}
/// Returns a new lowercase copy.
fn to_lowercase(self) -> String {
let len = self.length();
let v = Vec<char>::with_capacity(len + 1);
for (let i: usize = 0; i < len; i = i + 1) {
let c = self.vec.get(i);
if (c >= 'A' && c <= 'Z') {
v.push((char)((int)c + 32));
} else {
v.push(c);
}
}
v.push(0);
let d = v.data;
let l = v.len;
let c_cap = v.cap;
v.forget();
return String { vec: Vec<char> { data: d, len: l, cap: c_cap } };
}
/// Returns a new string padded on the right to `target_len`.
fn pad_right(self, target_len: usize, pad_char: char) -> String {
let current_len = self.length();
if (current_len >= target_len) {
return String::from(self.c_str());
}
let v = Vec<char>::with_capacity(target_len + 1);
if (current_len > 0) {
memcpy(v.data, self.vec.data, current_len * sizeof(char));
}
v.len = current_len;
for (let i: usize = current_len; i < target_len; i = i + 1) {
v.push(pad_char);
}
v.push(0);
let d = v.data;
let l = v.len;
let c_cap = v.cap;
v.forget();
return String { vec: Vec<char> { data: d, len: l, cap: c_cap } };
}
/// Returns a new string padded on the left to `target_len`.
fn pad_left(self, target_len: usize, pad_char: char) -> String {
let current_len = self.length();
if (current_len >= target_len) {
return String::from(self.c_str());
}
let v = Vec<char>::with_capacity(target_len + 1);
let diff = target_len - current_len;
for (let i: usize = 0; i < diff; i = i + 1) {
v.push(pad_char);
}
if (current_len > 0) {
memcpy(v.data + diff, self.vec.data, current_len * sizeof(char));
}
v.len = v.len + current_len;
v.push(0);
let d = v.data;
let l = v.len;
let c_cap = v.cap;
v.forget();
return String { vec: Vec<char> { data: d, len: l, cap: c_cap } };
}
/// Returns a new uppercase copy.
fn to_uppercase(self) -> String {
let len = self.length();
let v = Vec<char>::with_capacity(len + 1);
for (let i: usize = 0; i < len; i = i + 1) {
let c = self.vec.get(i);
if (c >= 'a' && c <= 'z') {
v.push((char)((int)c - 32));
} else {
v.push(c);
}
}
v.push(0);
let d = v.data;
let l = v.len;
let c_cap = v.cap;
v.forget();
return String { vec: Vec<char> { data: d, len: l, cap: c_cap } };
}
/// Returns the first index of the character `target`, or `None`.
fn find(self, target: char) -> Option<usize> {
let len = self.length();
for (let i: usize = 0; i < len; i = i + 1) {
if (self.vec.get(i) == target) {
return Option<usize>::Some(i);
}
}
return Option<usize>::None();
}
/// Prints the string to stdout.
fn print(self) {
printf("%s", self.c_str());
fflush(stdout);
}
/// Prints the string to stdout followed by a newline.
fn println(self) {
printf("%s\n", self.c_str());
}
/// Returns whether the string is empty.
fn is_empty(self) -> bool {
return self.length() == 0;
}
/// Returns whether the character `target` occurs.
fn contains(self, target: char) -> bool {
return self.find(target).is_some();
}
/// Returns whether the string starts with `prefix`.
fn starts_with(self, prefix: char*) -> bool {
let plen = strlen(prefix);
if plen > self.length() { return false; }
let zero: c_int = 0;
return strncmp(self.c_str(), prefix, plen) == zero;
}
/// Returns whether the string ends with `suffix`.
fn ends_with(self, suffix: char*) -> bool {
let slen = strlen(suffix);
let len = self.length();
if slen > len { return false; }
let offset = (int)(len - slen);
let zero: c_int = 0;
return strcmp(self.c_str() + offset, suffix) == zero;
}
/// Ensures capacity for at least `cap` bytes.
fn reserve(self, cap: usize) {
self.vec.grow_to_fit(cap + 1);
}
/// Frees the string memory.
fn free(self) {
self.vec.free();
}
fn _utf8_seq_len(first_byte: char) -> usize {
let b = (int)first_byte;
if ((b & 0x80) == 0) { return 1; }
if ((b & 0xE0) == 0xC0) { return 2; }
if ((b & 0xF0) == 0xE0) { return 3; }
if ((b & 0xF8) == 0xF0) { return 4; }
return 1; // Fallback
}
/// Returns the number of Unicode code points.
fn utf8_len(self) -> usize {
let count: usize = 0;
let i: usize = 0;
let len = self.length();
while i < len {
let c = self.vec.get(i);
i = i + String::_utf8_seq_len(c);
count = count + 1;
}
return count;
}
/// Returns the code point at byte index `idx` as a String.
fn utf8_at(self, idx: usize) -> String {
let count: usize = 0;
let i: usize = 0;
let len = self.length();
while i < len {
let c = self.vec.get(i);
let seq = String::_utf8_seq_len(c);
if (count == idx) {
return self.substring(i, seq);
}
i = i + seq;
count = count + 1;
}
return String::new("");
}
/// Returns the code point at byte index `idx` as a rune.
fn utf8_get(self, idx: usize) -> rune {
let count: usize = 0;
let i: usize = 0;
let len = self.length();
while i < len {
let c = (byte)self.vec.get(i);
let seq = String::_utf8_seq_len((char)c);
if (count == idx) {
if (seq == 1) { return (rune)c; }
if (seq == 2) { return (rune)(((int)(c & 0x1F) << 6) | ((int)(byte)self.vec.get(i + 1) & 0x3F)); }
if (seq == 3) { return (rune)(((int)(c & 0x0F) << 12) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 2) & 0x3F)); }
if (seq == 4) { return (rune)(((int)(c & 0x07) << 18) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 12) | (((int)(byte)self.vec.get(i + 2) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 3) & 0x3F)); }
}
i = i + seq;
count = count + 1;
}
return (rune)0;
}
/// Returns a Vec of the code points.
fn runes(self) -> Vec<rune> {
let v = Vec<rune>::new();
let i: usize = 0;
let len = self.length();
while i < len {
let c = (byte)self.vec.get(i);
let seq = String::_utf8_seq_len((char)c);
let val: rune = 0;
if (seq == 1) {
val = (rune)c;
} else if (seq == 2) {
val = (rune)(((int)(c & 0x1F) << 6) | ((int)(byte)self.vec.get(i + 1) & 0x3F));
} else if (seq == 3) {
val = (rune)(((int)(c & 0x0F) << 12) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 2) & 0x3F));
} else if (seq == 4) {
val = (rune)(((int)(c & 0x07) << 18) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 12) | (((int)(byte)self.vec.get(i + 2) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 3) & 0x3F));
}
v.push(val);
i = i + seq;
}
return v;
}
/// Returns an iterator over the code points.
fn iterator(self) -> StringCharsIter {
return self.chars();
}
/// Returns an iterator over the code points.
fn chars(self) -> StringCharsIter {
return StringCharsIter {
data: self.c_str(),
len: self.length(),
pos: 0
};
}
/// Creates a String from a Vec of runes.
fn from_runes_vec(runes: Vec<rune>) -> String {
let s = String::new("");
for (let i: usize = 0; i < runes.len; i = i + 1) {
s.push_rune(runes.get(i));
}
return s;
}
/// Inserts a rune at byte index `idx`.
fn insert_rune(self, idx: usize, r: rune) {
let i: usize = 0;
let count: usize = 0;
let len = self.length();
while i < len && count < idx {
i = i + String::_utf8_seq_len(self.vec.get(i));
count = count + 1;
}
let val = (uint32_t)r;
if (val < 0x80) {
self.vec.insert(i, (char)val);
} else if (val < 0x800) {
self.vec.insert(i, (char)(0xC0 | (val >> 6)));
self.vec.insert(i + 1, (char)(0x80 | (val & 0x3F)));
} else if (val < 0x10000) {
self.vec.insert(i, (char)(0xE0 | (val >> 12)));
self.vec.insert(i + 1, (char)(0x80 | ((val >> 6) & 0x3F)));
self.vec.insert(i + 2, (char)(0x80 | (val & 0x3F)));
} else {
self.vec.insert(i, (char)(0xF0 | (val >> 18)));
self.vec.insert(i + 1, (char)(0x80 | ((val >> 12) & 0x3F)));
self.vec.insert(i + 2, (char)(0x80 | ((val >> 6) & 0x3F)));
self.vec.insert(i + 3, (char)(0x80 | (val & 0x3F)));
}
}
/// Removes and returns the rune at byte index `idx`.
fn remove_rune_at(self, idx: usize) -> rune {
let i: usize = 0;
let count: usize = 0;
let len = self.length();
while i < len && count < idx {
i = i + String::_utf8_seq_len(self.vec.get(i));
count = count + 1;
}
if (i >= len) {
return (rune)0;
}
let c = (byte)self.vec.get(i);
let seq = String::_utf8_seq_len((char)c);
let val: rune = 0;
if (seq == 1) { val = (rune)c; }
else if (seq == 2) { val = (rune)(((int)(c & 0x1F) << 6) | ((int)(byte)self.vec.get(i + 1) & 0x3F)); }
else if (seq == 3) { val = (rune)(((int)(c & 0x0F) << 12) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 2) & 0x3F)); }
else if (seq == 4) { val = (rune)(((int)(c & 0x07) << 18) | (((int)(byte)self.vec.get(i + 1) & 0x3F) << 12) | (((int)(byte)self.vec.get(i + 2) & 0x3F) << 6) | ((int)(byte)self.vec.get(i + 3) & 0x3F)); }
for (let j: usize = 0; j < seq; j = j + 1) {
self.vec.remove(i);
}
return val;
}
/// Returns a substring of `num_chars` code points from `start_idx`.
fn utf8_substr(self, start_idx: usize, num_chars: usize) -> String {
if (num_chars == 0) { return String::new(""); }
let byte_start: usize = 0;
let byte_len: usize = 0;
let count: usize = 0;
let i: usize = 0;
let len = self.length();
let found_start = false;
while i < len {
// Check if we reached the start char
if (!found_start && count == start_idx) {
byte_start = i;
found_start = true;
// Reset count to track chars collected
count = 0;
} else if (!found_start) {
// Still seeking start
let c = self.vec.get(i);
i = i + String::_utf8_seq_len(c);
count = count + 1;
continue;
}
// If we are here, we are collecting chars
if (count < num_chars) {
let c = self.vec.get(i);
let seq = String::_utf8_seq_len(c);
byte_len = byte_len + seq;
i = i + seq;
count = count + 1;
} else {
break;
}
}
if (!found_start) { return String::new(""); }
return self.substring(byte_start, byte_len);
}
/// Splits on the delimiter character into a Vec of substrings.
fn split(self, delim: char) -> Vec<String> {
let parts = Vec<String>::new();
let len = self.length();
if (len == 0) { return parts; }
let start: usize = 0;
let i: usize = 0;
while i < len {
if (self.vec.get(i) == delim) {
// Found delimiter
parts.push(self.substring(start, i - start));
start = i + 1;
}
i = i + 1;
}
// Push last segment
if (start <= len) {
parts.push(self.substring(start, len - start));
}
return parts;
}
/// Returns a copy with leading/trailing whitespace removed.
fn trim(self) -> String {
let start: usize = 0;
let len = self.length();
let end = len;
// Find start
while (start < len) {
let c = self.vec.get(start);
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
break;
}
start = start + 1;
}
if (start == len) {
return String::new("");
}
// Find end
while (end > start) {
let c = self.vec.get(end - 1);
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
break;
}
end = end - 1;
}
return self.substring(start, end - start);
}
/// Returns a copy with all `target` replaced by `replacement`.
fn replace(self, target: char*, replacement: char*) -> String {
let t_len = strlen(target);
if (t_len == 0) {
return self.substring(0, self.length()); // clone
}
let s_len = self.length();
let result = String::new("");
let i: usize = 0;
while (i < s_len) {
// Check if match
if (i + t_len <= s_len) {
let is_match = true;
// Manual strncmp against vec data
for (let k: usize = 0; k < t_len; k = k + 1) {
if (self.vec.get(i + k) != target[k]) {
is_match = false;
break;
}
}
if (is_match) {
let r_str = String::new(replacement);
result.append(&r_str);
i = i + t_len;
continue;
}
}
// Append single char
let v = Vec<char>::new();
v.push(self.vec.get(i));
v.push(0);
let ch_s = String::new(v.data);
result.append(&ch_s);
ch_s.free();
i = i + 1;
}
return result;
}
// Parses the string as a decimal integer. Returns 0 when the string is
// not a valid integer.
/// Parses the string as a decimal integer (0 when invalid).
fn to_int(self) -> int {
let len = self.length();
if (len == 0) { return 0; }
let i: usize = 0;
let neg = false;
if (self.vec.get(0) == '-') {
neg = true;
i = 1;
} else if (self.vec.get(0) == '+') {
i = 1;
}
if (i >= len) { return 0; }
let result: int = 0;
while (i < len) {
let c = self.vec.get(i);
if (c < '0' || c > '9') { return 0; }
result = result * 10 + (int)(c - '0');
i = i + 1;
}
if (neg) { return -result; }
return result;
}
// Returns a new string with this string repeated `count` times.
/// Returns a copy repeated `count` times.
fn repeated(self, count: usize) -> String {
let len = self.length();
let out = String::new("");
if (count == 0 || len == 0) { return out; }
out.reserve(len * count);
for (let i: usize = 0; i < count; i = i + 1) {
out.append(self);
}
return out;
}
// Returns a new string with the characters in reverse order.
/// Returns a copy with the characters reversed.
fn reverse(self) -> String {
let len = self.length();
let out = String::new("");
if (len == 0) { return out; }
out.reserve(len);
let i = len;
while (i > 0) {
i = i - 1;
let v = Vec<char>::new();
v.push(self.vec.get(i));
v.push(0);
let ch_s = String::new(v.data);
out.append(&ch_s);
ch_s.free();
}
return out;
}
// Returns a copy with leading whitespace removed.
/// Returns a copy with leading whitespace removed.
fn trim_start(self) -> String {
let len = self.length();
let start: usize = 0;
while (start < len) {
let c = self.vec.get(start);
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
break;
}
start = start + 1;
}
if (start == len) { return String::new(""); }
return self.substring(start, len - start);
}
// Returns a copy with trailing whitespace removed.
/// Returns a copy with trailing whitespace removed.
fn trim_end(self) -> String {
let len = self.length();
let end = len;
while (end > 0) {
let c = self.vec.get(end - 1);
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
break;
}
end = end - 1;
}
return self.substring(0, end);
}
}