commit c08cc7e751106913fe3c146f9091fb4b84ddc454
parent 1e28e8ac6853366424048307904de2ceed11522a
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 7 Feb 2021 11:45:43 -0500
bytes, strings: add contains
Diffstat:
3 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/bytes/contains.ha b/bytes/contains.ha
@@ -0,0 +1,5 @@
+// Returns true if a byte slice contains a byte or a sequence of bytes.
+export fn contains(haystack: []u8, needle: (u8 | []u8)) bool = match (needle) {
+ b: u8 => !(index_byte(haystack, b) is void),
+ b: []u8 => !(index_slice(haystack, b) is void),
+};
diff --git a/bytes/index.ha b/bytes/index.ha
@@ -1,6 +1,13 @@
// Returns the offset of the first instance of 'needle' in a 'haystack' of
// bytes, or void if it is not found.
-export fn index(haystack: []u8, needle: u8) (size | void) = {
+export fn index(haystack: []u8, needle: (u8 | []u8)) (size | void) = {
+ return match (needle) {
+ b: u8 => index_byte(haystack, b),
+ b: []u8 => index_slice(haystack, b),
+ };
+};
+
+fn index_byte(haystack: []u8, needle: u8) (size | void) = {
for (let i = 0z; i < len(haystack); i += 1z) {
if (haystack[i] == needle) {
return i;
@@ -8,7 +15,26 @@ export fn index(haystack: []u8, needle: u8) (size | void) = {
};
};
+fn index_slice(haystack: []u8, needle: []u8) (size | void) = {
+ if (len(needle) > len(haystack)) {
+ return;
+ };
+ let i = 0z;
+ for (i < len(haystack)) {
+ let n = nequal(haystack[i..], needle);
+ if (n == len(needle)) {
+ return i;
+ } else if (n != 0z) {
+ i += n;
+ } else {
+ i += 1z;
+ };
+ };
+};
+
+
@test fn index() void = {
+ // Bytes
let a: [4]u8 = [1u8, 3u8, 3u8, 7u8];
match (index(a, 7u8)) {
n: size => assert(n == 3z),
@@ -22,4 +48,18 @@ export fn index(haystack: []u8, needle: u8) (size | void) = {
size => abort(),
void => void,
};
+
+ // Slices
+ match (index(a, [3u8, 3u8])) {
+ n: size => assert(n == 1z),
+ void => abort(),
+ };
+ match (index(a, [])) {
+ n: size => assert(n == 0z),
+ void => abort(),
+ };
+ match(index(a, [4u8, 2u8])) {
+ size => abort(),
+ void => void,
+ };
};
diff --git a/strings/contains.ha b/strings/contains.ha
@@ -0,0 +1,15 @@
+use bytes;
+use encoding::utf8;
+
+// Returns true if a string contains a rune or a sub-string.
+export fn contains(haystack: str, needle: (str | rune)) bool = match (needle) {
+ s: str => bytes::contains(to_utf8(haystack), to_utf8(s)),
+ r: rune => bytes::contains(to_utf8(haystack), utf8::encode_rune(r)),
+};
+
+@test fn contains() void = {
+ assert(contains("hello world", "hello"));
+ assert(contains("hello world", "world"));
+ assert(contains("hello world", ""));
+ assert(!contains("hello world", "foobar"));
+};