Browse Source

Improve `strings.index_any` and `strings.last_index_any`

gingerBill 4 years ago
parent
commit
bf56e3ea8d
2 changed files with 82 additions and 11 deletions
  1. 22 0
      core/strings/ascii_set.odin
  2. 60 11
      core/strings/strings.odin

+ 22 - 0
core/strings/ascii_set.odin

@@ -0,0 +1,22 @@
+//+private
+package strings
+
+import "core:unicode/utf8"
+
+Ascii_Set :: distinct [8]u32;
+
+ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_check {
+	for i in 0..<len(chars) {
+		c := chars[i];
+		if c >= utf8.RUNE_SELF {
+			return;
+		}
+		as[c>>5] |= 1 << uint(c&31);
+	}
+	ok = true;
+	return;
+}
+
+ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check {
+	return as[c>>5] & (1<<(c&31)) != 0;
+}

+ 60 - 11
core/strings/strings.odin

@@ -479,17 +479,34 @@ last_index :: proc(s, substr: string) -> int {
 	return -1;
 }
 
+
 index_any :: proc(s, chars: string) -> int {
 	if chars == "" {
 		return -1;
 	}
-
-	// TODO(bill): Optimize
-	for r, i in s {
-		for c in chars {
-			if r == c {
-				return i;
+	
+	if len(chars) == 1 {
+		r := rune(chars[0]);
+		if r >= utf8.RUNE_SELF {
+			r = utf8.RUNE_ERROR;
+		}
+		return index_rune(s, r);
+	}
+	
+	if len(s) > 8 {
+		if as, ok := ascii_set_make(chars); ok {
+			for i in 0..<len(s) {
+				if ascii_set_contains(as, s[i]) {
+					return i;
+				}
 			}
+			return -1;
+		}
+	}
+
+	for c, i in chars {
+		if index_rune(chars, c) >= 0 {
+			return i;
 		}
 	}
 	return -1;
@@ -499,14 +516,46 @@ last_index_any :: proc(s, chars: string) -> int {
 	if chars == "" {
 		return -1;
 	}
+	
+	if len(s) == 1 {
+		r := rune(s[0]);
+		if r >= utf8.RUNE_SELF {
+			r = utf8.RUNE_ERROR;
+		}
+		return index_rune(chars, r);
+	}
+	
+	if len(s) > 8 {
+		if as, ok := ascii_set_make(chars); ok {
+			for i := len(s)-1; i >= 0; i -= 1 {
+				if ascii_set_contains(as, s[i]) {
+					return i;
+				}
+			}
+			return -1;
+		}
+	}
+	
+	if len(chars) == 1 {
+		r := rune(chars[0]);
+		if r >= utf8.RUNE_SELF {
+			r = utf8.RUNE_ERROR;
+		}
+		for i := len(s); i > 0; /**/ {
+			c, w := utf8.decode_last_rune_in_string(s[:i]);
+			i -= w;
+			if c == r {
+				return i;
+			}
+		}
+		return -1;
+	}
 
-	for i := len(s); i > 0;  {
+	for i := len(s); i > 0; /**/ {
 		r, w := utf8.decode_last_rune_in_string(s[:i]);
 		i -= w;
-		for c in chars {
-			if r == c {
-				return i;
-			}
+		if index_rune(chars, r) >= 0 {
+			return i;
 		}
 	}
 	return -1;