#include "website.h" String::String() { ptr = nullptr; capacity = INITIAL_SIZE; len = 0; initial_buf[0] = 0;}String::String(char *buf, int size) { ptr = (char*)((u64)buf | 1ULL); capacity = size; len = 0;}String::String(String& other) { capacity = other.capacity; len = other.len; ptr = other.ptr ? new char[other.capacity] : nullptr; memcpy(data(), other.data(), len + 1);}String::~String() { if (ptr && ((u64)ptr & 1ULL) == 0) delete[] ptr;} char *String::data() { return ptr ? (char*)((u64)ptr & ~1ULL) : &initial_buf[0];} char String::last() { return data()[len-1];} void String::scrub(int len_from_end) { if (len_from_end <= 0) return; int new_head = len - len_from_end; if (new_head < 0) new_head = 0; memset(data() + new_head, 0, len - new_head); len = new_head;} int String::resize(int sz) { if (sz < 0) return len; int actual_sz = sz + 1; if (actual_sz <= capacity) { len = sz; data()[sz] = 0; return len; } if ((u64)ptr & 1ULL) { if (actual_sz > capacity) sz = capacity - 1; len = sz >= 0 ? sz : 0; return len; } int old_cap = capacity; if (capacity < INITIAL_SIZE) capacity = INITIAL_SIZE; while (actual_sz > capacity) capacity *= 2; char *buf = new char[capacity]; if (old_cap > 0) { if (ptr) { memcpy(buf, ptr, old_cap); delete[] ptr; } else if (old_cap <= INITIAL_SIZE) { memcpy(buf, initial_buf, old_cap); } } buf[sz] = 0; ptr = buf; len = sz; return len;} void String::reserve(int sz) { if (sz <= 0 || sz <= capacity || ((u64)ptr & 1ULL)) return; char *buf = new char[sz]; if (capacity > 0) { if (ptr) { memcpy(buf, ptr, capacity); delete[] ptr; } else if (capacity <= INITIAL_SIZE) { memcpy(buf, initial_buf, capacity); } } capacity = sz; ptr = buf;} int String::add(String str) { char *src_data = str.data(); int head = len; int new_size = resize(len + str.len); int to_add = new_size - head; if (to_add > 0) { char *dst_data = data(); memcpy(dst_data + head, src_data, to_add); dst_data[head + to_add] = 0; to_add--; } return to_add;} int String::add(const char *str, int size) { size = size < 0 ? strlen(str) : size; if (size == 0) return 0; int head = len; int new_size = resize(len + size); int to_add = new_size - head; if (to_add > 0) { char *dst_data = data(); memcpy(dst_data + head, str, to_add); dst_data[head + to_add] = 0; to_add--; } return to_add;} int String::add(char c) { return add(&c, 1);}int String::add(const char *str) { return add(str, -1);} void String::assign(const char *str, int size) { resize(0); add(str, size);} void String::add_chars(char c, int n) { if (n <= 0) return; int head = len; resize(head + n); char *p = data() + head; while (--n >= 0) *p++ = c;} int String::add_and_escape(const char *str, int size) { if (!str) return 0; if (size <= 0) size = strlen(str); int head = len; int old_len = len; resize(len + size); for (int i = 0; str[i] && i < size; i++) { char c = str[i]; if (NEEDS_ESCAPE(c)) { int h = head + 6; if (h+1 >= capacity) resize(h+1); write_escaped_byte(c, data() + head); head = h; } else { int h = head + 1; if (h+1 >= capacity) resize(h+1); data()[head] = c; head = h; } } len = head; return len - old_len;} struct String_Format { int param; int subparam; int type; int size; int left_sp; int right_sp;}; static int fmt_write_string(char *param, int len, String& str, int pos, String_Format& f, int spec_len) { if (!param) { str.resize(str.len + 6 - spec_len); char *p = &str.data()[pos]; *p++ = '('; *p++ = 'n'; *p++ = 'u'; *p++ = 'l'; *p++ = 'l'; *p++ = ')'; return 6; } int s_len = strlen(param); if (!len || len > s_len) len = s_len; int elem_size = f.size; if (!elem_size || elem_size > len) elem_size = len; str.resize(str.len + f.left_sp + elem_size + f.right_sp - spec_len); char *p = &str.data()[pos]; for (int i = 0; i < f.left_sp; i++) *p++ = ' '; memcpy(p, param, elem_size); p += elem_size; for (int i = 0; i < f.right_sp; i++) *p++ = ' '; return f.left_sp + elem_size + f.right_sp;} static int fmt_write_number(long param, String& str, int pos, String_Format& f, int spec_len) { int elem_size = f.size; bool neg = param < 0; if (neg) param = -param; char digits[24]; int n_digits = 0; if (param == 0) { digits[0] = '0'; n_digits = 1; } else { int base = f.type == 1 || f.type == 2 ? 10 : 16; long n = param; while (n) { char d = (char)(n % base); n /= base; d += d < 10 ? '0' : 'a' - 10; digits[n_digits++] = d; } } if (!elem_size) elem_size = n_digits + (neg ? 1 : 0); str.resize(str.len + f.left_sp + elem_size + f.right_sp - spec_len); char *p = &str.data()[pos]; for (int i = 0; i < f.left_sp; i++) *p++ = ' '; int to_write = elem_size; if (neg) { *p++ = '-'; to_write--; } for (int i = n_digits; i < to_write; i++) *p++ = '0'; int end = to_write < n_digits ? to_write : n_digits; for (int i = end-1; i >= 0; i--) *p++ = digits[i]; for (int i = 0; i < f.right_sp; i++) *p++ = ' '; return f.left_sp + elem_size + f.right_sp;} void write_formatted_string(String& str, const char *fmt, va_list args) { str.resize(0); if (!fmt) { str.data()[0] = 0; return; } int fmt_len = strlen(fmt); str.resize(fmt_len + 1); bool was_esc = false; bool formatting = false; int fmt_start = -1; int pos = 0; String_Format f = {0}; for (int i = 0; i < fmt_len; i++) { char c = fmt[i]; if (c == '\\') { if (was_esc) str.data()[pos++] = '\\'; was_esc = !was_esc; continue; } if (!was_esc && c == '}') { if (formatting) { int written = 0; int spec_len = i - fmt_start + 1; if (f.type == 0) { char *param = va_arg(args, char*); written = fmt_write_string(param, 0, str, pos, f, spec_len); } else if (f.type == 1) { char *s_param = va_arg(args, char*); int l_param = va_arg(args, int); written = fmt_write_string(s_param, l_param, str, pos, f, spec_len); } else if (f.type >= 2 && f.type <= 5) { long param = 0; if (f.type == 2 || f.type == 4) param = va_arg(args, int); else if (f.type == 3 || f.type == 5) param = va_arg(args, long); written = fmt_write_number(param, str, pos, f, spec_len); } pos += written; } formatting = false; continue; } if (formatting) { if (c == ':') { f.param++; } else if (c == '.') { f.subparam++; } else if (f.param == 0) { const char *data_types = "sSdDxX"; int s = strlen(data_types); for (int i = 0; i < s; i++) { if (c == data_types[i]) { f.type = i; break; } } } else if (f.param == 1 && c >= '0' && c <= '9') { if (f.subparam == 0) { f.size *= 10; f.size += c - '0'; } } else if (f.param == 2 && c >= '0' && c <= '9') { if (f.subparam == 1) { f.left_sp *= 10; f.left_sp += c - '0'; } else if (f.subparam == 1) { f.left_sp *= 10; f.right_sp += c - '0'; } } } else { formatting = !was_esc && c == '{'; if (formatting) { memset(&f, 0, sizeof(String_Format)); fmt_start = i; } else if (!(c == '\\' && !was_esc)) { str.data()[pos++] = c; } } was_esc = false; } str.data()[pos] = 0; str.resize(str.len - 1);} char *append_string(char *dst, char *src, int size) { if (size <= 0) { while (*src) *dst++ = *src++; } else { for (int i = 0; i < size; i++) *dst++ = *src++; } *dst = 0; return dst;} void write_escaped_byte(int ch, char *buf) { u64 map[2]; map[0] = 0x3736353433323130; map[1] = 0x6665646362613938; char *p = buf; *p++ = '&'; *p++ = '#'; *p++ = 'x'; *p++ = ((char*)map)[(ch >> 4) & 0xf]; *p++ = ((char*)map)[ch & 0xf]; *p++ = ';'; *p = 0;} bool caseless_match(const char *a, const char *b) { while (*a && *b) { char c1 = *a++; char c2 = *b++; if (c1 >= 'A' && c1 <= 'Z') c1 += 0x20; if (c2 >= 'A' && c2 <= 'Z') c2 += 0x20; if (c1 != c2) return false; } return *a == 0 && *b == 0;}