aboutsummaryrefslogtreecommitdiff
path: root/src/base/string
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/string')
-rw-r--r--src/base/string/append.c53
-rw-r--r--src/base/string/appendf.c31
-rw-r--r--src/base/string/clear.c9
-rw-r--r--src/base/string/copyn.c11
-rw-r--r--src/base/string/equals.c12
-rw-r--r--src/base/string/find.c11
-rw-r--r--src/base/string/fit.c20
-rw-r--r--src/base/string/free.c8
-rw-r--r--src/base/string/grow.c33
-rw-r--r--src/base/string/internal.h12
-rw-r--r--src/base/string/join.c16
-rw-r--r--src/base/string/len.c17
-rw-r--r--src/base/string/lower.c12
-rw-r--r--src/base/string/make.c53
-rw-r--r--src/base/string/makef.c25
-rw-r--r--src/base/string/read.c12
-rw-r--r--src/base/string/replace.c26
-rw-r--r--src/base/string/rules.mk19
-rw-r--r--src/base/string/split.c39
-rw-r--r--src/base/string/upper.c12
20 files changed, 431 insertions, 0 deletions
diff --git a/src/base/string/append.c b/src/base/string/append.c
new file mode 100644
index 0000000..d4d0396
--- /dev/null
+++ b/src/base/string/append.c
@@ -0,0 +1,53 @@
+#include "internal.h"
+
+// append will append the given null terminated c string to the string data
+// structure. this variant can append a substring of length len of the given
+// string to our buffer. the result is reallocated if not enough room is present
+// in the buffer.
+int
+str·appendlen(string *s, vlong n, const byte* b)
+{
+ /*
+ bl = strlen(b);
+ if (n > bl) panicf("attempted to make a substring longer than string");
+ */
+
+ str·grow(s, n);
+ if(*s == nil)
+ return 0;
+
+ Hdr* h = (Hdr*)(*s - sizeof(Hdr));
+
+ memcpy(*s + str·len(*s), b, n);
+ h->len += n;
+ (*s)[h->len] = '\0';
+
+ return n;
+}
+
+// append will append the given null terminated c string to the string data
+// structure. this variant will append the entire string.
+int
+str·append(string *s, const byte* b)
+{
+ return str·appendlen(s, strlen(b), b);
+}
+
+// appendbyte will append the given byte to our string.
+// NOTE: as the byte is on the stack, it is not null-terminated.
+// can not pass to the above functions.
+int
+str·appendbyte(string *s, const byte b)
+{
+ str·grow(s, 1);
+ if(*s == nil)
+ return 0;
+
+ Hdr* h = (Hdr*)(*s - sizeof(Hdr));
+
+ *(*s + str·len(*s)) = b;
+ h->len++;
+ (*s)[h->len] = '\0'; // NOTE: I don't think an explicit zero is required..?
+
+ return 1;
+}
diff --git a/src/base/string/appendf.c b/src/base/string/appendf.c
new file mode 100644
index 0000000..4b8d76c
--- /dev/null
+++ b/src/base/string/appendf.c
@@ -0,0 +1,31 @@
+#include "internal.h"
+
+/*
+ * appendf will append the given formatted string to our buffer.
+ * returns the newly minted string
+ */
+
+int
+str·appendf(string *s, const byte* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ int remain = str·cap(*s) - str·len(*s);
+ int n = vsnprintf(*s + str·len(*s), remain + 1, fmt, args);
+ va_end(args);
+
+ if(n > remain){
+ // If the first write was incomplete, we overwite the data again.
+ str·grow(s, n);
+ va_list args;
+ va_start(args, fmt);
+ n = vsnprintf(*s + str·len(*s), n + 1, fmt, args);
+ assert(n - remain <= str·cap(*s));
+ va_end(args);
+ }
+
+ Hdr* h = (Hdr*)(*s - sizeof(Hdr));
+ h->len += n;
+
+ return n;
+}
diff --git a/src/base/string/clear.c b/src/base/string/clear.c
new file mode 100644
index 0000000..986f809
--- /dev/null
+++ b/src/base/string/clear.c
@@ -0,0 +1,9 @@
+#include "internal.h"
+
+void
+str·clear(string *s)
+{
+ Hdr* h = (Hdr*)(*s - sizeof(Hdr));
+ h->len = 0;
+ *s[0] = '\0';
+}
diff --git a/src/base/string/copyn.c b/src/base/string/copyn.c
new file mode 100644
index 0000000..09c2879
--- /dev/null
+++ b/src/base/string/copyn.c
@@ -0,0 +1,11 @@
+#include "internal.h"
+
+char *
+str·copyn(char *dst, char *src, int n)
+{
+ while(*src && n-- > 0)
+ *dst++ = *src++;
+
+ *dst = 0;
+ return dst;
+}
diff --git a/src/base/string/equals.c b/src/base/string/equals.c
new file mode 100644
index 0000000..a975cf5
--- /dev/null
+++ b/src/base/string/equals.c
@@ -0,0 +1,12 @@
+#include "internal.h"
+
+// Equals returns true if string s and t are equivalent.
+bool
+str·equals(const string s, const string t)
+{
+ vlong sL = str·len(s);
+ vlong tL = str·len(t);
+ if (sL != tL) return false;
+
+ return memcmp(s, t, sL) == 0;
+}
diff --git a/src/base/string/find.c b/src/base/string/find.c
new file mode 100644
index 0000000..20f990e
--- /dev/null
+++ b/src/base/string/find.c
@@ -0,0 +1,11 @@
+#include "internal.h"
+
+// find will find the first occurence of
+// substr in the string returns -1 if nothing was found.
+int
+str·find(string s, const byte* substr)
+{
+ byte* loc = strstr(s, substr);
+ if (loc == nil) return -1;
+ return (int)(loc - s);
+}
diff --git a/src/base/string/fit.c b/src/base/string/fit.c
new file mode 100644
index 0000000..56ab041
--- /dev/null
+++ b/src/base/string/fit.c
@@ -0,0 +1,20 @@
+#include "internal.h"
+
+// fit reallocates the string such that the buffer is exactly sized for the
+// buffer. if the capacity equals the length, then the function is a noop. the
+// byte array is unchanged.
+void
+str·fit(string *s)
+{
+ Hdr* h;
+ vlong cap = str·cap(*s);
+ vlong len = str·len(*s);
+
+ if (cap == len) return;
+
+ h = (Hdr*)(s - sizeof(Hdr));
+ h = realloc(h, sizeof(*h) + len + 1);
+ h->cap = len;
+
+ *s = h->buf;
+}
diff --git a/src/base/string/free.c b/src/base/string/free.c
new file mode 100644
index 0000000..7b5ee98
--- /dev/null
+++ b/src/base/string/free.c
@@ -0,0 +1,8 @@
+#include "internal.h"
+
+// free returns memory associated to the buffer.
+void
+str·free(string s)
+{
+ free(s - sizeof(Hdr));
+}
diff --git a/src/base/string/grow.c b/src/base/string/grow.c
new file mode 100644
index 0000000..39a9d2f
--- /dev/null
+++ b/src/base/string/grow.c
@@ -0,0 +1,33 @@
+#include "internal.h"
+
+// grow ensures that the string can encompass at least delta bytes.
+// if it already can, this is a no op.
+// if it can't, the string will be reallocated.
+void
+str·grow(string *s, vlong delta)
+{
+ Hdr *h, *newh;
+ vlong cap = str·cap(*s);
+ vlong len = str·len(*s);
+ assert(cap >= len); // To prevent unsigned behavior
+
+ if (cap - len >= delta) return;
+
+ h = (Hdr*)(*s - sizeof(Hdr));
+
+ vlong newCap = cap + delta;
+ assert(newCap >= cap); // To prevent unsigned behavior
+ if (newCap < MAX_STRING_ALLOC) {
+ newCap *= 2;
+ } else
+ newCap += MAX_STRING_ALLOC;
+
+ newh = (Hdr*)realloc(h, sizeof(*h) + newCap + 1);
+ if (newh == nil) return;
+
+ memset(newh->buf + len, '\0', newCap - len);
+ newh->cap = newCap;
+ newh->len = len;
+
+ *s = newh->buf;
+}
diff --git a/src/base/string/internal.h b/src/base/string/internal.h
new file mode 100644
index 0000000..8c16c64
--- /dev/null
+++ b/src/base/string/internal.h
@@ -0,0 +1,12 @@
+#pragma once
+#include <u.h>
+#include <base.h>
+
+#define MAX_STRING_ALLOC 1024 * 1024
+
+typedef struct Hdr
+{
+ vlong len;
+ vlong cap;
+ byte buf[];
+} Hdr;
diff --git a/src/base/string/join.c b/src/base/string/join.c
new file mode 100644
index 0000000..fb97b6c
--- /dev/null
+++ b/src/base/string/join.c
@@ -0,0 +1,16 @@
+#include "internal.h"
+
+string
+str·join(vlong len, byte** fields, const byte* sep)
+{
+ string s = str·makecap("", 0, 10);
+ int j = 0;
+
+ for (j = 0; j < len; j++) {
+ str·append(&s, fields[j]);
+ if (j < len - 1)
+ str·appendlen(&s, 1, sep);
+ }
+
+ return s;
+}
diff --git a/src/base/string/len.c b/src/base/string/len.c
new file mode 100644
index 0000000..5e42919
--- /dev/null
+++ b/src/base/string/len.c
@@ -0,0 +1,17 @@
+#include "internal.h"
+
+// len returns the length of the string.
+int
+str·len(const string s)
+{
+ Hdr* h = (Hdr*)(s - sizeof(Hdr));
+ return h->len;
+}
+
+// cap returns the capacity of the string buffer.
+int
+str·cap(const string s)
+{
+ Hdr* h = (Hdr*)(s - sizeof(Hdr));
+ return h->cap;
+}
diff --git a/src/base/string/lower.c b/src/base/string/lower.c
new file mode 100644
index 0000000..c6935f8
--- /dev/null
+++ b/src/base/string/lower.c
@@ -0,0 +1,12 @@
+#include "internal.h"
+
+// lower will force all runes in the string to be lowercase
+void
+str·lower(string s)
+{
+ byte *b, *e;
+ b = s;
+ e = b + str·len(s);
+ while (b++ != e)
+ *b = tolower(*b);
+}
diff --git a/src/base/string/make.c b/src/base/string/make.c
new file mode 100644
index 0000000..eb71543
--- /dev/null
+++ b/src/base/string/make.c
@@ -0,0 +1,53 @@
+#include "internal.h"
+
+// new returns a new dynamic string object, initialized from the given c string.
+// len defines the length of the c substring that we will copy into our buffer.
+// the backing buffer will have capacity cap.
+string
+str·makecap(const byte *s, vlong len, vlong cap)
+{
+ struct Hdr* h;
+
+ h = malloc(sizeof(*h) + cap + 1);
+ if (s == nil) memset(h, 0, sizeof(*h));
+
+ if (h == nil) return nil; // Allocation failed.
+
+ h->len = (s == nil) ? 0 : len;
+ h->cap = cap;
+
+ if (cap < h->len) goto cleanup;
+
+ if (s != nil && cap > 0) {
+ memcpy(h->buf, s, h->len);
+ memset(h->buf + h->len, '\0', h->cap - h->len + 1);
+ }
+
+ return h->buf;
+
+cleanup:
+ free(h);
+ panicf("Attempted to create a string with less capacity than length");
+ return nil;
+}
+
+// new returns a new dynamic string object, initialized from the given c string.
+// the backing buffer capacity is equivalent to the string length.
+string
+str·makelen(const byte *s, vlong len)
+{
+ vlong sl = (!s) ? 0 : strlen(s);
+ if (sl < len) panicf("attempted to take a bigger substring than string length");
+
+ vlong cap = (len == 0) ? 1 : len;
+ return str·makecap(s, len, cap);
+}
+
+// new returns a new dynamic string object, initialized from the given c string.
+// the backing buffer capacity is equivalent to the string length.
+string
+str·make(const byte *s)
+{
+ vlong len = (!s) ? 0 : strlen(s);
+ return str·makelen(s, len);
+}
diff --git a/src/base/string/makef.c b/src/base/string/makef.c
new file mode 100644
index 0000000..8fb9c38
--- /dev/null
+++ b/src/base/string/makef.c
@@ -0,0 +1,25 @@
+#include "internal.h"
+
+// Newf returns a new dynamic string object
+string
+str·makef(const byte *fmt, ...)
+{
+ vlong n;
+ string s;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vsnprintf(nil, 0, fmt, args);
+ va_end(args);
+
+ s = str·makecap(nil, 0, n);
+
+ va_start(args, fmt);
+ vsnprintf(s, n + 1, fmt, args);
+ va_end(args);
+
+ Hdr* h = (Hdr*)(s - sizeof(Hdr));
+ h->len = n;
+
+ return s;
+}
diff --git a/src/base/string/read.c b/src/base/string/read.c
new file mode 100644
index 0000000..df2028f
--- /dev/null
+++ b/src/base/string/read.c
@@ -0,0 +1,12 @@
+#include "internal.h"
+
+int
+str·read(string s, int size, int n, void *buf)
+{
+ int len;
+
+ len = MIN(n * size, str·len(s));
+ memcpy(buf, s, len);
+
+ return len;
+}
diff --git a/src/base/string/replace.c b/src/base/string/replace.c
new file mode 100644
index 0000000..127daed
--- /dev/null
+++ b/src/base/string/replace.c
@@ -0,0 +1,26 @@
+#include "internal.h"
+
+// replace will replace all occurences of the given bytes 'from' to bytes 'to'
+// edits are done in place and modify the string.
+// NOTE: as of now strings from and to must be the same size.
+void
+str·replace(string s, const byte* from, const byte* to)
+{
+ vlong fromL = strlen(from);
+ vlong toL = strlen(to);
+ if (toL != fromL) { panicf("different sized replacement string not supported"); }
+
+ vlong l = str·len(s);
+ vlong i = l;
+ vlong j = l;
+
+ for (i = 0; i < l; i++) {
+ for (j = 0; j < toL; j++) {
+ if (s[i] == from[j]) {
+ s[i] = to[j];
+ break;
+ }
+ }
+ }
+}
+
diff --git a/src/base/string/rules.mk b/src/base/string/rules.mk
new file mode 100644
index 0000000..e517ca5
--- /dev/null
+++ b/src/base/string/rules.mk
@@ -0,0 +1,19 @@
+SRCS_$(d)+=\
+ $(d)/string/append.c\
+ $(d)/string/appendf.c\
+ $(d)/string/clear.c\
+ $(d)/string/copyn.c\
+ $(d)/string/equals.c\
+ $(d)/string/find.c\
+ $(d)/string/fit.c\
+ $(d)/string/free.c\
+ $(d)/string/grow.c\
+ $(d)/string/join.c\
+ $(d)/string/len.c\
+ $(d)/string/lower.c\
+ $(d)/string/make.c\
+ $(d)/string/makef.c\
+ $(d)/string/read.c\
+ $(d)/string/replace.c\
+ $(d)/string/split.c\
+ $(d)/string/upper.c\
diff --git a/src/base/string/split.c b/src/base/string/split.c
new file mode 100644
index 0000000..2aa68b4
--- /dev/null
+++ b/src/base/string/split.c
@@ -0,0 +1,39 @@
+#include "internal.h"
+
+// split will split the string by the given token.
+// returns a stretchy buffer of strings that result from the partition.
+// it is the caller's responsibility to clean the memory.
+string*
+str·split(string s, const byte* tok)
+{
+ string* fields = nil;
+ vlong start = 0;
+
+ vlong sL = str·len(s);
+ vlong tokL = strlen(tok);
+ if (sL == 0 || tokL == 0) return nil;
+
+ buffit(fields, 5);
+
+ for (vlong i = 0; i < sL - tokL; i++) {
+ if ((tokL == 1 && s[i] == tokL) || !memcmp(s + i, tok, tokL)) {
+ bufpush(fields, str·makelen(s + start, i - start));
+ if (fields[buflen(fields) - 1] == nil) goto cleanup;
+
+ start = i + tokL;
+ i += tokL - 1;
+ }
+ }
+
+ bufpush(fields, str·makelen(s + start, sL - start));
+
+ return fields;
+
+cleanup:
+ for (vlong i = 0; i < buflen(fields); i++) {
+ str·free(fields[i]);
+ }
+ buffree(fields);
+ return nil;
+}
+
diff --git a/src/base/string/upper.c b/src/base/string/upper.c
new file mode 100644
index 0000000..ab692c1
--- /dev/null
+++ b/src/base/string/upper.c
@@ -0,0 +1,12 @@
+#include "internal.h"
+
+// Upper will force all runes in the string to be uppercase.
+void
+str·upper(string s)
+{
+ byte *b, *e;
+ b = s;
+ e = b + str·len(s);
+ while (b++ != e)
+ *b = toupper(*b);
+}