use core {string, math}
+//
+// Converts a string into an integer. Works with positive and
+// negative integers. If given a pointer to a string, will
+// modify the string to extract the integer part.
str_to_i64 :: #match #local {}
#overload
return value * ~~mul;
}
+
+//
+// Converts a string to a floating point number.
str_to_f64 :: #match #local {}
#overload
}
+//
+// Converts an integer into a string using the buffer provided.
+// Supports upto base 64. If prefix is true, binary numbers are
+// prefixed with '0b' and hexadecimal numbers are prefixed with
+// '0x'.
i64_to_str :: (n: i64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
is_neg := false;
if n < 0 && base == 10 {
return str.{ data = c + 1, count = len };
}
+
+//
+// Converts an unsigned number into a string using the buffer provided.
+// Behaves like i64_to_str.
u64_to_str :: (n: u64, base: u64, buf: [] u8, min_length := 0, prefix := false) -> str {
c := ^buf[buf.count - 1];
len := 0;
return str.{ data = c + 1, count = len };
}
+//
+// Converts a floating point number into a string, using
+// the buffer provided.
+//
// This is better than what used to be, but still relies on converting the integer
// part of the float to an integer, which could overflow.
f64_to_str :: (f: f64, buf: [] u8, digits_after_decimal := 4) -> str {
// I like the way that parse_int reads better than 'str_to_i64'.
// For a soft transistion, I am allowing the programmer to use either.
-// At some point, it might be worth deprecating 'str_to_i64', but no
+// At some point, it might be worth deprecating 'str_to_i64', but no harm
// in leaving it here for now. Same thing applied to all other functions
// below.
parse_int :: str_to_i64
format :: #match {}
-#match format (buffer: [] u8, format: str, va: ..any) -> str {
+
+#overload
+format :: (buffer: [] u8, format: str, va: ..any) -> str {
return format_va(buffer, format, ~~va);
}
-#match format (output: ^Format_Output, format: str, va: ..any) -> str {
+
+#overload
+format :: (output: ^Format_Output, format: str, va: ..any) -> str {
return format_va(output, format, ~~va);
}
-#match format (buffer: ^[..] u8, format: str, va: ..any) {
+
+#overload
+format :: (buffer: ^[..] u8, format: str, va: ..any) {
buffer.count = buffer.capacity;
out := format_va(*buffer, format, ~~va);
buffer.count = out.count;
}
-#match format (format: str, va: ..any) -> str {
+
+#overload
+format :: (format: str, va: ..any) -> str {
buffer : [256] u8;
out := make([..] u8);
output := Format_Output.{
}
format_va :: #match {}
-#match format_va (buffer: [] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) -> str {
+
+#overload
+format_va :: (buffer: [] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) -> str {
output := Format_Output.{ buffer.data, 0, buffer.count, flush };
return format_va(^output, format, va);
}
-#match format_va (buffer: ^[..] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) {
+
+#overload
+format_va :: (buffer: ^[..] u8, format: str, va: [] any, flush := Format_Flush_Callback.{}) {
buffer.count = buffer.capacity;
out := format_va(*buffer, format, va, flush);
buffer.count = out.count;
}
-#match format_va (format: [] u8, va: [] any, allocator := context.allocator) -> str {
+
+#overload
+format_va :: (format: [] u8, va: [] any, allocator := context.allocator) -> str {
buffer : [256] u8;
out := make([..] u8, allocator=allocator);
output := Format_Output.{
return out;
}
-#match format_va (output: ^Format_Output, format: str, va: [] any) -> str {
+#overload
+format_va :: (output: ^Format_Output, format: str, va: [] any) -> str {
vararg_index := 0;
while i := 0; i < format.count {
return .{ output.data, output.count };
}
+
format_any :: (output: ^Format_Output, formatting: ^Format, v: any) {
use package runtime.info
array :: package core.array;
// x: i32;
// parse_any(^x, "12.34");
parse_any :: #match {}
-#match parse_any (v: any, to_parse: str, string_allocator := context.allocator) -> bool {
- use package runtime.info;
- info := get_type_info(v.type);
- if info.kind != .Pointer do return false;
-
- data_type := (cast(^Type_Info_Pointer) info).to;
- target := *cast(^rawptr) v.data;
- return parse_any(target, data_type, to_parse, string_allocator);
+#overload
+parse_any :: macro (v: ^$T, to_parse: str, string_allocator := context.allocator) -> bool {
+ return #this_package.parse_any(v, T, to_parse, string_allocator);
}
-#match parse_any (target: rawptr, data_type: type_expr, to_parse: str, string_allocator := context.allocator) -> bool {
+#overload
+parse_any :: (target: rawptr, data_type: type_expr, to_parse: str, string_allocator := context.allocator) -> bool {
if custom_parsers->has(data_type) {
return custom_parsers[data_type](target, to_parse, string_allocator);
}
package core.encoding.base64
+//
+// A simple Base64 encoding and decoding library. Currently
+// only supports base64 with + and / characters. A simple
+// find and replace could be used to change to other base64
+// standards.
+//
+
+//
+// Encodes the given data in base64 into a new buffer, allocated
+// from the allocator provided. It is the callers responsibilty
+// to free this memory.
encode :: (data: [] u8, allocator := context.allocator) -> [] u8 {
out := make([..] u8, allocator=allocator);
return out;
}
+//
+// Decodes the given base64 data into a new buffer, allocated
+// from the allocator provided.
decode :: (data: [] u8, allocator := context.allocator) -> [] u8 {
if data.count % 4 != 0 do return null_str;
package core.encoding.ini
+//
+// This library allows for parsing and formatting a simple 'ini' like
+// file format. It is very crude, but enables a quick way to create
+// a configuration file that can be mapped to a structure.
+//
+// The data for the ini file is stored in a 2-deep nested structure.
+// The outer structure stores the sections, and each section is
+// represented as another structure. For example,
+//
+// Config :: struct {
+// metadata: struct {
+// name: str;
+// version: str;
+// };
+//
+// player_settings: struct {
+// speed: f32;
+// display_name: str;
+// };
+// }
+//
+// This corresponds to an ini file with the following data:
+//
+// [metadata]
+// name=some name
+// version=0.0.1
+//
+// [player_settings]
+// speed=1.2
+// display_name=Player
+//
+
use core {
alloc, conv, string, io,
aprintf
}
+use core.intrinsics.types {
+ type_is_struct
+}
+//
+// Represents if the parsing was successful or encountered an error.
IniParseResult :: enum {
Success;
Error;
}
+//
+// Represents an error that occured while parsing.
IniParseError :: struct {
msg: str;
line: u32;
}
-parse_ini_file :: macro (r: ^io.Reader, output_ptr: ^$T) => {
+//
+// Parses an ini file into the variable given. Only structures
+// work with this procedure, as the structure provides the section
+// and value names.
+parse_ini_file :: macro (r: ^io.Reader, output_ptr: ^$T/type_is_struct) => {
parse_ini_file_inner :: parse_ini_file_inner
+
+ // No need to make the actual implementation polymorphic.
+ // That can work with an any, but this macro provides proper errors
+ // a pointer to a structure is not provided.
return parse_ini_file_inner(r, output_ptr);
}
return .Success, .{"",0};
}
-write_ini_file :: (w: ^io.Writer, output: any) -> bool {
+
+//
+// Outputs a two-level structure into an ini file. The inverse of `parse_ini_file`.
+write_ini_file :: macro (w: ^io.Writer, output: $T/type_is_struct) => {
+ write_ini_file_inner :: write_ini_file_inner
+
+ // See note above in parse_ini_file.
+ return write_ini_file_inner(w, output);
+}
+
+#local
+write_ini_file_inner :: (w: ^io.Writer, output: any) -> bool {
info :: runtime.info
output_info := cast(^info.Type_Info_Struct) info.get_type_info(output.type);