aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2018-12-14 14:35:48 +0000
committergingerBill <bill@gingerbill.org>2018-12-14 14:35:48 +0000
commit542e524a874ec1f17ea5cdcbebd9add030ab5551 (patch)
treee8c8c44e6500176648fa6914cfd61344548584e9
parentb54c35639b288b926192cdc6ad25ab985289d66b (diff)
parent1a6b7f99454ba67fab0e69ddc46e20aff94c8320 (diff)
Merge branch 'master' of https://github.com/odin-lang/Odin
-rw-r--r--.gitignore3
-rw-r--r--core/log/file_console_logger.odin142
-rw-r--r--core/log/log.odin72
-rw-r--r--core/os/os_linux.odin79
-rw-r--r--core/time/time_linux.odin45
-rw-r--r--core/time/time_osx.odin3
-rw-r--r--core/time/time_windows.odin4
7 files changed, 317 insertions, 31 deletions
diff --git a/.gitignore b/.gitignore
index 0c3e8b65a..a56b4f1e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,7 +18,7 @@ bld/
[Bb]in/
[Oo]bj/
[Ll]og/
-
+![Cc]ore/[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
@@ -264,7 +264,6 @@ bin/
odin
odin.dSYM
-
# shared collection
shared/
diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin
new file mode 100644
index 000000000..1d598285e
--- /dev/null
+++ b/core/log/file_console_logger.odin
@@ -0,0 +1,142 @@
+package log
+
+import "core:fmt";
+import "core:os";
+import "core:time";
+
+Level_Headers := []string{
+ "[DEBUG] --- ",
+ "[INFO ] --- ",
+ "[WARN ] --- ",
+ "[ERROR] --- ",
+ "[FATAL] --- ",
+};
+
+Default_Console_Logger_Opts :: Options{
+ Option.Level,
+ Option.Terminal_Color,
+ Option.Short_File_Path,
+ Option.Line,
+ Option.Procedure,
+} | Full_Timestamp_Opts;
+
+Default_File_Logger_Opts :: Options{
+ Option.Level,
+ Option.Short_File_Path,
+ Option.Line,
+ Option.Procedure,
+} | Full_Timestamp_Opts;
+
+
+File_Console_Logger_Data :: struct {
+ lowest_level: Level,
+ file_handle: os.Handle,
+ ident : string,
+}
+
+create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
+ data := new(File_Console_Logger_Data);
+ data.lowest_level = lowest;
+ data.file_handle = h;
+ data.ident = ident;
+ return Logger{file_console_logger_proc, data, opt};
+}
+
+destroy_file_logger ::proc(log : ^Logger) {
+ data := cast(^File_Console_Logger_Data)log.data;
+ if data.file_handle != os.INVALID_HANDLE do os.close(data.file_handle);
+ free(data);
+ log^ = nil_logger();
+}
+
+create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
+ data := new(File_Console_Logger_Data);
+ data.lowest_level = lowest;
+ data.file_handle = os.INVALID_HANDLE;
+ data.ident = ident;
+ return Logger{file_console_logger_proc, data, opt};
+}
+
+destroy_console_logger ::proc(log : ^Logger) {
+ free(log.data);
+ log^ = nil_logger();
+}
+
+file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
+ data := cast(^File_Console_Logger_Data)logger_data;
+ if level < data.lowest_level do return;
+
+ h : os.Handle;
+ if(data.file_handle != os.INVALID_HANDLE) do h = data.file_handle;
+ else do h = level <= Level.Error ? os.stdout : os.stderr;
+ backing: [1024]byte; //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
+ buf := fmt.string_buffer_from_slice(backing[:]);
+
+ do_level_header(options, level, &buf);
+
+ when time.IS_SUPPORTED {
+ if Full_Timestamp_Opts & options != nil {
+ fmt.sbprint(&buf, "[");
+ t := time.now();
+ y, m, d := time.date(t);
+ h, min, s := time.clock(t);
+ if Option.Date in options do fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d);
+ if Option.Time in options do fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s);
+ fmt.sbprint(&buf, "] ");
+ }
+ }
+
+ do_location_header(options, &buf, location);
+
+ if data.ident != "" do fmt.sbprintf(&buf, "[%s] ", data.ident);
+ //TODO(Hoej): When we have better atomics and such, make this thread-safe
+ fmt.fprintf(h, "%s %s\n", fmt.to_string(buf), text);
+}
+
+do_level_header :: proc(opts : Options, level : Level, buf : ^fmt.String_Buffer) {
+
+ RESET :: "\x1b[0m";
+ RED :: "\x1b[31m";
+ YELLOW :: "\x1b[33m";
+ DARK_GREY :: "\x1b[90m";
+
+ col := RESET;
+ switch level {
+ case Level.Debug : col = DARK_GREY;
+ case Level.Info : col = RESET;
+ case Level.Warning : col = YELLOW;
+ case Level.Error, Level.Fatal : col = RED;
+ }
+
+ if Option.Level in opts {
+ if Option.Terminal_Color in opts do fmt.sbprint(buf, col);
+ fmt.sbprint(buf, Level_Headers[level]);
+ if Option.Terminal_Color in opts do fmt.sbprint(buf, RESET);
+ }
+}
+
+do_location_header :: proc(opts : Options, buf : ^fmt.String_Buffer, location := #caller_location) {
+ if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
+
+ file := location.file_path;
+ if Option.Short_File_Path in opts {
+ when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/';
+ last := 0;
+ for r, i in location.file_path do if r == delimiter do last = i+1;
+ file = location.file_path[last:];
+ }
+
+ if Location_File_Opts & opts != nil do fmt.sbprint(buf, file);
+
+ if Option.Procedure in opts {
+ if Location_File_Opts & opts != nil do fmt.sbprint(buf, ".");
+ fmt.sbprintf(buf, "%s()", location.procedure);
+ }
+
+ if Option.Line in opts {
+ if Location_File_Opts & opts != nil || Option.Procedure in opts do fmt.sbprint(buf, ":");
+ fmt.sbprint(buf, location.line);
+ }
+
+ fmt.sbprint(buf, "] ");
+} \ No newline at end of file
diff --git a/core/log/log.odin b/core/log/log.odin
index fac2fbe5b..4a3af3cd2 100644
--- a/core/log/log.odin
+++ b/core/log/log.odin
@@ -1,5 +1,8 @@
package log
+import "core:fmt";
+import "core:runtime";
+
Level :: enum {
Debug,
Info,
@@ -9,26 +12,79 @@ Level :: enum {
}
Option :: enum {
- Level,
- Time,
- File,
- Line,
- Procedure,
+ Level,
+ Date,
+ Time,
+ Short_File_Path,
+ Long_File_Path,
+ Line,
+ Procedure,
+ Terminal_Color
}
+
Options :: bit_set[Option];
+Full_Timestamp_Opts :: Options{
+ Option.Date,
+ Option.Time
+};
+Location_Header_Opts :: Options{
+ Option.Short_File_Path,
+ Option.Long_File_Path,
+ Option.Line,
+ Option.Procedure,
+};
+Location_File_Opts :: Options{
+ Option.Short_File_Path,
+ Option.Long_File_Path
+};
-Logger_Proc :: #type proc(data: rawptr, level: Level, ident, text: string, options: Options, location := #caller_location);
+Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
Logger :: struct {
procedure: Logger_Proc,
data: rawptr,
+ options: Options,
+}
+
+Multi_Logger_Data :: struct {
+ loggers : []Logger,
}
+create_multi_logger :: proc(logs: ..Logger) -> Logger {
+ data := new(Multi_Logger_Data);
+ data.loggers = make([]Logger, len(logs));
+ copy(data.loggers, logs);
+ return Logger{multi_logger_proc, data, nil};
+}
-nil_logger_proc :: proc(data: rawptr, level: Level, ident, text: string, options: Options, location := #caller_location) {
+destroy_multi_logger ::proc(log : ^Logger) {
+ free(log.data);
+ log^ = nil_logger();
+}
+
+multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
+ options: Options, location := #caller_location) {
+ data := cast(^Multi_Logger_Data)logger_data;
+ if data.loggers == nil || len(data.loggers) == 0 do return;
+ for log in data.loggers do log.procedure(log.data, level, text, log.options, location);
+}
+
+nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
// Do nothing
}
nil_logger :: proc() -> Logger {
- return Logger{nil_logger_proc, nil};
+ return Logger{nil_logger_proc, nil, nil};
}
+
+debug :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug, fmt_str=fmt_str, args=args, location=location);
+info :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info, fmt_str=fmt_str, args=args, location=location);
+warn :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location);
+error :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Error, fmt_str=fmt_str, args=args, location=location);
+fatal :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal, fmt_str=fmt_str, args=args, location=location);
+
+logf :: proc(level : Level, fmt_str : string, args : ..any, location := #caller_location) {
+ logger := context.logger;
+ str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
+ logger.procedure(logger.data, level, str, logger.options, location);
+} \ No newline at end of file
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index a8d241d65..1ca41ee77 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -125,29 +125,49 @@ X_OK :: 1; // Test for execute permission
W_OK :: 2; // Test for write permission
R_OK :: 4; // Test for read permission
+TimeSpec :: struct {
+ tv_sec : i64, /* seconds */
+ tv_nsec : i64, /* nanoseconds */
+};
+
+CLOCK_REALTIME :: 0;
+CLOCK_MONOTONIC :: 1;
+CLOCK_PROCESS_CPUTIME_ID :: 2;
+CLOCK_THREAD_CPUTIME_ID :: 3;
+CLOCK_MONOTONIC_RAW :: 4;
+CLOCK_REALTIME_COARSE :: 5;
+CLOCK_MONOTONIC_COARSE :: 6;
+CLOCK_BOOTTIME :: 7;
+CLOCK_REALTIME_ALARM :: 8;
+CLOCK_BOOTTIME_ALARM :: 9;
+
foreign libc {
- @(link_name="open") _unix_open :: proc(path: cstring, mode: int) -> Handle ---;
- @(link_name="close") _unix_close :: proc(fd: Handle) -> i32 ---;
- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
- @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
- @(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
- @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> i32 ---;
- @(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> i32 ---;
-
- @(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
- @(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
- @(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
-
- @(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
+ @(link_name="open") _unix_open :: proc(path: cstring, mode: int) -> Handle ---;
+ @(link_name="close") _unix_close :: proc(fd: Handle) -> i32 ---;
+ @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
+ @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
+ @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
+ @(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
+ @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> i32 ---;
+ @(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> i32 ---;
+
+ @(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
+ @(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
+ @(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
+ @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
+ @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
+
+ @(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) ---;
+ @(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> int ---;
+ @(link_name="sleep") _unix_sleep :: proc(seconds: u64) -> int ---;
+
+ @(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
}
foreign dl {
- @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
- @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
- @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
- @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
+ @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
+ @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
+ @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
+ @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
}
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
@@ -245,6 +265,25 @@ exit :: proc(code: int) -> ! {
_unix_exit(code);
}
+clock_gettime :: proc(clock_id: u64) -> TimeSpec {
+ ts : TimeSpec;
+ _unix_clock_gettime(clock_id, &ts);
+ return ts;
+}
+
+sleep :: proc(seconds: u64) -> int {
+
+ return _unix_sleep(seconds);
+}
+
+nanosleep :: proc(nanoseconds: i64) -> int {
+ assert(nanoseconds <= 999999999);
+ requested, remaining : TimeSpec;
+ requested = TimeSpec{tv_nsec = nanoseconds};
+
+ return _unix_nanosleep(&requested, &remaining);
+}
+
current_thread_id :: proc "contextless" () -> int {
// return int(_unix_gettid());
return 0;
diff --git a/core/time/time_linux.odin b/core/time/time_linux.odin
new file mode 100644
index 000000000..af19d9706
--- /dev/null
+++ b/core/time/time_linux.odin
@@ -0,0 +1,45 @@
+package time
+
+import "core:os";
+import "core:fmt";
+
+// NOTE(Jeroen): The times returned are in UTC
+IS_SUPPORTED :: true;
+
+now :: proc() -> Time {
+
+ time_spec_now := os.clock_gettime(os.CLOCK_REALTIME);
+ ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec;
+ return Time{_nsec=ns};
+}
+
+boot_time :: proc() -> Time {
+
+ ts_now := os.clock_gettime(os.CLOCK_REALTIME);
+ ts_boottime := os.clock_gettime(os.CLOCK_BOOTTIME);
+
+ ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec;
+ return Time{_nsec=ns};
+}
+
+seconds_since_boot :: proc() -> f64 {
+
+ ts_boottime := os.clock_gettime(os.CLOCK_BOOTTIME);
+ return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9;
+}
+
+sleep :: proc(d: Duration) {
+
+ ds := duration_seconds(d);
+ seconds := u64(ds);
+ nanoseconds := i64((ds - f64(seconds)) * 1e9);
+
+ if seconds > 0 do os.sleep(seconds);
+ if nanoseconds > 0 do os.nanosleep(nanoseconds);
+}
+
+nanosleep :: proc(d: Duration) {
+ // NOTE(Jeroen): os.nanosleep returns -1 on failure, 0 on success
+ // duration needs to be [0, 999999999] nanoseconds.
+ os.nanosleep(i64(d));
+}
diff --git a/core/time/time_osx.odin b/core/time/time_osx.odin
new file mode 100644
index 000000000..7d1a1441c
--- /dev/null
+++ b/core/time/time_osx.odin
@@ -0,0 +1,3 @@
+package time
+
+IS_SUPPORTED :: false; \ No newline at end of file
diff --git a/core/time/time_windows.odin b/core/time/time_windows.odin
index 6789a59d1..31fbca769 100644
--- a/core/time/time_windows.odin
+++ b/core/time/time_windows.odin
@@ -2,6 +2,8 @@ package time
import "core:sys/win32"
+IS_SUPPORTED :: true;
+
now :: proc() -> Time {
file_time: win32.Filetime;
@@ -18,5 +20,5 @@ now :: proc() -> Time {
sleep :: proc(d: Duration) {
- win32.Sleep(u32(d/Millisecond));
+ win32.sleep(i32(d/Millisecond));
}