// SafeC Standard Library — String implementation
#pragma once
#include <std/collections/string.h>
#include <std/mem.h>
#include <std/str.h>
#include <std/fmt.h>
#include <std/convert.h>
// ── Internal: ensure at least `need` bytes of capacity (not counting NUL) ─────
namespace std {
inline int String::reserve_(unsigned long need) {
unsigned long needed_cap = need + 1UL;
if (needed_cap <= self.cap) { return 1; }
unsigned long new_cap = self.cap < 16UL ? 16UL : self.cap;
while (new_cap < needed_cap) { new_cap = new_cap * 2UL; }
unsafe {
unsigned char* raw = realloc_buf_raw_((unsigned char*)self.data, new_cap);
if (raw == (unsigned char*)0) { return 0; }
self.data = (&heap char)raw;
self.cap = new_cap;
}
return 1;
}
// ── Lifecycle ─────────────────────────────────────────────────────────────────
inline struct String String::new() {
struct String s;
s.len = 0UL;
unsafe {
unsigned char* raw = alloc_raw_(16UL);
if (raw == (unsigned char*)0) {
s.data = (&heap char)0;
s.cap = 0UL;
return s;
}
s.data = (&heap char)raw;
}
unsafe { s.data[0] = (char)0; }
s.cap = 16UL;
return s;
}
inline struct String String::from(const &char cstr) {
struct String s = String::new();
if (cstr != (const char*)0) { s.push(cstr); }
return s;
}
inline struct String String::from_n_(const &char cstr, unsigned long n) {
struct String s;
s.len = 0UL;
unsafe {
unsigned char* raw = alloc_raw_(n + 1UL);
if (raw == (unsigned char*)0) {
s.data = (&heap char)0;
s.cap = 0UL;
return s;
}
s.data = (&heap char)raw;
s.cap = n + 1UL;
safe_memcpy_raw_((unsigned char*)s.data, (const unsigned char*)cstr, n);
}
unsafe { s.data[n] = (char)0; }
s.len = n;
return s;
}
inline struct String String::with_cap(unsigned long cap) {
struct String s;
s.len = 0UL;
s.cap = cap + 1UL;
unsafe {
unsigned char* raw = alloc_raw_(s.cap);
if (raw == (unsigned char*)0) {
s.data = (&heap char)0;
s.cap = 0UL;
return s;
}
s.data = (&heap char)raw;
}
unsafe { s.data[0] = (char)0; }
return s;
}
inline struct String String::repeat_str(const &char cstr, unsigned long n) {
unsigned long slen = str_len(cstr);
unsigned long total = slen * n;
struct String s = String::with_cap(total);
unsigned long i = 0UL;
while (i < n) { s.push(cstr); i = i + 1UL; }
return s;
}
inline struct String String::join(const ?&char sep, []String parts) {
struct String out = String::new();
unsigned long i = 0UL;
while (i < parts.len) {
if (i > 0UL) {
match (sep) { case some(s): out.push(s); case null: { } }
}
out.push_str(&parts[i]);
i = i + 1UL;
}
return out;
}
inline struct String String::from_bytes([]const char bytes) {
if (bytes.len == 0UL) return String::new();
unsafe { return String::from_n_((const char*)bytes.ptr, bytes.len); }
}
inline int String::push_bytes([]const char bytes) {
if (bytes.len == 0UL) return 1;
unsafe { return self.push_n_((const char*)bytes.ptr, bytes.len); }
}
inline []const char String::as_bytes() const {
unsafe { return ((const char*)self.data)[0..self.len]; }
}
// kept for std/ml until its refactor moves to the static method
inline struct String string_new() { return String::new(); }
inline struct String string_from(const &char cstr) { return String::from(cstr); }
inline struct String string_from_n_(const &char cstr, unsigned long n) { return String::from_n_(cstr, n); }
inline struct String String::clone() const {
unsafe { return String::from_n_((const char*)self.data, self.len); }
}
inline void String::free() { self.drop(); }
inline void String::drop() {
unsafe {
if (self.cap > 0UL) { dealloc(self.data); }
self.data = (&heap char)0;
}
self.len = 0UL;
self.cap = 0UL;
}
// ── Access ────────────────────────────────────────────────────────────────────
unsigned long String::length() const { return self.len; }
int String::is_empty() const { return self.len == 0UL; }
inline const &char String::as_ptr() const {
return self.data;
}
inline int String::char_at(unsigned long idx) const {
if (idx >= self.len) { return -1; }
unsafe { return (int)(unsigned char)self.data[idx]; }
}
inline void String::set_char(unsigned long idx, char c) {
if (idx < self.len) { unsafe { self.data[idx] = c; } }
}
// ── Capacity ──────────────────────────────────────────────────────────────────
inline int String::reserve(unsigned long additional) {
return self.reserve_(self.len + additional);
}
inline void String::shrink_to_fit() {
if (self.len + 1UL >= self.cap) { return; }
unsigned long new_cap = self.len + 1UL;
unsafe {
unsigned char* raw = realloc_buf_raw_((unsigned char*)self.data, new_cap);
if (raw == (unsigned char*)0) { return; }
self.data = (&heap char)raw;
self.cap = new_cap;
}
}
// ── Append ────────────────────────────────────────────────────────────────────
inline int String::push_char(char c) {
if (!self.reserve_(self.len + 1UL)) { return 0; }
unsafe { self.data[self.len] = c; }
self.len = self.len + 1UL;
unsafe { self.data[self.len] = (char)0; }
return 1;
}
inline int String::push(const &char cstr) {
if (cstr == (const char*)0) { return 1; }
unsigned long clen = str_len(cstr);
if (!self.reserve_(self.len + clen)) { return 0; }
unsafe { safe_memcpy_raw_((unsigned char*)((char*)self.data + self.len), (const unsigned char*)cstr, clen + 1UL); }
self.len = self.len + clen;
return 1;
}
inline int String::push_n_(const &char data, unsigned long n) {
if (n == 0UL) { return 1; }
if (!self.reserve_(self.len + n)) { return 0; }
unsafe { safe_memcpy_raw_((unsigned char*)((char*)self.data + self.len), (const unsigned char*)data, n); }
self.len = self.len + n;
unsafe { self.data[self.len] = (char)0; }
return 1;
}
inline int String::push_str(&String other) {
unsafe { return self.push((const char*)other.data); }
}
inline int String::push_int(long long v) {
char buf[32];
fmt_int(buf[0..32], v);
return self.push(&buf[0]);
}
inline int String::push_uint(unsigned long long v) {
char buf[32];
fmt_uint(buf[0..32], v);
return self.push(&buf[0]);
}
inline int String::push_float(double v, int decimals) {
char buf[64];
fmt_float(buf[0..64], v, decimals);
return self.push(&buf[0]);
}
inline int String::push_bool(int v) {
return self.push(v ? "true" : "false");
}
// ── Modification ──────────────────────────────────────────────────────────────
inline void String::clear() {
self.len = 0UL;
if (self.cap > 0UL) { unsafe { self.data[0] = (char)0; } }
}
inline void String::truncate(unsigned long new_len) {
if (new_len >= self.len) { return; }
self.len = new_len;
if (self.cap > 0UL) { unsafe { self.data[new_len] = (char)0; } }
}
inline int String::insert(unsigned long idx, const &char cstr) {
if (idx > self.len) { idx = self.len; }
unsigned long clen = str_len(cstr);
if (!self.reserve_(self.len + clen)) { return 0; }
unsafe {
safe_memmove_raw_((unsigned char*)((char*)self.data + idx + clen),
(const unsigned char*)((char*)self.data + idx), self.len - idx + 1UL);
safe_memcpy_raw_((unsigned char*)((char*)self.data + idx), (const unsigned char*)cstr, clen);
}
self.len = self.len + clen;
return 1;
}
inline int String::remove_range(unsigned long start, unsigned long end) {
if (start >= self.len || start >= end) { return 0; }
if (end > self.len) { end = self.len; }
unsigned long removed = end - start;
unsafe {
safe_memmove_raw_((unsigned char*)((char*)self.data + start),
(const unsigned char*)((char*)self.data + end), self.len - end + 1UL);
}
self.len = self.len - removed;
return 1;
}
inline void String::replace_char(char from, char to) {
unsigned long i = 0UL;
while (i < self.len) {
unsafe { if (self.data[i] == from) { self.data[i] = to; } }
i = i + 1UL;
}
}
inline int String::replace(const &char from, const &char to) {
long long pos = self.index_of(from);
if (pos < 0LL) { return 0; }
unsigned long from_len = str_len(from);
self.remove_range((unsigned long)pos, (unsigned long)pos + from_len);
self.insert((unsigned long)pos, to);
return 1;
}
// Single left-to-right pass into a freshly-sized buffer — O(n). The
// previous implementation called index_of() (a full O(n) scan from the
// *start* of the string, not from the current position) once per match,
// then shifted the whole remaining tail via remove_range()+insert() for
// each one — O(matches * n) overall, quadratic for a string with many
// matches.
inline int String::replace_all(const &char from, const &char to) {
unsigned long from_len = str_len(from);
if (from_len == 0UL) { return 0; }
unsigned long to_len = str_len(to);
// Count non-overlapping matches first, so the replacement buffer can
// be allocated exactly once at its final size.
int cnt = 0;
unsigned long i = 0UL;
unsafe {
while (i + from_len <= self.len) {
if (safe_memcmp_raw_((const unsigned char*)((char*)self.data + i), (const unsigned char*)from, from_len) == 0) {
cnt = cnt + 1;
i = i + from_len;
} else {
i = i + 1UL;
}
}
}
if (cnt == 0) { return 0; }
unsigned long newLen = self.len - (unsigned long)cnt * from_len + (unsigned long)cnt * to_len;
unsafe {
char* newBuf = (char*)alloc_raw_(newLen + 1UL);
if (newBuf == (char*)0) { return 0; }
unsigned long src = 0UL;
unsigned long dst = 0UL;
while (src < self.len) {
if (src + from_len <= self.len &&
safe_memcmp_raw_((const unsigned char*)((char*)self.data + src), (const unsigned char*)from, from_len) == 0) {
if (to_len > 0UL) {
safe_memcpy_raw_((unsigned char*)(newBuf + dst), (const unsigned char*)to, to_len);
}
dst = dst + to_len;
src = src + from_len;
} else {
newBuf[dst] = self.data[src];
dst = dst + 1UL;
src = src + 1UL;
}
}
newBuf[dst] = (char)0;
if (self.data != (&heap char)0) { dealloc(self.data); }
self.data = (&heap char)newBuf;
self.len = newLen;
self.cap = newLen + 1UL;
}
return cnt;
}
inline void String::reverse() {
if (self.len < 2UL) { return; }
unsigned long lo = 0UL;
unsigned long hi = self.len - 1UL;
while (lo < hi) {
unsafe {
char tmp = self.data[lo];
self.data[lo] = self.data[hi];
self.data[hi] = tmp;
}
lo = lo + 1UL;
hi = hi - 1UL;
}
}
inline int String::pop_char() {
if (self.len == 0UL) { return -1; }
self.len = self.len - 1UL;
unsafe {
int c = (int)(unsigned char)self.data[self.len];
self.data[self.len] = (char)0;
return c;
}
}
// ── Search ────────────────────────────────────────────────────────────────────
inline long long String::index_of(const &char needle) const {
if (needle == (const char*)0 || self.cap == 0UL) { return -1LL; }
unsafe {
const char* found = str_find((const char*)self.data, needle);
if (found == (const char*)0) { return -1LL; }
return (long long)(found - (const char*)self.data);
}
}
inline long long String::last_index_of(const &char needle) const {
if (needle == (const char*)0 || self.cap == 0UL) { return -1LL; }
unsigned long nlen = str_len(needle);
if (nlen == 0UL) { return (long long)self.len; }
long long result = -1LL;
unsigned long i = 0UL;
while (i + nlen <= self.len) {
unsafe {
if (safe_memcmp_raw_((const unsigned char*)((char*)self.data + i), (const unsigned char*)needle, nlen) == 0) {
result = (long long)i;
}
}
i = i + 1UL;
}
return result;
}
inline int String::contains(const &char needle) const {
return self.index_of(needle) >= 0LL;
}
inline int String::starts_with(const &char prefix) const {
unsigned long plen = str_len(prefix);
if (plen > self.len) { return 0; }
unsafe { return safe_memcmp_raw_((const unsigned char*)self.data, (const unsigned char*)prefix, plen) == 0; }
}
inline int String::ends_with(const &char suffix) const {
unsigned long slen = str_len(suffix);
if (slen > self.len) { return 0; }
unsafe {
return safe_memcmp_raw_((const unsigned char*)((char*)self.data + self.len - slen),
(const unsigned char*)suffix, slen) == 0;
}
}
inline int String::count(const &char needle) const {
unsigned long nlen = str_len(needle);
if (nlen == 0UL) { return 0; }
int cnt = 0;
unsigned long i = 0UL;
while (i + nlen <= self.len) {
unsafe {
if (safe_memcmp_raw_((const unsigned char*)((char*)self.data + i), (const unsigned char*)needle, nlen) == 0) {
cnt = cnt + 1;
i = i + nlen;
} else {
i = i + 1UL;
}
}
}
return cnt;
}
inline long long String::find_char(char c) const {
unsigned long i = 0UL;
while (i < self.len) {
unsafe { if (self.data[i] == c) { return (long long)i; } }
i = i + 1UL;
}
return -1LL;
}
inline long long String::rfind_char(char c) const {
if (self.len == 0UL) { return -1LL; }
unsigned long i = self.len;
while (i > 0UL) {
i = i - 1UL;
unsafe { if (self.data[i] == c) { return (long long)i; } }
}
return -1LL;
}
// ── Transformation ────────────────────────────────────────────────────────────
inline struct String String::substr(unsigned long start, unsigned long end) const {
if (start >= self.len) { return String::new(); }
if (end > self.len) { end = self.len; }
if (start >= end) { return String::new(); }
unsafe { return String::from_n_((const char*)((char*)self.data + start), end - start); }
}
inline struct String String::to_upper() const {
struct String out = self.clone();
unsigned long i = 0UL;
while (i < out.len) {
unsafe {
char c = out.data[i];
if (c >= 'a' && c <= 'z') { out.data[i] = (char)(c - (char)32); }
}
i = i + 1UL;
}
return out;
}
inline struct String String::to_lower() const {
struct String out = self.clone();
unsigned long i = 0UL;
while (i < out.len) {
unsafe {
char c = out.data[i];
if (c >= 'A' && c <= 'Z') { out.data[i] = (char)(c + (char)32); }
}
i = i + 1UL;
}
return out;
}
inline struct String String::trim_left() const {
unsigned long start = 0UL;
while (start < self.len) {
char c;
unsafe { c = self.data[start]; }
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { start = start + 1UL; }
else { break; }
}
unsafe { return String::from_n_((const char*)((char*)self.data + start), self.len - start); }
}
inline struct String String::trim_right() const {
unsigned long end = self.len;
while (end > 0UL) {
char c;
unsafe { c = self.data[end - 1UL]; }
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { end = end - 1UL; }
else { break; }
}
unsafe { return String::from_n_((const char*)self.data, end); }
}
inline struct String String::trim() const {
struct String tmp = self.trim_left();
struct String out = tmp.trim_right();
tmp.free();
return out;
}
inline struct String String::pad_left(unsigned long width, char fill) const {
if (self.len >= width) { return self.clone(); }
unsigned long pad = width - self.len;
struct String out = String::with_cap(width);
unsigned long i = 0UL;
while (i < pad) { out.push_char(fill); i = i + 1UL; }
unsafe { out.push((const char*)self.data); }
return out;
}
inline struct String String::pad_right(unsigned long width, char fill) const {
if (self.len >= width) { return self.clone(); }
unsigned long pad = width - self.len;
struct String out = String::with_cap(width);
unsafe { out.push((const char*)self.data); }
unsigned long i = 0UL;
while (i < pad) { out.push_char(fill); i = i + 1UL; }
return out;
}
inline struct String String::strip_prefix(const &char prefix) const {
unsigned long plen = str_len(prefix);
if (plen > self.len) { return self.clone(); }
unsafe {
if (safe_memcmp_raw_((const unsigned char*)self.data, (const unsigned char*)prefix, plen) != 0) {
return self.clone();
}
return String::from_n_((const char*)((char*)self.data + plen), self.len - plen);
}
}
inline struct String String::strip_suffix(const &char suffix) const {
unsigned long slen = str_len(suffix);
if (slen > self.len) { return self.clone(); }
unsafe {
if (safe_memcmp_raw_((const unsigned char*)((char*)self.data + self.len - slen), (const unsigned char*)suffix, slen) != 0) {
return self.clone();
}
return String::from_n_((const char*)self.data, self.len - slen);
}
}
inline struct String String::repeat(unsigned long n) const {
if (n == 0UL) { return String::new(); }
struct String out = String::with_cap(self.len * n);
unsigned long i = 0UL;
while (i < n) {
unsafe { out.push((const char*)self.data); }
i = i + 1UL;
}
return out;
}
inline struct String String::capitalize() const {
if (self.len == 0UL) { return String::new(); }
struct String out = self.clone();
unsafe {
char first = out.data[0];
if (first >= 'a' && first <= 'z') { out.data[0] = (char)(first - (char)32); }
}
// Lowercase the rest
unsigned long i = 1UL;
while (i < out.len) {
unsafe {
char c = out.data[i];
if (c >= 'A' && c <= 'Z') { out.data[i] = (char)(c + (char)32); }
}
i = i + 1UL;
}
return out;
}
// ── Split ─────────────────────────────────────────────────────────────────────
unsigned long String::split(const &char delim, []String out) const {
unsigned long max = out.len;
if (max == 0UL || self.cap == 0UL) { return 0UL; }
unsigned long dlen = str_len(delim);
if (dlen == 0UL) {
// Empty delimiter: return clone in first slot
unsafe {
[]String arr = out;
arr[0] = self.clone();
}
return 1UL;
}
unsigned long cnt = 0UL;
unsigned long start = 0UL;
while (start <= self.len && cnt < max) {
// If this is the last slot, take remainder
if (cnt == max - 1UL) {
unsafe {
[]String arr = out;
arr[cnt] = String::from_n_((const char*)((char*)self.data + start), self.len - start);
}
cnt = cnt + 1UL;
break;
}
// Search for delimiter
long long pos = -1LL;
if (start + dlen <= self.len) {
unsigned long j = start;
while (j + dlen <= self.len) {
unsafe {
if (safe_memcmp_raw_((const unsigned char*)((char*)self.data + j), (const unsigned char*)delim, dlen) == 0) {
pos = (long long)j;
break;
}
}
j = j + 1UL;
}
}
if (pos < 0LL) {
// No more delimiters — take the rest
unsafe {
[]String arr = out;
arr[cnt] = String::from_n_((const char*)((char*)self.data + start), self.len - start);
}
cnt = cnt + 1UL;
break;
}
// Extract segment before delimiter
unsafe {
[]String arr = out;
arr[cnt] = String::from_n_((const char*)((char*)self.data + start), (unsigned long)pos - start);
}
cnt = cnt + 1UL;
start = (unsigned long)pos + dlen;
}
// If string ends with delimiter and we have room, add empty string
if (start == self.len && cnt < max && cnt > 0UL) {
unsafe {
[]String arr = out;
arr[cnt] = String::new();
}
cnt = cnt + 1UL;
}
return cnt;
}
unsigned long String::split_lines([]String out) const {
unsigned long max = out.len;
if (max == 0UL || self.cap == 0UL) { return 0UL; }
unsigned long cnt = 0UL;
unsigned long start = 0UL;
while (start <= self.len && cnt < max) {
if (cnt == max - 1UL) {
unsafe {
[]String arr = out;
arr[cnt] = String::from_n_((const char*)((char*)self.data + start), self.len - start);
}
cnt = cnt + 1UL;
break;
}
// Find \n or \r\n
unsigned long j = start;
long long pos = -1LL;
unsigned long skip = 1UL; // how many bytes the line ending occupies
while (j < self.len) {
unsafe {
if (self.data[j] == '\n') {
pos = (long long)j;
skip = 1UL;
break;
}
if (self.data[j] == '\r') {
pos = (long long)j;
if (j + 1UL < self.len && self.data[j + 1UL] == '\n') {
skip = 2UL;
} else {
skip = 1UL;
}
break;
}
}
j = j + 1UL;
}
if (pos < 0LL) {
unsafe {
[]String arr = out;
arr[cnt] = String::from_n_((const char*)((char*)self.data + start), self.len - start);
}
cnt = cnt + 1UL;
break;
}
unsafe {
[]String arr = out;
arr[cnt] = String::from_n_((const char*)((char*)self.data + start), (unsigned long)pos - start);
}
cnt = cnt + 1UL;
start = (unsigned long)pos + skip;
}
return cnt;
}
unsigned long String::split_whitespace([]String out) const {
unsigned long max = out.len;
if (max == 0UL || self.cap == 0UL) { return 0UL; }
unsigned long cnt = 0UL;
unsigned long i = 0UL;
// Skip leading whitespace
while (i < self.len) {
char c;
unsafe { c = self.data[i]; }
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { i = i + 1UL; }
else { break; }
}
while (i < self.len && cnt < max) {
// If last slot, take remainder (trimmed of trailing whitespace)
if (cnt == max - 1UL) {
unsigned long end = self.len;
while (end > i) {
char c;
unsafe { c = self.data[end - 1UL]; }
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { end = end - 1UL; }
else { break; }
}
unsafe {
[]String arr = out;
arr[cnt] = String::from_n_((const char*)((char*)self.data + i), end - i);
}
cnt = cnt + 1UL;
break;
}
// Find end of current token
unsigned long start = i;
while (i < self.len) {
char c;
unsafe { c = self.data[i]; }
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { break; }
i = i + 1UL;
}
unsafe {
[]String arr = out;
arr[cnt] = String::from_n_((const char*)((char*)self.data + start), i - start);
}
cnt = cnt + 1UL;
// Skip whitespace between tokens
while (i < self.len) {
char c;
unsafe { c = self.data[i]; }
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { i = i + 1UL; }
else { break; }
}
}
return cnt;
}
// ── Comparison ────────────────────────────────────────────────────────────────
inline int String::eq(&String other) const {
if (self.len != other.len) { return 0; }
unsafe { return safe_memcmp_raw_((const unsigned char*)self.data, (const unsigned char*)other.data, self.len) == 0; }
}
inline int String::eq_cstr(const &char other) const {
unsafe { return str_cmp((const char*)self.data, other) == 0; }
}
inline int String::cmp(&String other) const {
unsafe { return str_cmp((const char*)self.data, (const char*)other.data); }
}
int String::lt(&String other) const { return self.cmp(other) < 0; }
int String::gt(&String other) const { return self.cmp(other) > 0; }
inline int String::eq_ignore_case(&String other) const {
if (self.len != other.len) { return 0; }
unsigned long i = 0UL;
while (i < self.len) {
unsafe {
char a = self.data[i];
char b = other.data[i];
if (a >= 'A' && a <= 'Z') { a = (char)(a + (char)32); }
if (b >= 'A' && b <= 'Z') { b = (char)(b + (char)32); }
if (a != b) { return 0; }
}
i = i + 1UL;
}
return 1;
}
inline int String::eq_cstr_ignore_case(const &char other) const {
unsigned long olen = str_len(other);
if (self.len != olen) { return 0; }
unsigned long i = 0UL;
while (i < self.len) {
unsafe {
char a = self.data[i];
char b = other[i];
if (a >= 'A' && a <= 'Z') { a = (char)(a + (char)32); }
if (b >= 'A' && b <= 'Z') { b = (char)(b + (char)32); }
if (a != b) { return 0; }
}
i = i + 1UL;
}
return 1;
}
// ── Query ─────────────────────────────────────────────────────────────────────
inline int String::is_ascii() const {
unsigned long i = 0UL;
while (i < self.len) {
unsafe { if ((unsigned char)self.data[i] > 127) { return 0; } }
i = i + 1UL;
}
return 1;
}
inline int String::is_numeric() const {
if (self.len == 0UL) { return 0; }
unsigned long i = 0UL;
while (i < self.len) {
char c;
unsafe { c = self.data[i]; }
if (c < '0' || c > '9') { return 0; }
i = i + 1UL;
}
return 1;
}
inline int String::is_alphanumeric() const {
if (self.len == 0UL) { return 0; }
unsigned long i = 0UL;
while (i < self.len) {
char c;
unsafe { c = self.data[i]; }
int ok = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
if (!ok) { return 0; }
i = i + 1UL;
}
return 1;
}
// ── Conversion ────────────────────────────────────────────────────────────────
inline long long String::parse_int(?&int ok) const {
unsafe { return str_to_int((const char*)self.data, ok); }
}
inline double String::parse_float(?&int ok) const {
unsafe { return str_to_float((const char*)self.data, ok); }
}
} // namespace std