aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPlatin21 <armin.hamar@icloud.com>2020-09-27 22:13:12 +0300
committerPlatin21 <armin.hamar@icloud.com>2020-09-27 22:13:12 +0300
commitd72a01a714dc150b6c58ecf7f4c066bce2a2a5d1 (patch)
tree835beb15048f2d0ab7f930630636763f81609666
parent2ed6785b4af6bfef40c4c1b34167507195619f32 (diff)
parent2ebb94fa729e50867ca882fd08acc64bab902ca3 (diff)
Merge branch 'master' of https://github.com/odin-lang/Odin
-rw-r--r--core/fmt/fmt.odin52
-rw-r--r--core/os/dir_windows.odin4
-rw-r--r--core/path/filepath/path.odin2
-rw-r--r--core/path/path.odin159
-rw-r--r--core/reflect/reflect.odin299
-rw-r--r--core/runtime/error_checks.odin2
-rw-r--r--core/unicode/utf8/utf8string/string.odin155
-rw-r--r--src/check_type.cpp2
8 files changed, 473 insertions, 202 deletions
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index 7252f31ed..d82963fee 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -1811,7 +1811,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
- if info.maybe && len(info.variants) == 1 && reflect.is_pointer(info.variants[0]) {
+ if reflect.type_info_union_is_pure_maybe(info) {
if v.data == nil {
strings.write_string(fi.buf, "nil");
} else {
@@ -1877,59 +1877,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fmt_opaque(fi, v);
case runtime.Type_Info_Relative_Pointer:
- ptr_any := any{v.data, info.base_integer.id};
- ptr: rawptr;
- switch i in &ptr_any {
- case u8: ptr = handle_relative_pointer(&i);
- case u16: ptr = handle_relative_pointer(&i);
- case u32: ptr = handle_relative_pointer(&i);
- case u64: ptr = handle_relative_pointer(&i);
- case i8: ptr = handle_relative_pointer(&i);
- case i16: ptr = handle_relative_pointer(&i);
- case i32: ptr = handle_relative_pointer(&i);
- case i64: ptr = handle_relative_pointer(&i);
- case u16le: ptr = handle_relative_pointer(&i);
- case u32le: ptr = handle_relative_pointer(&i);
- case u64le: ptr = handle_relative_pointer(&i);
- case i16le: ptr = handle_relative_pointer(&i);
- case i32le: ptr = handle_relative_pointer(&i);
- case i64le: ptr = handle_relative_pointer(&i);
- case u16be: ptr = handle_relative_pointer(&i);
- case u32be: ptr = handle_relative_pointer(&i);
- case u64be: ptr = handle_relative_pointer(&i);
- case i16be: ptr = handle_relative_pointer(&i);
- case i32be: ptr = handle_relative_pointer(&i);
- case i64be: ptr = handle_relative_pointer(&i);
- }
+ ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id);
absolute_ptr := any{ptr, info.pointer.id};
fmt_value(fi, absolute_ptr, verb);
case runtime.Type_Info_Relative_Slice:
- ptr_any := any{v.data, info.base_integer.id};
- ptr: rawptr;
- switch i in &ptr_any {
- case u8: ptr = handle_relative_pointer(&i);
- case u16: ptr = handle_relative_pointer(&i);
- case u32: ptr = handle_relative_pointer(&i);
- case u64: ptr = handle_relative_pointer(&i);
- case i8: ptr = handle_relative_pointer(&i);
- case i16: ptr = handle_relative_pointer(&i);
- case i32: ptr = handle_relative_pointer(&i);
- case i64: ptr = handle_relative_pointer(&i);
- case u16le: ptr = handle_relative_pointer(&i);
- case u32le: ptr = handle_relative_pointer(&i);
- case u64le: ptr = handle_relative_pointer(&i);
- case i16le: ptr = handle_relative_pointer(&i);
- case i32le: ptr = handle_relative_pointer(&i);
- case i64le: ptr = handle_relative_pointer(&i);
- case u16be: ptr = handle_relative_pointer(&i);
- case u32be: ptr = handle_relative_pointer(&i);
- case u64be: ptr = handle_relative_pointer(&i);
- case i16be: ptr = handle_relative_pointer(&i);
- case i32be: ptr = handle_relative_pointer(&i);
- case i64be: ptr = handle_relative_pointer(&i);
- }
+ ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id);
if verb == 'p' {
fmt_pointer(fi, ptr, 'p');
diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin
index 78c7d357b..6b8fa5dbe 100644
--- a/core/os/dir_windows.odin
+++ b/core/os/dir_windows.odin
@@ -4,7 +4,7 @@ import win32 "core:sys/windows"
import "core:strings"
import "core:time"
-read_dir :: proc(fd: Handle, n: int) -> (fi: []File_Info, err: Errno) {
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
// Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
@@ -53,6 +53,8 @@ read_dir :: proc(fd: Handle, n: int) -> (fi: []File_Info, err: Errno) {
return nil, ERROR_INVALID_HANDLE;
}
+ context.allocator = allocator;
+
h := win32.HANDLE(fd);
dir_fi, _ := stat_from_file_information("", h);
diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin
index f2dbedf1f..283eb3a9e 100644
--- a/core/path/filepath/path.odin
+++ b/core/path/filepath/path.odin
@@ -1,3 +1,5 @@
+// The path/filepath package uses either forward slashes or backslashes depending on the operating system
+// To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package
package filepath
import "core:os"
diff --git a/core/path/path.odin b/core/path/path.odin
index 9c8228d11..abdd213f9 100644
--- a/core/path/path.odin
+++ b/core/path/path.odin
@@ -1,3 +1,8 @@
+// The path package is only to be used for paths separated by forward slashes,
+// e.g. paths in URLs
+//
+// This package does not deal with Windows/NT paths with volume letters or backslashes
+// To manipulate operating system specific paths, use the path/filepath package
package path
import "core:strings"
@@ -5,29 +10,14 @@ import "core:runtime"
import "core:unicode/utf8"
// is_separator checks whether the byte is a valid separator character
-is_separator :: proc(c: byte) -> bool {
- switch c {
- case '/': return true;
- case '\\': return ODIN_OS == "windows";
- }
- return false;
+is_separator :: inline proc(c: byte) -> bool {
+ return c == '/';
}
// is_abs checks whether the path is absolute
-is_abs :: proc(path: string) -> bool {
- if len(path) > 0 && path[0] == '/' {
- return true;
- }
- when ODIN_OS == "windows" {
- if len(path) > 2 {
- switch path[0] {
- case 'A'..'Z', 'a'..'z':
- return path[1] == ':' && is_separator(path[2]);
- }
- }
- }
- return false;
+is_abs :: inline proc(path: string) -> bool {
+ return len(path) > 0 && path[0] == '/';
}
@@ -51,7 +41,7 @@ base :: proc(path: string, new := false, allocator := context.allocator) -> (las
for len(path) > 0 && is_separator(path[len(path)-1]) {
path = path[:len(path)-1];
}
- if i := strings.last_index_any(path, OS_SEPARATORS); i >= 0 {
+ if i := strings.last_index(path, "/"); i >= 0 {
path = path[i+1:];
}
@@ -79,13 +69,13 @@ dir :: proc(path: string, allocator := context.allocator) -> string {
// If there is no slash in path, it returns an empty dir and file set to path
// The returned values have the property that path = dir+file
split :: proc(path: string) -> (dir, file: string) {
- i := strings.last_index_any(path, OS_SEPARATORS);
+ i := strings.last_index(path, "/");
return path[:i+1], path[i+1:];
}
// split_elements splits the path elements into slices of the original path string
split_elements :: proc(path: string, allocator := context.allocator) -> []string {
- return strings.split_multi(path, OS_SEPARATORS_ARRAY, true, allocator);
+ return strings.split(path, "/", allocator);
}
// clean returns the shortest path name equivalent to path through lexical analysis only
@@ -206,131 +196,6 @@ name :: proc(path: string, new := false, allocator := context.allocator) -> (nam
-rel :: proc{rel_between, rel_current};
-
-// returns the relative path from one path to another
-rel_between :: proc(from, to: string, allocator := context.allocator) -> string {
- if from == "" || to == "" {
- return "";
- }
-
- from, to := from, to;
- from = full(from, context.temp_allocator);
- to = full(to, context.temp_allocator);
-
- from_is_dir := is_dir(from);
- to_is_dir := is_dir(to);
-
- index, slash := 0, 0;
-
- for {
- if index >= len(from) {
- if index >= len(to) || (from_is_dir && index < len(to) && (to[index] == '/' || to[index] == '\\')) {
- slash = index;
- }
-
- break;
- }
- else if index >= len(to) {
- if index >= len(from) || (to_is_dir && index < len(from) && (from[index] == '/' || from[index] == '\\')) {
- slash = index;
- }
-
- break;
- }
-
- lchar, skip := utf8.decode_rune_in_string(from[index:]);
- rchar, _ := utf8.decode_rune_in_string(to[index:]);
-
- if (lchar == '/' || lchar == '\\') && (rchar == '/' || lchar == '\\') {
- slash = index;
- }
- else if lchar != rchar {
- break;
- }
-
- index += skip;
- }
-
- if slash < 1 {
- // there is no common path, use the absolute `to` path
- return strings.clone(to, allocator);
- }
-
- from_slashes, to_slashes := 0, 0;
-
- if slash < len(from) {
- from = from[slash+1:];
-
- if from_is_dir {
- from_slashes += 1;
- }
- }
- else {
- from = "";
- }
-
- if slash < len(to) {
- to = to[slash+1:];
-
- if to_is_dir {
- to_slashes += 1;
- }
- }
- else {
- to = "";
- }
-
- for char in from {
- if char == '/' || char == '\\' {
- from_slashes += 1;
- }
- }
-
- for char in to {
- if char == '/' || char == '\\' {
- to_slashes += 1;
- }
- }
-
- if from_slashes == 0 {
- buffer := make([]byte, 2 + len(to), allocator);
-
- buffer[0] = '.';
- buffer[1] = '/';
- copy(buffer[2:], to);
-
- return string(buffer);
- }
- else {
- buffer := make([]byte, from_slashes*3 + len(to), allocator);
-
- for i in 0..<from_slashes {
- buffer[i*3+0] = '.';
- buffer[i*3+1] = '.';
- buffer[i*3+2] = '/';
- }
-
- copy(buffer[from_slashes*3:], to);
-
- return string(buffer);
- }
-
- return "";
-}
-
-// returns the relative path from the current directory to another path
-rel_current :: proc(to: string, allocator := context.allocator) -> string {
- return rel_between(current(context.allocator), to, allocator);
-}
-
-
-
-
-
-
-
-
/*
Lazy_Buffer is a lazily made path buffer
When it does allocate, it uses the context.allocator
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index e4d8a23c8..947a10771 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -2,6 +2,8 @@ package reflect
import "core:runtime"
import "core:mem"
+import "intrinsics"
+_ :: intrinsics;
Type_Info :: runtime.Type_Info;
@@ -551,11 +553,22 @@ union_variant_type_info :: proc(a: any) -> ^runtime.Type_Info {
return type_info_of(id);
}
+type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
+ return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0]);
+}
+
union_variant_typeid :: proc(a: any) -> typeid {
if a == nil { return nil; }
ti := runtime.type_info_base(type_info_of(a.id));
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+ if type_info_union_is_pure_maybe(info) {
+ if a.data != nil {
+ return info.variants[0].id;
+ }
+ return nil;
+ }
+
tag_ptr := uintptr(a.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
@@ -573,15 +586,159 @@ union_variant_typeid :: proc(a: any) -> typeid {
}
if a.data != nil && tag != 0 {
- return info.variants[tag-1].id;
+ i := tag if info.no_nil else tag-1;
+ return info.variants[i].id;
}
- } else {
- panic("expected a union to reflect.union_variant_typeid");
+
+ return nil;
}
+ panic("expected a union to reflect.union_variant_typeid");
- return nil;
}
+get_union_variant_raw_tag :: proc(a: any) -> i64 {
+ if a == nil { return -1; }
+
+ ti := runtime.type_info_base(type_info_of(a.id));
+ if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+ if type_info_union_is_pure_maybe(info) {
+ return 1 if a.data != nil else 0;
+ }
+
+ tag_ptr := uintptr(a.data) + info.tag_offset;
+ tag_any := any{rawptr(tag_ptr), info.tag_type.id};
+
+ tag: i64 = ---;
+ switch i in tag_any {
+ case u8: tag = i64(i);
+ case i8: tag = i64(i);
+ case u16: tag = i64(i);
+ case i16: tag = i64(i);
+ case u32: tag = i64(i);
+ case i32: tag = i64(i);
+ case u64: tag = i64(i);
+ case i64: tag = i64(i);
+ case: unimplemented();
+ }
+
+ return tag;
+ }
+ panic("expected a union to reflect.get_union_variant_raw_tag");
+}
+
+
+set_union_variant_raw_tag :: proc(a: any, tag: i64) {
+ if a == nil { return; }
+
+ ti := runtime.type_info_base(type_info_of(a.id));
+ if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+ if type_info_union_is_pure_maybe(info) {
+ // Cannot do anything
+ return;
+ }
+
+ tag_ptr := uintptr(a.data) + info.tag_offset;
+ tag_any := any{rawptr(tag_ptr), info.tag_type.id};
+
+ switch i in &tag_any {
+ case u8: i = u8(tag);
+ case i8: i = i8(tag);
+ case u16: i = u16(tag);
+ case i16: i = i16(tag);
+ case u32: i = u32(tag);
+ case i32: i = i32(tag);
+ case u64: i = u64(tag);
+ case i64: i = i64(tag);
+ case: unimplemented();
+ }
+
+ return;
+ }
+ panic("expected a union to reflect.set_union_variant_raw_tag");
+}
+
+set_union_variant_typeid :: proc(a: any, id: typeid) {
+ if a == nil { return; }
+
+ ti := runtime.type_info_base(type_info_of(a.id));
+ if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+ if type_info_union_is_pure_maybe(info) {
+ // Cannot do anything
+ return;
+ }
+
+ if id == nil && !info.no_nil {
+ set_union_variant_raw_tag(a, 0);
+ return;
+ }
+
+ for variant, i in info.variants {
+ if variant.id == id {
+ tag := i64(i);
+ if !info.no_nil {
+ tag += 1;
+ }
+ set_union_variant_raw_tag(a, tag);
+ return;
+ }
+ }
+ return;
+ }
+ panic("expected a union to reflect.set_union_variant_typeid");
+}
+
+set_union_variant_type_info :: proc(a: any, tag_ti: ^Type_Info) {
+ if a == nil { return; }
+
+ ti := runtime.type_info_base(type_info_of(a.id));
+ if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
+ if type_info_union_is_pure_maybe(info) {
+ // Cannot do anything
+ return;
+ }
+
+ if tag_ti == nil && !info.no_nil {
+ set_union_variant_raw_tag(a, 0);
+ return;
+ }
+
+ for variant, i in info.variants {
+ if variant == tag_ti {
+ tag := i64(i);
+ if !info.no_nil {
+ tag += 1;
+ }
+ set_union_variant_raw_tag(a, tag);
+ return;
+ }
+ }
+ return;
+ }
+ panic("expected a union to reflect.set_union_variant_type_info");
+}
+
+
+as_bool :: proc(a: any) -> (value: bool, valid: bool) {
+ if a == nil { return; }
+ a := a;
+ ti := runtime.type_info_core(type_info_of(a.id));
+ a.id = ti.id;
+
+ #partial switch info in ti.variant {
+ case Type_Info_Boolean:
+ valid = true;
+ switch v in a {
+ case bool: value = bool(v);
+ case b8: value = bool(v);
+ case b16: value = bool(v);
+ case b32: value = bool(v);
+ case b64: value = bool(v);
+ case: valid = false;
+ }
+ }
+
+ return;
+}
as_int :: proc(a: any) -> (value: int, valid: bool) {
v: i64;
@@ -915,3 +1072,137 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
return;
}
+
+
+as_string :: proc(a: any) -> (value: string, valid: bool) {
+ if a == nil { return; }
+ a := a;
+ ti := runtime.type_info_core(type_info_of(a.id));
+ a.id = ti.id;
+
+ #partial switch info in ti.variant {
+ case Type_Info_String:
+ valid = true;
+ switch v in a {
+ case string: value = string(v);
+ case cstring: value = string(v);
+ case: valid = false;
+ }
+ }
+
+ return;
+}
+
+relative_pointer_to_absolute :: proc(a: any) -> rawptr {
+ if a == nil { return nil; }
+ a := a;
+ ti := runtime.type_info_core(type_info_of(a.id));
+ a.id = ti.id;
+
+ #partial switch info in ti.variant {
+ case Type_Info_Relative_Pointer:
+ return relative_pointer_to_absolute_raw(a.data, info.base_integer.id);
+ }
+ return nil;
+}
+
+
+relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
+ _handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
+ if ptr^ == 0 {
+ return nil;
+ }
+ when intrinsics.type_is_unsigned(T) {
+ return rawptr(uintptr(ptr) + uintptr(ptr^));
+ } else {
+ return rawptr(uintptr(ptr) + uintptr(i64(ptr^)));
+ }
+ }
+
+ ptr_any := any{data, base_integer_id};
+ ptr: rawptr;
+ switch i in &ptr_any {
+ case u8: ptr = _handle(&i);
+ case u16: ptr = _handle(&i);
+ case u32: ptr = _handle(&i);
+ case u64: ptr = _handle(&i);
+ case i8: ptr = _handle(&i);
+ case i16: ptr = _handle(&i);
+ case i32: ptr = _handle(&i);
+ case i64: ptr = _handle(&i);
+ case u16le: ptr = _handle(&i);
+ case u32le: ptr = _handle(&i);
+ case u64le: ptr = _handle(&i);
+ case i16le: ptr = _handle(&i);
+ case i32le: ptr = _handle(&i);
+ case i64le: ptr = _handle(&i);
+ case u16be: ptr = _handle(&i);
+ case u32be: ptr = _handle(&i);
+ case u64be: ptr = _handle(&i);
+ case i16be: ptr = _handle(&i);
+ case i32be: ptr = _handle(&i);
+ case i64be: ptr = _handle(&i);
+ }
+ return ptr;
+
+}
+
+
+
+as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
+ if a == nil { return; }
+ a := a;
+ ti := runtime.type_info_core(type_info_of(a.id));
+ a.id = ti.id;
+
+ #partial switch info in ti.variant {
+ case Type_Info_Pointer:
+ valid = true;
+ value = a.data;
+
+ case Type_Info_String:
+ valid = true;
+ switch v in a {
+ case cstring: value = rawptr(v);
+ case: valid = false;
+ }
+
+ case Type_Info_Relative_Pointer:
+ valid = true;
+ value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id);
+ }
+
+ return;
+}
+
+
+as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
+ if a == nil { return; }
+ a := a;
+ ti := runtime.type_info_core(type_info_of(a.id));
+ a.id = ti.id;
+
+ #partial switch info in ti.variant {
+ case Type_Info_String:
+ valid = true;
+ switch v in a {
+ case string: value = raw_data(v);
+ case cstring: value = rawptr(v); // just in case
+ case: valid = false;
+ }
+
+ case Type_Info_Array:
+ valid = true;
+ value = a.data;
+
+ case Type_Info_Slice:
+ valid = true;
+ value = (^mem.Raw_Slice)(a.data).data;
+
+ case Type_Info_Dynamic_Array:
+ valid = true;
+ value = (^mem.Raw_Dynamic_Array)(a.data).data;
+ }
+
+ return;
+}
diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin
index 82d6b7159..d40ac45ca 100644
--- a/core/runtime/error_checks.odin
+++ b/core/runtime/error_checks.odin
@@ -35,7 +35,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: int, index
handle_error(file, line, column, index, count);
}
-slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) {
+slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) -> ! {
context = default_context();
fd := os_stderr();
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
diff --git a/core/unicode/utf8/utf8string/string.odin b/core/unicode/utf8/utf8string/string.odin
new file mode 100644
index 000000000..a34e754cf
--- /dev/null
+++ b/core/unicode/utf8/utf8string/string.odin
@@ -0,0 +1,155 @@
+package utf8string
+
+import "core:unicode/utf8"
+import "core:runtime"
+import "builtin"
+
+String :: struct {
+ contents: string,
+ rune_count: int,
+
+ // cached information
+ non_ascii: int, // index to non-ascii code points
+ width: int, // 0 if ascii
+ byte_pos: int,
+ rune_pos: int,
+}
+
+@(private)
+_len :: builtin.len; // helper procedure
+
+init :: proc(s: ^String, contents: string) -> ^String {
+ s.contents = contents;
+ s.byte_pos = 0;
+ s.rune_pos = 0;
+
+ for i in 0..<_len(contents) {
+ if contents[i] >= utf8.RUNE_SELF {
+ s.rune_count = utf8.rune_count_in_string(contents);
+ _, s.width = utf8.decode_rune_in_string(contents);
+ s.non_ascii = i;
+ return s;
+ }
+ }
+
+ s.rune_count = _len(contents);
+ s.width = 0;
+ s.non_ascii = _len(contents);
+ return s;
+}
+
+to_string :: proc(s: ^String) -> string {
+ return s.contents;
+}
+
+len :: proc(s: ^String) -> int {
+ return s.rune_count;
+}
+
+
+is_ascii :: proc(s: ^String) -> bool {
+ return s.width == 0;
+}
+
+at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) {
+ runtime.bounds_check_error_loc(loc, i, s.rune_count);
+
+ if i < s.non_ascii {
+ return rune(s.contents[i]);
+ }
+
+ switch i {
+ case 0:
+ r, s.width = utf8.decode_rune_in_string(s.contents);
+ s.rune_pos = 0;
+ s.byte_pos = 0;
+ return;
+
+ case s.rune_count-1:
+ r, s.width = utf8.decode_rune_in_string(s.contents);
+ s.rune_pos = i;
+ s.byte_pos = _len(s.contents) - s.width;
+ return;
+
+ case s.rune_pos-1:
+ r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]);
+ s.rune_pos = i;
+ s.byte_pos -= s.width;
+ return;
+
+ case s.rune_pos+1:
+ s.rune_pos = i;
+ s.byte_pos += s.width;
+ fallthrough;
+ case s.rune_pos:
+ r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
+ return;
+ }
+
+ // Linear scan
+ scan_forward := true;
+ if i < s.rune_pos {
+ if i < (s.rune_pos-s.non_ascii)/2 {
+ s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii;
+ } else {
+ scan_forward = false;
+ }
+ } else if i-s.rune_pos < (s.rune_count-s.rune_pos)/2 {
+ // scan_forward = true;
+ } else {
+ s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count;
+ scan_forward = false;
+ }
+
+ if scan_forward {
+ for {
+ r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
+ if s.rune_pos == i {
+ return;
+ }
+ s.rune_pos += 1;
+ s.byte_pos += s.width;
+
+ }
+ } else {
+ for {
+ r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]);
+ s.rune_pos -= 1;
+ s.byte_pos -= s.width;
+ if s.rune_pos == i {
+ return;
+ }
+ }
+ }
+}
+
+slice :: proc(s: ^String, i, j: int, loc := #caller_location) -> string {
+ runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count);
+
+ if j < s.non_ascii {
+ return s.contents[i:j];
+ }
+
+ if i == j {
+ return "";
+ }
+
+ lo, hi: int;
+ if i < s.non_ascii {
+ lo = i;
+ } else if i == s.rune_count {
+ lo = _len(s.contents);
+ } else {
+ at(s, i, loc);
+ lo = s.byte_pos;
+ }
+
+ if j == s.rune_count {
+ hi = _len(s.contents);
+ } else {
+ at(s, j, loc);
+ hi = s.byte_pos;
+ }
+
+ return s.contents[lo:hi];
+}
diff --git a/src/check_type.cpp b/src/check_type.cpp
index b6dfafcdc..d305d6859 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -110,6 +110,8 @@ bool does_field_type_allow_using(Type *t) {
return true;
} else if (is_type_array(t)) {
return t->Array.count <= 4;
+ } else if (is_type_typeid(t)) {
+ return true;
}
return false;
}