Переглянути джерело

Dynamic array syntax [...]Type; make entities private with a prefix of `_`; fix extension checking

Ginger Bill 8 роки тому
батько
коміт
4306345ff1
14 змінених файлів з 172 додано та 88 видалено
  1. 26 17
      code/demo.odin
  2. 1 1
      core/_preload.odin
  3. 1 1
      core/atomic.odin
  4. 11 11
      core/fmt.odin
  5. 2 2
      core/sys/windows.odin
  6. 44 13
      src/check_expr.c
  7. 37 3
      src/checker.c
  8. 1 0
      src/common.c
  9. 9 0
      src/entity.c
  10. 0 1
      src/main.c
  11. 9 4
      src/parser.c
  12. 27 6
      src/string.c
  13. 4 4
      src/tokenizer.c
  14. 0 25
      src/unicode.c

+ 26 - 17
code/demo.odin

@@ -1,4 +1,13 @@
-#import "fmt.odin";
+#import . "fmt.odin";
+#import "atomic.odin";
+#import "hash.odin";
+#import "math.odin";
+#import "mem.odin";
+#import "opengl.odin";
+#import "os.odin";
+#import "sync.odin";
+#import "types.odin";
+#import "utf8.odin";
 
 main :: proc() {
 
@@ -8,7 +17,7 @@ main :: proc() {
 			BANANA,
 			COCONUT,
 		}
-		fmt.println(Fruit.names);
+		println(x, Fruit.names);
 	}
 
 when false {
@@ -24,16 +33,16 @@ when false {
 		c := m[3.0];
 		assert(ok && c == 564);
 
-		fmt.print("map[");
+		print("map[");
 		i := 0;
 		for val, key in m {
 			if i > 0 {
-				fmt.print(", ");
+				print(", ");
 			}
-			fmt.printf("%v=%v", key, val);
+			printf("%v=%v", key, val);
 			i += 1;
 		}
-		fmt.println("]");
+		println("]");
 	}
 	{
 		m := map[string]u32{
@@ -47,11 +56,11 @@ when false {
 		_, ok := m["c"];
 		assert(ok && c == 7654);
 
-		fmt.println(m);
+		println(m);
 	}
 
 	{
-		fmt.println("Hellope!");
+		println("Hellope!");
 
 		x: [dynamic]f64;
 		reserve(x, 16);
@@ -59,24 +68,24 @@ when false {
 		append(x, 2_000_000.500_000, 3, 5, 7);
 
 		for p, i in x {
-			if i > 0 { fmt.print(", "); }
-			fmt.print(p);
+			if i > 0 { print(", "); }
+			print(p);
 		}
-		fmt.println();
+		println();
 
 		{
 			Vec3 :: [vector 3]f32;
 
 			x := Vec3{1, 2, 3};
 			y := Vec3{4, 5, 6};
-			fmt.println(x < y);
-			fmt.println(x + y);
-			fmt.println(x - y);
-			fmt.println(x * y);
-			fmt.println(x / y);
+			println(x < y);
+			println(x + y);
+			println(x - y);
+			println(x * y);
+			println(x / y);
 
 			for i in x {
-				fmt.println(i);
+				println(i);
 			}
 		}
 	}

+ 1 - 1
core/_preload.odin

@@ -373,7 +373,7 @@ Raw_Dynamic_Array :: struct #ordered {
 };
 
 Raw_Dynamic_Map :: struct #ordered {
-	hashes:  [dynamic]int,
+	hashes:  [...]int,
 	entries: Raw_Dynamic_Array,
 };
 

+ 1 - 1
core/atomic.odin

@@ -5,7 +5,7 @@
 _ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
 
 
-yield_thread :: proc() { win32._mm_pause(); }
+yield_thread :: proc() { win32.mm_pause(); }
 mfence       :: proc() { win32.ReadWriteBarrier(); }
 sfence       :: proc() { win32.WriteBarrier(); }
 lfence       :: proc() { win32.ReadBarrier(); }

+ 11 - 11
core/fmt.odin

@@ -3,7 +3,8 @@
 #import "utf8.odin";
 #import "types.odin";
 
-DEFAULT_BUFFER_SIZE :: 1<<12;
+
+_BUFFER_SIZE :: 1<<12;
 
 Buffer :: struct {
 	data:   []byte,
@@ -60,7 +61,7 @@ Fmt_Info :: struct {
 
 
 fprint :: proc(fd: os.Handle, args: ...any) -> int {
-	data: [DEFAULT_BUFFER_SIZE]byte;
+	data: [_BUFFER_SIZE]byte;
 	buf := Buffer{data[:], 0};
 	bprint(^buf, ...args);
 	os.write(fd, buf.data[:buf.length]);
@@ -68,14 +69,14 @@ fprint :: proc(fd: os.Handle, args: ...any) -> int {
 }
 
 fprintln :: proc(fd: os.Handle, args: ...any) -> int {
-	data: [DEFAULT_BUFFER_SIZE]byte;
+	data: [_BUFFER_SIZE]byte;
 	buf := Buffer{data[:], 0};
 	bprintln(^buf, ...args);
 	os.write(fd, buf.data[:buf.length]);
 	return buf.length;
 }
 fprintf :: proc(fd: os.Handle, fmt: string, args: ...any) -> int {
-	data: [DEFAULT_BUFFER_SIZE]byte;
+	data: [_BUFFER_SIZE]byte;
 	buf := Buffer{data[:], 0};
 	bprintf(^buf, fmt, ...args);
 	os.write(fd, buf.data[:buf.length]);
@@ -95,7 +96,7 @@ printf :: proc(fmt: string, args: ...any) -> int {
 
 
 fprint_type :: proc(fd: os.Handle, info: ^Type_Info) {
-	data: [DEFAULT_BUFFER_SIZE]byte;
+	data: [_BUFFER_SIZE]byte;
 	buf := Buffer{data[:], 0};
 	buffer_write_type(^buf, info);
 	os.write(fd, buf.data[:buf.length]);
@@ -174,8 +175,7 @@ buffer_write_type :: proc(buf: ^Buffer, ti: ^Type_Info) {
 		buffer_write_string(buf, "]");
 		buffer_write_type(buf, info.elem);
 	case Dynamic_Array:
-		buffer_write_string(buf, "[dynamic");
-		buffer_write_string(buf, "]");
+		buffer_write_string(buf, "[...]");
 		buffer_write_type(buf, info.elem);
 	case Slice:
 		buffer_write_string(buf, "[");
@@ -316,7 +316,7 @@ parse_int :: proc(s: string, offset: int) -> (int, int, bool) {
 	return result, offset+i, i != 0;
 }
 
-arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset: int, arg_count: int) -> (int, int, bool) {
+_arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset: int, arg_count: int) -> (int, int, bool) {
 	parse_arg_number :: proc(format: string) -> (int, int, bool) {
 		if format.count < 3 {
 			return 0, 1, false;
@@ -961,7 +961,7 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int {
 			}
 		}
 
-		arg_index, i, was_prev_index = arg_number(^fi, arg_index, fmt, i, args.count);
+		arg_index, i, was_prev_index = _arg_number(^fi, arg_index, fmt, i, args.count);
 
 		// Width
 		if i < end && fmt[i] == '*' {
@@ -991,7 +991,7 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int {
 				fi.good_arg_index = false;
 			}
 			if i < end && fmt[i] == '*' {
-				arg_index, i, was_prev_index = arg_number(^fi, arg_index, fmt, i, args.count);
+				arg_index, i, was_prev_index = _arg_number(^fi, arg_index, fmt, i, args.count);
 				i += 1;
 				fi.prec, arg_index, fi.prec_set = int_from_arg(args, arg_index);
 				if fi.prec < 0 {
@@ -1012,7 +1012,7 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int {
 		}
 
 		if !was_prev_index {
-			arg_index, i, was_prev_index = arg_number(^fi, arg_index, fmt, i, args.count);
+			arg_index, i, was_prev_index = _arg_number(^fi, arg_index, fmt, i, args.count);
 		}
 
 		if i >= end {

+ 2 - 2
core/sys/windows.odin

@@ -22,7 +22,7 @@ BOOL      :: i32;
 WNDPROC   :: #type proc(HWND, u32, WPARAM, LPARAM) -> LRESULT #cc_c;
 
 
-INVALID_HANDLE_VALUE :: cast(HANDLE)(~cast(int)0);
+INVALID_HANDLE_VALUE :: cast(HANDLE)~cast(int)0;
 
 FALSE: BOOL : 0;
 TRUE:  BOOL : 1;
@@ -288,7 +288,7 @@ InterlockedExchangeAdd64     :: proc(dst: ^i64, desired: i64) -> i64 #foreign ke
 InterlockedAnd64             :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32;
 InterlockedOr64              :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32;
 
-_mm_pause        :: proc() #foreign kernel32;
+mm_pause         :: proc() #foreign kernel32 "_mm_pause";
 ReadWriteBarrier :: proc() #foreign kernel32;
 WriteBarrier     :: proc() #foreign kernel32;
 ReadBarrier      :: proc() #foreign kernel32;

+ 44 - 13
src/check_expr.c

@@ -1081,6 +1081,11 @@ i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) {
 		return 0;
 	}
 	Operand o = {0};
+	if (e->kind == AstNode_UnaryExpr &&
+	    e->UnaryExpr.op.kind == Token_Question) {
+		return -1;
+	}
+
 	check_expr(c, &o, e);
 	if (o.mode != Addressing_Constant) {
 		if (o.mode != Addressing_Invalid) {
@@ -1314,7 +1319,12 @@ Type *check_type_extra(Checker *c, AstNode *e, Type *named_type) {
 	case_ast_node(at, ArrayType, e);
 		if (at->count != NULL) {
 			Type *elem = check_type_extra(c, at->elem, NULL);
-			type = make_type_array(c->allocator, elem, check_array_or_map_count(c, at->count, false));
+			i64 count = check_array_or_map_count(c, at->count, false);
+			if (count < 0) {
+				error_node(at->count, "? can only be used in conjuction with compound literals");
+				count = 0;
+			}
+			type = make_type_array(c->allocator, elem, count);
 		} else {
 			Type *elem = check_type(c, at->elem);
 			type = make_type_slice(c->allocator, elem);
@@ -2529,8 +2539,11 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 		add_entity_use(c, op_expr, e);
 		expr_entity = e;
 
-		if (e != NULL && e->kind == Entity_ImportName &&
-		    selector->kind == AstNode_Ident) {
+		if (e != NULL && e->kind == Entity_ImportName && selector->kind == AstNode_Ident) {
+			// IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile
+			// It pretty much needs to be in this order and this way
+			// If you can clean this up, please do but be really careful
+
 			String sel_name = selector->Ident.string;
 
 			check_op_expr = false;
@@ -2539,8 +2552,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 			if (is_declared) {
 				if (entity->kind == Entity_Builtin) {
 					is_declared = false;
-				}
-				if (entity->scope->is_global && !e->ImportName.scope->is_global) {
+				} else if (entity->scope->is_global && !e->ImportName.scope->is_global) {
 					is_declared = false;
 				}
 			}
@@ -2564,6 +2576,17 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 				}
 			}
 
+
+			is_not_exported = !is_entity_name_exported(entity);
+
+			if (is_not_exported) {
+				gbString sel_str = expr_to_string(selector);
+				error_node(op_expr, "`%s` is not exported by `%.*s`", sel_str, LIT(name));
+				gb_string_free(sel_str);
+				// NOTE(bill): We will have to cause an error his even though it exists
+				goto error;
+			}
+
 			if (is_overloaded) {
 				Scope *s = entity->scope;
 				bool skip = false;
@@ -2608,7 +2631,6 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
 			}
 
 			bool *found = map_bool_get(&e->ImportName.scope->implicit, hash_pointer(entity));
-
 			if (!found) {
 				is_not_exported = false;
 			} else {
@@ -4479,16 +4501,18 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 	case_ast_node(cl, CompoundLit, node);
 		Type *type = type_hint;
-		bool ellipsis_array = false;
+		bool is_to_be_determined_array_count = false;
 		bool is_constant = true;
 		if (cl->type != NULL) {
 			type = NULL;
 
 			// [..]Type
 			if (cl->type->kind == AstNode_ArrayType && cl->type->ArrayType.count != NULL) {
-				if (cl->type->ArrayType.count->kind == AstNode_Ellipsis) {
+				AstNode *count = cl->type->ArrayType.count;
+				if (count->kind == AstNode_UnaryExpr &&
+				    count->UnaryExpr.op.kind == Token_Question) {
 					type = make_type_array(c->allocator, check_type(c, cl->type->ArrayType.elem), -1);
-					ellipsis_array = true;
+					is_to_be_determined_array_count = true;
 				}
 			}
 
@@ -4663,7 +4687,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 				}
 			}
 
-			if (t->kind == Type_Array && ellipsis_array) {
+			if (t->kind == Type_Array && is_to_be_determined_array_count) {
 				t->Array.count = max;
 			}
 		} break;
@@ -5185,11 +5209,13 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 	case AstNode_ProcType:
 	case AstNode_PointerType:
 	case AstNode_ArrayType:
+	case AstNode_DynamicArrayType:
 	case AstNode_VectorType:
 	case AstNode_StructType:
 	case AstNode_UnionType:
 	case AstNode_RawUnionType:
 	case AstNode_EnumType:
+	case AstNode_MapType:
 		o->mode = Addressing_Type;
 		o->type = check_type(c, node);
 		break;
@@ -5417,7 +5443,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 	case_end;
 
 	case_ast_node(e, Ellipsis, node);
-		str = gb_string_appendc(str, "..");
+		str = gb_string_appendc(str, "...");
 	case_end;
 
 	case_ast_node(fv, FieldValue, node);
@@ -5433,13 +5459,18 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 
 	case_ast_node(at, ArrayType, node);
 		str = gb_string_appendc(str, "[");
-		str = write_expr_to_string(str, at->count);
+		if (at->count->kind == AstNode_UnaryExpr &&
+		    at->count->UnaryExpr.op.kind == Token_Hash) {
+			str = gb_string_appendc(str, "#");
+		} else {
+			str = write_expr_to_string(str, at->count);
+		}
 		str = gb_string_appendc(str, "]");
 		str = write_expr_to_string(str, at->elem);
 	case_end;
 
 	case_ast_node(at, DynamicArrayType, node);
-		str = gb_string_appendc(str, "[dynamic]");
+		str = gb_string_appendc(str, "[...]");
 		str = write_expr_to_string(str, at->elem);
 	case_end;
 

+ 37 - 3
src/checker.c

@@ -1531,6 +1531,8 @@ void check_all_global_entities(Checker *c) {
 		add_curr_ast_file(c, d->scope->file);
 
 		if (!d->scope->has_been_imported) {
+			// NOTE(bill): All of these unchecked entities could mean a lot of unused allocations
+			// TODO(bill): Should this be worried about?
 			continue;
 		}
 
@@ -1567,6 +1569,30 @@ void check_all_global_entities(Checker *c) {
 }
 
 
+bool is_string_an_identifier(String s) {
+	isize offset = 0;
+	if (s.len < 1) {
+		return false;
+	}
+	while (offset < s.len) {
+		bool ok = false;
+		Rune r = -1;
+		isize size = gb_utf8_decode(s.text+offset, s.len-offset, &r);
+		if (offset == 0) {
+			ok = rune_is_letter(r);
+		} else {
+			ok = rune_is_letter(r) || rune_is_digit(r);
+		}
+
+		if (!ok) {
+			return false;
+		}
+		offset += size;
+	}
+
+	return offset == s.len;
+}
+
 String path_to_entity_name(String name, String fullpath) {
 	if (name.len != 0) {
 		return name;
@@ -1677,9 +1703,17 @@ void check_import_entities(Checker *c, MapScope *file_scopes) {
 				case Entity_LibraryName:
 					break;
 				default: {
-					bool ok = add_entity(c, parent_scope, NULL, e);
-					if (ok && id->is_import) { // `#import`ed entities don't get exported
-						map_bool_set(&parent_scope->implicit, hash_pointer(e), true);
+
+					if (id->is_import) {
+						if (is_entity_name_exported(e)) {
+							// TODO(bill): Should these entities be imported but cause an error when used?
+							bool ok = add_entity(c, parent_scope, NULL, e);
+							if (ok) {
+								map_bool_set(&parent_scope->implicit, hash_pointer(e), true);
+							}
+						}
+					} else {
+						/* bool ok =  */add_entity(c, parent_scope, NULL, e);
 					}
 				} break;
 				}

+ 1 - 0
src/common.c

@@ -6,6 +6,7 @@ gbAllocator heap_allocator(void) {
 	return gb_heap_allocator();
 }
 
+#include "unicode.c"
 #include "string.c"
 #include "array.c"
 

+ 9 - 0
src/entity.c

@@ -98,6 +98,15 @@ struct Entity {
 
 gb_global Entity *e_context = NULL;
 
+bool is_entity_name_exported(Entity *e) {
+	GB_ASSERT(e != NULL);
+	String name = e->token.string;
+	if (name.len == 0) {
+		return false;
+	}
+	return name.text[0] != '_';
+}
+
 
 Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
 	Entity *entity = gb_alloc_item(a, Entity);

+ 0 - 1
src/main.c

@@ -4,7 +4,6 @@ extern "C" {
 
 #include "common.c"
 #include "timings.c"
-#include "unicode.c"
 #include "build.c"
 #include "tokenizer.c"
 #include "parser.c"

+ 9 - 4
src/parser.c

@@ -1832,6 +1832,7 @@ bool is_literal_type(AstNode *node) {
 	case AstNode_ArrayType:
 	case AstNode_VectorType:
 	case AstNode_StructType:
+	case AstNode_DynamicArrayType:
 	case AstNode_MapType:
 		return true;
 	}
@@ -2575,8 +2576,8 @@ AstNode *parse_type_or_ident(AstFile *f) {
 		AstNode *count_expr = NULL;
 		bool is_vector = false;
 
-		if (f->curr_token.kind == Token_Ellipsis) {
-			count_expr = ast_ellipsis(f, expect_token(f, Token_Ellipsis), NULL);
+		if (f->curr_token.kind == Token_Question) {
+			count_expr = ast_unary_expr(f, expect_token(f, Token_Question), NULL);
 		} else if (f->curr_token.kind == Token_vector) {
 			next_token(f);
 			if (f->curr_token.kind != Token_CloseBracket) {
@@ -2587,7 +2588,7 @@ AstNode *parse_type_or_ident(AstFile *f) {
 				syntax_error(f->curr_token, "Vector type missing count");
 			}
 			is_vector = true;
-		} else if (f->curr_token.kind == Token_dynamic) {
+		} else if (f->curr_token.kind == Token_Ellipsis) {
 			next_token(f);
 			expect_token(f, Token_CloseBracket);
 			return ast_dynamic_array_type(f, token, parse_type(f));
@@ -3527,6 +3528,7 @@ AstNodeArray parse_stmt_list(AstFile *f) {
 
 
 ParseFileError init_ast_file(AstFile *f, String fullpath) {
+	fullpath = string_trim_whitespace(fullpath); // Just in case
 	if (!string_has_extension(fullpath, str_lit("odin"))) {
 		return ParseFile_WrongExtension;
 	}
@@ -3606,6 +3608,9 @@ void destroy_parser(Parser *p) {
 bool try_add_import_path(Parser *p, String path, String rel_path, TokenPos pos) {
 	gb_mutex_lock(&p->mutex);
 
+	path = string_trim_whitespace(path);
+	rel_path = string_trim_whitespace(rel_path);
+
 	for_array(i, p->imports) {
 		String import = p->imports.e[i].path;
 		if (str_eq(import, path)) {
@@ -3786,7 +3791,7 @@ ParseFileError parse_files(Parser *p, char *init_filename) {
 				gb_printf_err("Invalid file extension: File must have the extension `.odin`");
 				break;
 			case ParseFile_InvalidFile:
-				gb_printf_err("Invalid file");
+				gb_printf_err("Invalid file or cannot be found");
 				break;
 			case ParseFile_Permission:
 				gb_printf_err("File permissions problem");

+ 27 - 6
src/string.c

@@ -145,16 +145,37 @@ gb_inline isize string_extension_position(String str) {
 	return dot_pos;
 }
 
+String string_trim_whitespace(String str) {
+	while (str.len > 0 && rune_is_whitespace(str.text[str.len-1])) {
+		str.len--;
+	}
+
+	while (str.len > 0 && rune_is_whitespace(str.text[0])) {
+		str.text++;
+		str.len--;
+	}
+
+	return str;
+}
+
 gb_inline bool string_has_extension(String str, String ext) {
-	if (str.len > ext.len+1) {
-		u8 *s = str.text+str.len - ext.len-1;
-		if (s[0] == '.') {
-			s++;
-			return gb_memcompare(s, ext.text, ext.len) == 0;
+	str = string_trim_whitespace(str);
+	if (str.len <= ext.len+1) {
+		return false;
+	}
+	isize len = str.len;
+	for (isize i = len-1; i >= 0; i--) {
+		if (str.text[i] == '.') {
+			break;
 		}
+		len--;
+	}
+	if (len == 0) {
 		return false;
 	}
-	return false;
+
+	u8 *s = str.text + len;
+	return gb_memcompare(s, ext.text, ext.len) == 0;
 }
 
 bool string_contains_char(String s, u8 c) {

+ 4 - 4
src/tokenizer.c

@@ -107,12 +107,12 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_enum,           "enum"),            \
 	TOKEN_KIND(Token_vector,         "vector"),          \
 	TOKEN_KIND(Token_map,            "map"),             \
-	TOKEN_KIND(Token_static,         "static"),          \
-	TOKEN_KIND(Token_dynamic,        "dynamic"),         \
+	/* TOKEN_KIND(Token_static,         "static"), */    \
+	/* TOKEN_KIND(Token_dynamic,        "dynamic"), */   \
 	TOKEN_KIND(Token_using,          "using"),           \
 	TOKEN_KIND(Token_no_alias,       "no_alias"),        \
-	/* TOKEN_KIND(Token_mutable,        "mutable"),  */  \
-	/* TOKEN_KIND(Token_immutable,      "immutable"),  */\
+	/* TOKEN_KIND(Token_mutable,        "mutable"), */   \
+	/* TOKEN_KIND(Token_immutable,      "immutable"), */ \
 	TOKEN_KIND(Token_thread_local,   "thread_local"),    \
 	TOKEN_KIND(Token_cast,           "cast"),            \
 	TOKEN_KIND(Token_transmute,      "transmute"),       \

+ 0 - 25
src/unicode.c

@@ -39,28 +39,3 @@ bool rune_is_whitespace(Rune r) {
 	}
 	return false;
 }
-
-
-bool is_string_an_identifier(String s) {
-	isize offset = 0;
-	if (s.len < 1) {
-		return false;
-	}
-	while (offset < s.len) {
-		bool ok = false;
-		Rune r = -1;
-		isize size = gb_utf8_decode(s.text+offset, s.len-offset, &r);
-		if (offset == 0) {
-			ok = rune_is_letter(r);
-		} else {
-			ok = rune_is_letter(r) || rune_is_digit(r);
-		}
-
-		if (!ok) {
-			return false;
-		}
-		offset += size;
-	}
-
-	return offset == s.len;
-}