summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/string.c293
-rw-r--r--src/string.h97
2 files changed, 373 insertions, 17 deletions
diff --git a/src/string.c b/src/string.c
index 2185a81..41119e9 100644
--- a/src/string.c
+++ b/src/string.c
@@ -3,6 +3,7 @@
#include "log.h"
#include "string.h"
+#include <immintrin.h>
#include <ctype.h>
#include <string.h>
@@ -125,19 +126,289 @@ void strng_clear(strng_t *string) {
strng_view_t sv_from_chars(char const* c) { u64 l = strlen(c);
return (strng_view_t){
.start=0,
- .end=l,
- .max=l,
+ .end=l-1,
+ .max=l-1,
.string=c, }; }
-void sv_trim_left(strng_view_t *sv) {
- while (sv->start <= sv->end && isspace(*(sv->string+sv->start))) sv->start++;
- if (sv->start > sv->end) sv->start = sv->end;
+void sv_trimws_left(strng_view_t *sv) {
+ while (sv->start <= sv->end && isspace((unsigned char)*(sv->string+sv->start))) sv->start++;
}
-void sv_trim_right(strng_view_t *sv) {
- while (sv->end >= sv->start && isspace(*(sv->string+sv->end))) sv->end--;
- if (sv->end < sv->start) sv->end = sv->start;
+
+void sv_trimws_right(strng_view_t *sv) {
+ while (sv->end >= sv->start && isspace((unsigned char)*(sv->string+sv->end))) sv->end--;
+}
+
+void sv_trimws(strng_view_t *sv) {
+ sv_trimws_left(sv);
+ sv_trimws_right(sv);
+}
+
+void sv_trim_pastc_left(strng_view_t *sv, char n) {
+ while (sv->start <= sv->end) {
+ if (*(sv->string+sv->start) == n) {
+ sv->start++;
+ return;
+ }
+ sv->start++;
+ }
+}
+
+void sv_trim_pastc_right(strng_view_t *sv, char n) {
+ while (sv->end >= sv->start) {
+ if (*(sv->string+sv->end) == n) {
+ sv->end--;
+ return;
+ }
+ sv->end--;
+ }
+}
+
+void sv_trim_end_nextc(strng_view_t *sv, char n) {
+ if (sv->start > sv->end) return;
+
+ i32 i = sv_contains_c_simd(sv, n);
+
+ if (i > -1) {
+ if (i == 0) {
+ sv->end = sv->start - 1;
+ } else {
+ sv->end = sv->start + i - 1;
+ }
+ }
+}
+
+void sv_trim_strt_prvc(strng_view_t *sv, char n) {
+ if (sv->start > sv->end) return;
+
+ for (i64 i = (i64)sv->end; i >= (i64)sv->start; i--) {
+ if (*(sv->string + i) == n) {
+ sv->start = i + 1;
+ return;
+ }
+ }
+}
+
+void sv_set_len_left(strng_view_t *sv, u64 n) {
+ if (sv->start > sv->end) return;
+
+ if (n == 0) {
+ sv->end = sv->start - 1;
+ return;
+ }
+ u64 target_end = sv->start + n - 1;
+ if (target_end < (u64)sv->end) {
+ sv->end = (u64)target_end;
+ }
}
-void sv_trim(strng_view_t *sv) { sv_trim_left(sv);
- sv_trim_right(sv); }
-void sv_reset(strng_view_t *sv) { sv->end=sv->max; sv->start=0; }
+void sv_set_len_right(strng_view_t *sv, u64 n) {
+ if (sv->start > sv->end) return;
+
+ if (n == 0) {
+ sv->start = sv->end + 1;
+ return;
+ }
+
+ if (n >= sv_len(sv)) return;
+
+ sv->start = sv->end - n + 1;
+}
+
+i32 sv_contains_simd(strng_view_t const *sv, char const *_needle) {
+ u64 nlen = strlen(_needle);
+ u64 hlen = sv_len(sv);
+
+ if ((nlen > hlen) || !hlen) return -1;
+ if (nlen == hlen && memcmp(sv->string+sv->start, _needle, hlen) == 0) return 0;
+
+ char const *restrict haystack = sv->string+sv->start;
+ char const *restrict needle = _needle;
+
+ __m256i first_vec = _mm256_set1_epi8(*needle);
+
+ u64 end_idx = hlen - nlen + 1;
+ u64 i = 0;
+
+ for (; i + 32 <= end_idx; i += 32) {
+ __m256i hay_vec = _mm256_loadu_si256((const __m256i*)(haystack+i));
+ __m256i cmp = _mm256_cmpeq_epi8(hay_vec, first_vec);
+ u32 mask = _mm256_movemask_epi8(cmp);
+
+ while (mask != 0) {
+ i32 offset = __builtin_ctz(mask);
+ if (strncmp(haystack+i+offset, needle, nlen) == 0) return i+offset;
+ mask &= (mask - 1);
+ }
+ }
+
+ for (; i < end_idx; i++) { if (strncmp(haystack + i, needle, nlen) == 0) return i; }
+
+ return -1;
+}
+
+i32 sv_contains_c_simd(strng_view_t const *sv, char n) {
+ u64 hlen = sv_len(sv);
+
+ char const *restrict haystack = SV_TO(*sv);
+
+ if (hlen == 0) return -1;
+ if (hlen == 1 && *haystack == n) return 0;
+
+
+ __m256i needle_vec = _mm256_set1_epi8(n);
+ u64 i = 0;
+
+ for (; i + 32 <= hlen; i += 32) {
+ __m256i hay_vec = _mm256_loadu_si256((const __m256i*)(haystack+i));
+ __m256i cmp = _mm256_cmpeq_epi8(hay_vec, needle_vec);
+ u32 mask = _mm256_movemask_epi8(cmp);
+
+ if (mask) {
+ i32 offset = __builtin_ctz(mask);
+ return (i32)(i+offset);
+ }
+ }
+
+ for (; i < hlen; i++) { if (haystack[i] == n) return i; }
+
+ return -1;
+}
+
+b32 sv_starts_with(strng_view_t const *sv, char const *prefix) {
+ u64 plen = strlen(prefix);
+ u64 hlen = sv_len(sv);
+ if (plen > hlen || hlen == 0) return false;
+ return memcmp(SV_TO(*sv), prefix, plen) == 0;
+}
+
+b32 sv_ends_with(strng_view_t const *sv, char const *suffix) {
+ u64 slen = strlen(suffix);
+ u64 hlen = sv_len(sv);
+ if (slen > hlen || hlen == 0) return false;
+ return memcmp(SV_TO(*sv)+(hlen-slen), suffix, slen) == 0;
+}
+
+b32 sv_eq_case_insensitive(strng_view_t const *sv1, strng_view_t const *sv2) {
+ u64 len1 = sv_len(sv1);
+ u64 len2 = sv_len(sv2);
+ if (len1 != len2) return false;
+
+ char const *restrict c1 = SV_TO(*sv1);
+ char const *restrict c2 = SV_TO(*sv2);
+
+ __m256i Av = _mm256_set1_epi8('A'-1);
+ __m256i Zv = _mm256_set1_epi8('Z'+1);
+ __m256i subV = _mm256_set1_epi8(0x20);
+
+ u64 i = 0;
+ for (; i + 32 <= len1; i += 32) {
+ __m256i v1 = _mm256_loadu_si256((const __m256i*)(c1+i));
+ __m256i v2 = _mm256_loadu_si256((const __m256i*)(c2+i));
+
+ // Get chars which are 'A'<=c<='Z'
+ __m256i A1 = _mm256_cmpgt_epi8(v1, Av);
+ __m256i Z1 = _mm256_cmpgt_epi8(v1, Zv);
+ __m256i u1 = _mm256_and_si256(subV, _mm256_andnot_si256(Z1, A1));
+ v1 = _mm256_or_si256(v1, u1);
+
+ // Same for v2
+ __m256i A2 = _mm256_cmpgt_epi8(v2, Av);
+ __m256i Z2 = _mm256_cmpgt_epi8(v2, Zv);
+ __m256i u2 = _mm256_and_si256(subV, _mm256_andnot_si256(Z2, A2));
+ v2 = _mm256_or_si256(v2, u2);
+
+
+ __m256i cmp = _mm256_cmpeq_epi8(v1, v2);
+ u32 mask = _mm256_movemask_epi8(cmp);
+
+ if (mask != 0xFFFFFFFF) return false;
+ }
+
+ for (; i < len1; i++) { if (tolower(c1[i]) != tolower(c2[i])) return false; }
+
+ return true;
+}
+
+strng_view_t sv_split_once(strng_view_t *sv, char const *needle) {
+ u64 nlen = strlen(needle);
+ u64 hlen = sv_len(sv);
+ if (hlen == 0) return *sv;
+
+ i32 i = sv_contains_simd(sv, needle);
+ if (nlen == 0 || i == -1) {
+ strng_view_t out = *sv;
+ sv->start = sv->end+1;
+ return out;
+ }
+
+ strng_view_t out = *sv;
+ if (i == 0) {
+ out.end = out.start - 1;
+ } else {
+ out.end = out.start + i - 1;
+ }
+ sv->start += (i + nlen);
+ return out;
+}
+
+strng_view_t sv_split_once_c(strng_view_t *sv, char n) {
+ u64 hlen = sv_len(sv);
+ if (hlen == 0) return *sv;
+
+ i32 i = sv_contains_c_simd(sv, n);
+ if (i == -1) {
+ strng_view_t out = *sv;
+ sv->start = sv->end+1;
+ return out;
+ }
+
+ strng_view_t out = *sv;
+ if (i == 0) {
+ out.end = out.start - 1;
+ } else {
+ out.end = out.start + i - 1;
+ }
+ sv->start += (i + 1);
+ return out;
+}
+
+
+i32 sv_to_i64(strng_view_t const *sv, i64 *value_o) {
+ char const *origin_ptr = SV_TO(*sv);
+
+ strng_view_t a = sv_dup(sv);
+ sv_trimws(&a);
+
+ char const *digit = SV_TO(a);
+ char const *end_ptr = a.string + a.end;
+
+ i64 mod = 1;
+ if (*digit == '-') {
+ mod = -1;
+ digit++;
+ } else if (*digit == '+') {
+ digit++;
+ }
+
+ if (digit > end_ptr || !(*digit >= '0' && *digit <= '9')) return -1;
+ i64 result = 0;
+ while (digit <= end_ptr && *digit >= '0' && *digit <= '9') {
+ i64 d = *digit - '0';
+
+ if (mod == 1) {
+ if (result > (INT64_MAX / 10) || (result == (INT64_MAX / 10) && d > (INT64_MAX % 10))) {
+ return -1;
+ }
+ result = (result * 10) + d;
+ } else {
+ if (result < (INT64_MIN / 10) || (result == (INT64_MIN / 10) && -d < (INT64_MIN % 10))) {
+ return -1;
+ }
+ result = (result * 10) - d;
+ }
+ digit++;
+ }
+
+ *value_o = result;
+ return (i32)(digit - origin_ptr);
+}
diff --git a/src/string.h b/src/string.h
index a2e69c9..14e3c8a 100644
--- a/src/string.h
+++ b/src/string.h
@@ -33,7 +33,10 @@ strng_t * strng_dup(smrt_arena_t *arena, strng_t const *src);
#define STRNG_TO(s) (char *)((u8*)(s)+STRNG_BASE_POS)
#define STRNG_FMT(s) (i32)s->len, (char *)((u8*)(s)+STRNG_BASE_POS)
-static inline strng_view_t sv_from(strng_t *string) {
+#define SV_TO(sv) (char *)((u8*)((sv).string) + (sv).start)
+#define SV_FMT(sv) (i32)(sv_len(&(sv))), SV_TO((sv))
+
+static inline strng_view_t sv_from(strng_t const *string) {
return (strng_view_t){
.string=(char*)((u8*)string+STRNG_BASE_POS),
.start=0,
@@ -41,12 +44,94 @@ static inline strng_view_t sv_from(strng_t *string) {
.max=string->len,
};
}
+
+static inline u64 sv_len(strng_view_t const *sv) {
+ return (sv->start <= sv->end) ? (sv->end - sv->start + 1) : 0;
+}
+
+static inline strng_view_t sv_dup(strng_view_t const *src) { return *src; }
+static inline strng_view_t sv_orig(strng_view_t const *src) { return (strng_view_t){
+ .string=src->string,
+ .start=0,
+ .end=src->max,
+ .max=src->max }; }
+static inline strng_view_t sv_subv(strng_view_t const *src,
+ u64 start_off, u64 max_len) {
+ u64 src_len = sv_len(src);
+ u64 new_start = src->start + MIN(start_off, src_len);
+ u64 available_len = (src->start + src_len) - new_start;
+ u64 actual_len = MIN(available_len, max_len);
+
+ return (strng_view_t){
+ .string = src->string,
+ .start = new_start,
+ .end = actual_len == 0 ? new_start - 1 : new_start + actual_len - 1,
+ .max = src->max
+ };
+}
+static inline strng_view_t sv_drop_left(strng_view_t const *src, u64 n) {
+ u64 src_len = sv_len(src);
+ u64 m = MIN(src_len, n);
+ return sv_subv(src, m, src_len - m);
+}
+static inline strng_view_t sv_drop_right(strng_view_t const *src, u64 n) {
+ u64 src_len = sv_len(src);
+ u64 m = MIN(src_len, n);
+ return sv_subv(src, 0, src_len - m);
+}
+
strng_view_t sv_from_chars(char const* c);
-void sv_trim_left(strng_view_t *sv);
-void sv_trim_right(strng_view_t *sv);
-void sv_trim(strng_view_t *sv);
-void sv_reset(strng_view_t *sv);
+void sv_trimws_left (strng_view_t *sv);
+void sv_trimws_right(strng_view_t *sv);
+void sv_trimws (strng_view_t *sv);
+
+void sv_trim_pastc_left (strng_view_t *sv, char n);
+void sv_trim_pastc_right(strng_view_t *sv, char n);
+
+// Starts from start and sets end to the next char n.
+// Does nothing if there is no next char n
+void sv_trim_end_nextc(strng_view_t *sv, char n);
+// Starts from end and sets start to the prev char n.
+// Does nothing if there is no prev char n
+void sv_trim_strt_prvc(strng_view_t *sv, char n);
+
+// Sets length keeping start
+void sv_set_len_left (strng_view_t *sv, u64 n);
+// Sets length keeping end
+void sv_set_len_right(strng_view_t *sv, u64 n);
+
+
+static inline void sv_pop_left (strng_view_t *sv) { if (sv->start <= sv->end) sv->start++; }
+static inline void sv_pop_right(strng_view_t *sv) { if (sv->end >= sv->start) sv->end--; }
+static inline void sv_popn_left (strng_view_t *sv, u64 n) { for (u64 i = 0; i < n; i++) sv_pop_left(sv); }
+static inline void sv_popn_right(strng_view_t *sv, u64 n) { for (u64 i = 0; i < n; i++) sv_pop_right(sv); }
+
+static inline void sv_reset(strng_view_t *sv) {
+ sv->start=0;
+ sv->end = sv->max;
+}
+static inline void sv_reset_right(strng_view_t *sv) { sv->end = sv->max; }
+static inline void sv_reset_left(strng_view_t *sv) { sv->start = 0; }
+
+// Returns -1 if not found, otherwise distance from sv->start to beginning of needle
+i32 sv_contains_simd(strng_view_t const *sv, char const *needle);
+// Returns -1 if not found, otherwise distance from sv->start
+i32 sv_contains_c_simd(strng_view_t const *sv, char n);
+
+b32 sv_starts_with(strng_view_t const *sv, char const *prefix);
+b32 sv_ends_with(strng_view_t const *sv, char const *suffix);
+static inline b32 sv_starts_with_c(strng_view_t sv, char c) { return
+ (sv_len(&sv) > 0) && (*SV_TO(sv) == c); }
+static inline b32 sv_ends_with_c(strng_view_t sv, char c) { return
+ (sv_len(&sv) > 0) && (*(sv.string + sv.end) == c); }
+
+b32 sv_eq_case_insensitive(strng_view_t const *sv1, strng_view_t const *sv2);
-#define SV_FMT(sv) (i32)((sv)->end - (sv)->start), (char *)((u8*)((sv)->string) + (sv)->start)
+strng_view_t sv_split_once(strng_view_t *sv, char const *needle);
+strng_view_t sv_split_once_c(strng_view_t *sv, char n);
+// Returns chars read on succes, -1 if couldn't read
+// Creates its own SV and skips whitespace.
+// Fails if first char after trim isnt +/- or 0-9
+i32 sv_to_i64(strng_view_t const *sv, i64 *value_o);