Str8 Str8_Wrap(S64 count, U8 *data) {
	Str8 result;
	result.data  = data;
	result.count = count;

	return result;
}

Str8 Str8_WrapRange(U8 *start, U8 *end) {
	Str8 result;
	result.data  = start;
	result.count = cast(S64) (end - start);

	return result;
}

internal S64 Str8_CountZ(U8 *data) {
	S64 result = 0;
	while (data[result] != 0) { result += 1; }

	return result;
}

Str8 Str8_WrapZ(U8 *data) {
	Str8 result;
	result.data  = data;
	result.count = Str8_CountZ(data);

	return result;
}

#define FNV_OFFSET_BIAS ((U64) 0xCBF29CE484222325)
#define FNV_PRIME       ((U64) 0x100000001B3)

U64 Str8_Hash(Str8 v) {
	U64 result = FNV_OFFSET_BIAS;

	for (S64 it = 0; it < v.count; ++it) {
		result ^= v.data[it];
		result *= FNV_PRIME;
	}

	return result;
}

Str8 Str8_Copy(M_Arena *arena, Str8 s) {
	Str8 result;
	result.count = s.count;
	result.data  = M_ArenaPush(arena, U8, .count = s.count + 1);

	M_CopySize(result.data, s.data, s.count);
	return result;
}

Str8 Str8_Format(M_Arena *arena, const char *format, ...) {
	va_list args;
	va_start(args, format);

	Str8 result = Str8_FormatArgs(arena, format, args);
	va_end(args);

	return result;
}

internal S64 Str8_ProcessFormat(Str8 out, const char *format, va_list args) {
	S64 result = vsnprintf((char *) out.data, (int) out.count, format, args);
	return result;
}

Str8 Str8_FormatArgs(M_Arena *arena, const char *format, va_list args) {
	Str8 result;

	va_list copy;
	va_copy(copy, args);

	U64 offset = M_ArenaOffset(arena);

	result.count = KB(1);
	result.data  = M_ArenaPush(arena, U8, .count = result.count);

	S64 needed = Str8_ProcessFormat(result, format, args);
	if (needed >= result.count) {
		M_ArenaPop(arena, offset);

		result.count = needed;
		result.data  = M_ArenaPush(arena, U8, .count = result.count + 1);

		Str8_ProcessFormat(result, format, copy);
	}
	else {
		U64 size = result.count - needed - 1;
		M_ArenaPopSize(arena, size);

		result.count = needed;
	}

	return result;
}

B32 Str8_Equal(Str8 a, Str8 b, Str8_EqualFlags flags) {
	B32 result = (a.count == b.count);

	if (result) {
		B32 ignore_case = (flags & STR8_EQUAL_IGNORE_CASE) != 0;

		for (S64 it = 0; it < a.count; ++it) {
			U8 ac = ignore_case ? Chr_ToLowercase(a.data[it]) : a.data[it];
			U8 bc = ignore_case ? Chr_ToLowercase(b.data[it]) : b.data[it];

			if (ac != bc) {
				result = false;
				break;
			}
		}
	}

	return result;
}

Str8 Str8_Prefix(Str8 s, S64 count) {
	Str8 result;
	result.data  = s.data;
	result.count = Min(s.count, count);

	return result;
}

Str8 Str8_Suffix(Str8 s, S64 count) {
	Str8 result;
	result.count = Min(s.count, count);
	result.data  = s.data + (s.count - result.count);

	return result;
}

Str8 Str8_RemoveAfterLast(Str8 s, U8 c) {
	Str8 result;
	result.data  = s.data;
	result.count = s.count;

	for (S64 it = s.count - 1; it >= 0; --it) {
		if (result.data[it] == c) {
			result.count = it;
			break;
		}
	}

	return result;
}

B32 Str8_EndsWith(Str8 s, Str8 suffix) {
	B32 result = Str8_Equal(Str8_Suffix(s, suffix.count), suffix, 0);
	return result;
}

U8 Chr_ToUppercase(U8 c) {
	U8 result = (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;
	return result;
}

U8 Chr_ToLowercase(U8 c) {
	U8 result = (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
	return result;
}
