cOMS/log/Log.h

238 lines
6.2 KiB
C

/**
* Jingga
*
* @copyright Jingga
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
#ifndef TOS_LOG_H
#define TOS_LOG_H
#include "../stdlib/Types.h"
#include "../compiler/CompilerUtils.h"
#include "../utils/StringUtils.h"
#include "../platform/win32/TimeUtils.h"
#define LOG_DATA_ARRAY 5
#ifndef LOG_LEVEL
#define LOG_LEVEL 0
#endif
#ifndef MAX_LOG_LENGTH
#define MAX_LOG_LENGTH 256
#endif
#ifndef MAX_LOG_MESSAGES
#define MAX_LOG_MESSAGES 256
#endif
#if _WIN32
static HANDLE _log_fp;
#elif __linux__
static int32 _log_fp;
#endif
struct LogMemory {
byte* memory;
uint64 size;
uint64 pos;
};
static LogMemory* _log_memory = NULL;
enum LogDataType {
LOG_DATA_NONE,
LOG_DATA_VOID,
LOG_DATA_BYTE,
LOG_DATA_INT32,
LOG_DATA_UINT32,
LOG_DATA_INT64,
LOG_DATA_UINT64,
LOG_DATA_CHAR,
LOG_DATA_CHAR_STR,
LOG_DATA_FLOAT32,
LOG_DATA_FLOAT64
};
struct LogMessage {
const char* file;
const char* function;
int32 line;
uint64 time;
char* message;
};
struct LogData {
LogDataType type;
void* value;
};
struct LogDataArray{
LogData data[LOG_DATA_ARRAY];
};
// @bug This probably requires thread safety
byte* log_get_memory()
{
if (_log_memory->pos + MAX_LOG_LENGTH > _log_memory->size) {
_log_memory->pos = 0;
}
byte* offset = (byte *) (_log_memory->memory + _log_memory->pos);
memset((void *) offset, 0, MAX_LOG_LENGTH);
_log_memory->pos += MAX_LOG_LENGTH;
return offset;
}
void log_to_file()
{
// we don't log an empty log pool
if (!_log_memory || _log_memory->pos == 0 || !_log_fp) {
return;
}
#if _WIN32
DWORD written;
WriteFile(
_log_fp,
(char *) _log_memory->memory,
(uint32) _log_memory->pos,
&written,
NULL
);
#else
if (_log_fp < 0) {
return;
}
write(
_log_fp,
(char *) _log_memory->memory,
(uint32) _log_memory->pos
);
#endif
}
void log(const char* str, bool should_log, const char* file, const char* function, int32 line)
{
if (!should_log || !_log_memory) {
return;
}
int64 len = str_length(str);
while (len > 0) {
LogMessage* msg = (LogMessage *) log_get_memory();
// Fill file
msg->file = file;
msg->function = function;
msg->line = line;
msg->message = (char *) (msg + 1);
msg->time = system_time();
int32 message_length = (int32) OMS_MIN(MAX_LOG_LENGTH - sizeof(LogMessage) - 1, len);
memcpy(msg->message, str, message_length);
msg->message[message_length] = '\0';
str += message_length;
len -= MAX_LOG_LENGTH - sizeof(LogMessage);
if (_log_memory->size - _log_memory->pos < MAX_LOG_LENGTH) {
log_to_file();
_log_memory->pos = 0;
}
}
}
void log(const char* format, LogDataArray data, bool should_log, const char* file, const char* function, int32 line)
{
ASSERT_SIMPLE(str_length(format) + str_length(file) + str_length(function) + 50 < MAX_LOG_LENGTH);
if (!should_log || !_log_memory) {
return;
}
if (data.data[0].type == LOG_DATA_VOID) {
log(format, should_log, file, function, line);
return;
}
LogMessage* msg = (LogMessage *) log_get_memory();
msg->file = file;
msg->function = function;
msg->line = line;
msg->message = (char *) (msg + 1);
msg->time = system_time();
char temp_format[MAX_LOG_LENGTH];
str_copy_short(msg->message, format);
for (int32 i = 0; i < LOG_DATA_ARRAY; ++i) {
if (data.data[i].type == LOG_DATA_VOID) {
break;
}
str_copy_short(temp_format, msg->message);
switch (data.data[i].type) {
case LOG_DATA_NONE: {
} break;
case LOG_DATA_BYTE: {
sprintf_fast_iter(msg->message, temp_format, (int32) *((byte *) data.data[i].value));
} break;
case LOG_DATA_INT32: {
sprintf_fast_iter(msg->message, temp_format, *((int32 *) data.data[i].value));
} break;
case LOG_DATA_UINT32: {
sprintf_fast_iter(msg->message, temp_format, *((uint32 *) data.data[i].value));
} break;
case LOG_DATA_INT64: {
sprintf_fast_iter(msg->message, temp_format, *((int64 *) data.data[i].value));
} break;
case LOG_DATA_UINT64: {
sprintf_fast_iter(msg->message, temp_format, *((uint64 *) data.data[i].value));
} break;
case LOG_DATA_CHAR: {
sprintf_fast_iter(msg->message, temp_format, *((char *) data.data[i].value));
} break;
case LOG_DATA_CHAR_STR: {
sprintf_fast_iter(msg->message, temp_format, (const char *) data.data[i].value);
} break;
case LOG_DATA_FLOAT32: {
sprintf_fast_iter(msg->message, temp_format, *((f32 *) data.data[i].value));
} break;
case LOG_DATA_FLOAT64: {
sprintf_fast_iter(msg->message, temp_format, *((f64 *) data.data[i].value));
} break;
default: {
UNREACHABLE();
}
}
}
if (_log_memory->size - _log_memory->pos < MAX_LOG_LENGTH) {
log_to_file();
_log_memory->pos = 0;
}
}
#define LOG(should_log, str) log((str), (should_log), __FILE__, __func__, __LINE__)
#define LOG_FORMAT(should_log, format, ...) log((format), LogDataArray{__VA_ARGS__}, (should_log), __FILE__, __func__, __LINE__)
#define LOG_TO_FILE() log_to_file()
#if LOG_LEVEL == 2
#define LOG_LEVEL_1(format, ...) log((format), LogDataArray{__VA_ARGS__}, true, __FILE__, __func__, __LINE__)
#define LOG_LEVEL_2(format, ...) log((format), LogDataArray{__VA_ARGS__}, true, __FILE__, __func__, __LINE__)
#elif LOG_LEVEL == 1
#define LOG_LEVEL_1(format, ...) log((format), LogDataArray{__VA_ARGS__}, true, __FILE__, __func__, __LINE__)
#define LOG_LEVEL_2(format, ...) ((void) 0)
#elif LOG_LEVEL == 0
#define LOG_LEVEL_1(format, ...) ((void) 0)
#define LOG_LEVEL_2(format, ...) ((void) 0)
#endif
#endif