Compare commits
35 Commits
generic-pr
...
master
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef _COMP_H
|
||||||
|
#define _COMP_H
|
||||||
|
|
||||||
|
#include <macros.h>
|
||||||
|
#include <ints.h>
|
||||||
|
#include <slice.h>
|
||||||
|
#include <map.h>
|
||||||
|
|
||||||
|
typedef struct comp_aggregate {
|
||||||
|
bool all_matched;
|
||||||
|
|
||||||
|
int idx_first_failure;
|
||||||
|
|
||||||
|
usize num_results;
|
||||||
|
struct comp_single_result {
|
||||||
|
const map_t* _shared mapping;
|
||||||
|
|
||||||
|
bool matched;
|
||||||
|
usize slice_start;
|
||||||
|
} aggr[];
|
||||||
|
} comp_multires_t;
|
||||||
|
|
||||||
|
// Create a `slice_t` from a successful match against `needle`.
|
||||||
|
_mixin bool comp_slice_of(const map_t*pIN needle, const struct comp_single_result *pIN match, slice_t *pOUT slice)
|
||||||
|
{
|
||||||
|
ifL(match->matched) {
|
||||||
|
*slice = SLICE(AS(match->mapping->origin,u8*)+match->slice_start, needle->len);
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find `needle` in `haystack`, sets the start of `needle`'s `origin` in `haystack`'s to `pos` and returns `true` if a match is found, otherwise returns false.
|
||||||
|
bool cmp_find(const map_t *pIN needle, const map_t *pIN haystack, usize *pOUT pos);
|
||||||
|
/// Check `needle` against `nhaystacks` number of haystack maps, store the offset in `sizes[n]` where `n` is the haystack number.
|
||||||
|
/// `sizes` must be at least `nhaystacks` long. The haystacks themselves are expected to be of type `const map_t* pIN`
|
||||||
|
/// Returns `-1` if all haystacks passed match, otherwise returns the index of the haystack that failed.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// Will `FATAL` if any of the haystack pointers are `NULL`
|
||||||
|
int cmp_find_many(const map_t *pIN needle, usize nhaystacks, usize sizes[pOUT nhaystacks], ...);
|
||||||
|
|
||||||
|
// The return value of this function must be `free()`d after use
|
||||||
|
comp_multires_t* comp_match_all(map_t *pIN needle, usize nh, map_t hs[pIN nh], bool abort_on_fail);
|
||||||
|
|
||||||
|
#endif /* _COMP_H */
|
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef _DISPLAY_H
|
||||||
|
#define _DISPLAY_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "ints.h"
|
||||||
|
#include "slice.h"
|
||||||
|
|
||||||
|
typedef enum display_kind {
|
||||||
|
DISPK_NORMAL = 0,
|
||||||
|
DISPK_CSV,
|
||||||
|
DISPK_BINARY,
|
||||||
|
DISPK_INI,
|
||||||
|
} dispkind_t;
|
||||||
|
|
||||||
|
#define X(n) AS(1lu << (n), usize)
|
||||||
|
typedef enum display_flags {
|
||||||
|
// Show the number (index) of they haystack matched
|
||||||
|
DISPF_SHOW_NUMBER = X(0),
|
||||||
|
// Show the slice
|
||||||
|
DISPF_SHOW_SLICE = X(1),
|
||||||
|
// Show the length of the slice
|
||||||
|
DISPF_SHOW_LENGTH = X(2),
|
||||||
|
// Show failed matches
|
||||||
|
DISPF_SHOW_FAILURES = X(3),
|
||||||
|
} dispflags_t;
|
||||||
|
#define DISPLAY_FLAGS_DEFAULT (DISPF_SHOW_SLICE | DISPF_SHOW_NUMBER | DISPF_SHOW_FAILURES)
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool matched;
|
||||||
|
slice_t cslice;
|
||||||
|
const void* base;
|
||||||
|
usize index;
|
||||||
|
} dispin_t;
|
||||||
|
|
||||||
|
void display_result(FILE* output, dispin_t input, dispkind_t how, dispflags_t flags);
|
||||||
|
|
||||||
|
#endif /* _DISPLAY_H */
|
@ -0,0 +1,75 @@
|
|||||||
|
//! Memory mapping abstraction
|
||||||
|
#ifndef _MAP_H
|
||||||
|
#define _MAP_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "ints.h"
|
||||||
|
|
||||||
|
typedef enum mm_err {
|
||||||
|
MAP_ERR_UNKNOWN = 0,
|
||||||
|
MAP_SUCCESS = 1,
|
||||||
|
|
||||||
|
MAP_ERR_MMAP,
|
||||||
|
MAP_ERR_UNMAP,
|
||||||
|
MAP_ERR_OPEN,
|
||||||
|
MAP_ERR_CLOSE,
|
||||||
|
MAP_ERR_STAT,
|
||||||
|
MAP_ERR_ADVISE,
|
||||||
|
} map_result_t;
|
||||||
|
|
||||||
|
typedef union memory_map {
|
||||||
|
// Common
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
void* origin;
|
||||||
|
u8* area;
|
||||||
|
};
|
||||||
|
usize len;
|
||||||
|
};
|
||||||
|
// Specific to anon / file
|
||||||
|
struct mm_file {
|
||||||
|
void* origin;
|
||||||
|
usize len;
|
||||||
|
|
||||||
|
off_t fd_offset;
|
||||||
|
int fd;
|
||||||
|
} file;
|
||||||
|
struct mm_anon {
|
||||||
|
void* origin;
|
||||||
|
usize len;
|
||||||
|
} anon;
|
||||||
|
|
||||||
|
} map_t;
|
||||||
|
|
||||||
|
map_result_t map_fd(int fd, bool write, usize size, off_t offset, map_t *pOUT map);
|
||||||
|
/// Specify size as 0 to use the file's full size for the map.
|
||||||
|
map_result_t map_file(const char* file, bool write, usize size, off_t offset, map_t *pOUT map);
|
||||||
|
map_result_t map_anon(void* ptr, usize len, map_t *pOUT map);
|
||||||
|
map_result_t map_free(map_t map);
|
||||||
|
map_result_t map_preload(map_t *pINOUT map, bool random);
|
||||||
|
|
||||||
|
const char* map_error_str(map_result_t res);
|
||||||
|
|
||||||
|
_mixin int _map_handle_err(const char* stmt, const char* file, int line, const char* func, map_result_t res)
|
||||||
|
{
|
||||||
|
if(res == MAP_SUCCESS) return 1;
|
||||||
|
else COLD_BLOCK {
|
||||||
|
const char* err = map_error_str(res);
|
||||||
|
ERROR("mapping failed at %s:%d->%s(): on stmt `%s`", file, line, func, stmt);
|
||||||
|
switch(res) {
|
||||||
|
// Non-errno errors
|
||||||
|
case MAP_ERR_UNKNOWN:
|
||||||
|
case MAP_SUCCESS:
|
||||||
|
fprintf(stderr, err);
|
||||||
|
if(0)
|
||||||
|
// errno errors
|
||||||
|
default:
|
||||||
|
perror(err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
#define map_handle_err(stmt) _map_handle_err(#stmt, __FILE__, __LINE__, __func__, stmt)
|
||||||
|
|
||||||
|
#endif /* _MAP_H */
|
@ -0,0 +1,53 @@
|
|||||||
|
//! Generic pointer slice
|
||||||
|
#ifndef _SLICE_H
|
||||||
|
#define _SLICE_H
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "ints.h"
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
typedef struct _slice {
|
||||||
|
union {
|
||||||
|
void* ptr;
|
||||||
|
u8* bytes;
|
||||||
|
};
|
||||||
|
usize len;
|
||||||
|
} slice_t;
|
||||||
|
|
||||||
|
#define SLICE_MAX ((struct _slice){ .ptr = AS(UINTPTR_MAX, void*), .len = SIZE_MAX })
|
||||||
|
|
||||||
|
#define SLICE(origin, length) ((struct _slice){ .ptr = ((void*)(origin)), .len = ((usize)(length)) })
|
||||||
|
|
||||||
|
_mixin usize slice_end(const slice_t*pIN slice)
|
||||||
|
{
|
||||||
|
return AS(slice->bytes + slice->len, usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
_mixin usize slice_start(const slice_t*pIN slice)
|
||||||
|
{
|
||||||
|
return AS(slice->bytes, usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
_mixin struct { usize start, end; } slice_sted(const slice_t*pIN slice)
|
||||||
|
{
|
||||||
|
return (var(slice_sted(NULL))){
|
||||||
|
.start = slice_start(slice),
|
||||||
|
.end = slice_end(slice),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format string for slices
|
||||||
|
#define SLICE_FORMAT "%lu..%lu"
|
||||||
|
// Expects a pointer to a `slice_t`
|
||||||
|
#define SLICE_FORMAT_ARGS(slice) slice_start(slice), slice_end(slice)
|
||||||
|
|
||||||
|
#define SLICE_STR_MAX_SIZE 42lu
|
||||||
|
usize slice_strn(const slice_t*pIN slice, usize len, char str[static pOUT len]);
|
||||||
|
usize slice_str_sz(const slice_t*pIN slice);
|
||||||
|
// Pointer returned from this function is TLS static and will change on each call.
|
||||||
|
const char* slice_strs(const slice_t*pIN slice);
|
||||||
|
// Pointer returned from this function must be free'd
|
||||||
|
char* slice_str(const slice_t*pIN slice);
|
||||||
|
|
||||||
|
#endif /* _SLICE_H */
|
@ -0,0 +1,37 @@
|
|||||||
|
//! Trace messages
|
||||||
|
#ifndef _TRACE_H
|
||||||
|
#define _TRACE_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
enum trace_level {
|
||||||
|
TRACE_LEVEL_DEBUG = 0,
|
||||||
|
TRACE_LEVEL_INFO,
|
||||||
|
TRACE_LEVEL_WARN,
|
||||||
|
TRACE_LEVEL_ERROR,
|
||||||
|
TRACE_LEVEL_FATAL,
|
||||||
|
|
||||||
|
_TRACE_LEVEL_NUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define _TRACE_LEVEL_DEFAULT TRACE_LEVEL_DEBUG
|
||||||
|
#else
|
||||||
|
#define _TRACE_LEVEL_DEFAULT TRACE_LEVEL_WARN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int _t_fprintf(enum trace_level l, FILE* output, const char* msg, ...);
|
||||||
|
|
||||||
|
enum trace_level trace_max_level();
|
||||||
|
void trace_init_with(enum trace_level max_level);
|
||||||
|
// Initialise the tracer with env-var `_TRACE_CONTROL_ENV_NAME`. If the env-var is not found, the max trace level is not modified. If it is found but has an invalid value, this function returns 0. Otherwise it modifies the max trace level value and returns 1.
|
||||||
|
int trace_init();
|
||||||
|
|
||||||
|
const char* trace_name_of(enum trace_level lv);
|
||||||
|
|
||||||
|
extern const char* const _TRACE_CONTROL_ENV_NAME;
|
||||||
|
extern const char* const _trace_level_descriptions[_TRACE_LEVEL_NUM];
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _TRACE_H */
|
@ -0,0 +1,89 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <macros.h>
|
||||||
|
#include <ints.h>
|
||||||
|
#include <map.h>
|
||||||
|
|
||||||
|
#include <comp.h>
|
||||||
|
|
||||||
|
|
||||||
|
bool cmp_find(const map_t *pIN needle, const map_t *pIN haystack, usize *pOUT pos)
|
||||||
|
{
|
||||||
|
u8* start;
|
||||||
|
//TODO: Find sub-substring even if the full match fails
|
||||||
|
u8* substr = memmem(start = haystack->origin, haystack->len,
|
||||||
|
needle->origin, needle->len);
|
||||||
|
if(!substr) return false;
|
||||||
|
debug_assert(substr >= start);
|
||||||
|
*pos = (usize) (substr - start);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmp_find_many(const map_t *pIN needle, usize nhaystacks, usize sizes[pOUT nhaystacks], ...)
|
||||||
|
{
|
||||||
|
va_list v_haystacks;
|
||||||
|
va_start(v_haystacks, sizes);
|
||||||
|
|
||||||
|
const map_t* pINOUT haystack;
|
||||||
|
register int rval=-1;
|
||||||
|
for(usize i=0;i<nhaystacks;i++)
|
||||||
|
{
|
||||||
|
usize *pOUT size = sizes + i;
|
||||||
|
haystack = va_arg(v_haystacks, const map_t* pIN);
|
||||||
|
if(!haystack || !haystack->origin) FATAL("haystack %lu was null or its origin was null", i);
|
||||||
|
if(!cmp_find(needle, haystack, size)) {
|
||||||
|
rval = (int)i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(v_haystacks);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
comp_multires_t* comp_match_all(map_t *pIN needle, usize nh, map_t hs[pIN nh], bool abort_on_fail)
|
||||||
|
{
|
||||||
|
comp_multires_t* result = malloc(sizeof(comp_multires_t) + (sizeof(struct comp_single_result) * nh));
|
||||||
|
debug_assert(result);
|
||||||
|
|
||||||
|
result->all_matched = true;
|
||||||
|
result->idx_first_failure = -1;
|
||||||
|
result->num_results = nh;
|
||||||
|
|
||||||
|
|
||||||
|
usize i;
|
||||||
|
for(i =0;i<nh;i++)
|
||||||
|
{
|
||||||
|
usize start=0;
|
||||||
|
bool matched = cmp_find(needle, hs+i, &start);
|
||||||
|
struct comp_single_result res = {
|
||||||
|
.mapping = hs+i,
|
||||||
|
.matched = matched,
|
||||||
|
.slice_start = start,
|
||||||
|
};
|
||||||
|
TRACE("Result for %lu (%p): Matched: %s, slice start: %lu", i, hs+i, matched ? "yes" : "no", start);
|
||||||
|
result->aggr[i] = res;
|
||||||
|
if(!res.matched) {
|
||||||
|
WARN("Matching haystack %lu failed", i);
|
||||||
|
result->all_matched = false;
|
||||||
|
if(result->idx_first_failure < 0) {
|
||||||
|
INFO("First match failure is %lu", i);
|
||||||
|
result->idx_first_failure = (int)i;
|
||||||
|
}
|
||||||
|
if(abort_on_fail) {
|
||||||
|
result->num_results = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
INFO("Aggregate result for %lu / %lu haystack matches: Full passed: %s, first filaure: %d",
|
||||||
|
i, nh,
|
||||||
|
result->all_matched ? "yes" : "no",
|
||||||
|
result->idx_first_failure);
|
||||||
|
TRACE("Output result aggregate size: %lu (alloc: %p)", sizeof(comp_multires_t) + (sizeof(struct comp_single_result) * nh), result);
|
||||||
|
return result;
|
||||||
|
}
|
@ -0,0 +1,179 @@
|
|||||||
|
//! Displaying match data
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <macros.h>
|
||||||
|
#include <comp.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
#define FLAGSET(f, flag) (( (f) & (flag) ) == (flag))
|
||||||
|
#define DFLAGSET(f, flagname) FLAGSET(f, DISPF_SHOW_ ## flagname)
|
||||||
|
|
||||||
|
#define CMP(x,cmp,y) ( (x) cmp (y) ? (x) : (y) )
|
||||||
|
#define CMPv(x,cmp,y) ({ let _x = (x); let _y = (y); CMP(_x, cmp, _y); })
|
||||||
|
#define MIN(x, y) CMP((x), <, (y))
|
||||||
|
#define MINv(x, y) CMPv((x), <, (y))
|
||||||
|
#define MAX(x, y) CMP((x), >, (y))
|
||||||
|
#define MAXv(x, y) CMPv((x), >, (y))
|
||||||
|
|
||||||
|
// Returns number of bytes written to `dest`. Otherwise same as `strncpy()`.
|
||||||
|
static usize strncpy_n(char*pOUT dest, const char* restrict src, usize n)
|
||||||
|
{
|
||||||
|
usize i;
|
||||||
|
for(i=0;i<n;i++)
|
||||||
|
if(! (*dest++ = *src++)) return i;
|
||||||
|
dest[i] = 0;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will not write past `dn` bytes of `dest`, will not read past `sn` bytes of `src`. Otherwise, same as `strncpy_n()`.
|
||||||
|
static usize s_strncpy_n(usize sn, usize dn; char dest[static pOUT dn], usize dn, const char src[static restrict sn], usize sn)
|
||||||
|
{
|
||||||
|
usize i;
|
||||||
|
usize n = MIN(sn,dn);
|
||||||
|
for(i=0;i<n;i++)
|
||||||
|
if(! (*dest++ = *src++)) return i;
|
||||||
|
if(i<dn) dest[i] = 0;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will not write past `dn` bytes of `dest`, will not read past `sn` bytes of `src`. Otherwise, same as `strncat_n()`.
|
||||||
|
static inline usize s_strncat_n(usize sn, usize dn; char dest[static pOUT dn], usize dn, const char src[static restrict sn], usize sn)
|
||||||
|
{
|
||||||
|
usize dl = MINv(strlen(dest), dn);
|
||||||
|
return s_strncpy_n(dest + dl, dn - dl, src, sn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns number of bytes written to (dest+strlen(dest)). Otherwise same as `strncat()`.
|
||||||
|
static inline usize strncat_n(char*pOUT dest, const char* restrict src, usize n)
|
||||||
|
{
|
||||||
|
usize dl = strlen(dest);
|
||||||
|
return strncpy_n(dest+dl, src, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: This isn't working at all...
|
||||||
|
#ifdef _DEBUG_USE_UNSAFE_DISPLAY_FMT_ARGS
|
||||||
|
_dead
|
||||||
|
#endif
|
||||||
|
static usize _display_get_fmt_n(dispflags_t flags, bool matched, usize _n, char str[pOUT _n])
|
||||||
|
{
|
||||||
|
register usize n = _n, fw=0;
|
||||||
|
const char* fmts[] = {
|
||||||
|
DFLAGSET(flags, FAILURES)
|
||||||
|
? "failure: "
|
||||||
|
: "",
|
||||||
|
|
||||||
|
"match: ",
|
||||||
|
"%lu: ",
|
||||||
|
SLICE_FORMAT,
|
||||||
|
" (len %lu)",
|
||||||
|
(const char*)NULL,
|
||||||
|
};
|
||||||
|
usize w = s_strncpy_n(str, n, fmts[!!matched], n); // SAFETY: Will never read past `fmts[x]` nul-terminator, so setting n as both dest and src length here is safe.
|
||||||
|
//TODO: Fix this... It isn't working...
|
||||||
|
#define ADDSTR(s) do { \
|
||||||
|
debug_assert((s)); \
|
||||||
|
w = MINv(s_strncat_n(str, n, (s), n), n); \
|
||||||
|
/* Remove written from `n` */ \
|
||||||
|
n -= w; \
|
||||||
|
/* Check if we have written into the limit (if so, w will be equal to n above, since w is high-bounded to n by `MINv()` above. So if n is 0, we have hit the limit. */ \
|
||||||
|
ifU(!n) { \
|
||||||
|
/*XXX: Should this be `str[w-1]?` If this assert fails then yes it should. */ \
|
||||||
|
debug_assert(w < _n); \
|
||||||
|
str[w] = 0; \
|
||||||
|
return fw + w; \
|
||||||
|
} \
|
||||||
|
/* Increase `str` pointer by written */ \
|
||||||
|
str += w; \
|
||||||
|
/* Increase return value by written. */ \
|
||||||
|
fw += w; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
if(DFLAGSET(flags, NUMBER)) ADDSTR(fmts[2]);
|
||||||
|
if(DFLAGSET(flags, SLICE)) ADDSTR(fmts[3]);
|
||||||
|
if(DFLAGSET(flags, LENGTH)) ADDSTR(fmts[4]);
|
||||||
|
ADDSTR("\n");
|
||||||
|
TRACE("Output string written: %lu, (rlen %lu)", fw, n);
|
||||||
|
#undef ADDSTR
|
||||||
|
return fw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//XXX: This works, but seems inherantly unsafe. Rewrite to use the above `strn*_n()` functions for better length tracking.
|
||||||
|
#ifndef _DEBUG_USE_UNSAFE_DISPLAY_FMT_ARGS
|
||||||
|
_dead deprecated_message("this is unsafe, use `_display_get_fmt_n`")
|
||||||
|
#endif
|
||||||
|
static usize _display_get_fmt0(dispflags_t flags, bool matched, usize _len, char str[pOUT _len])
|
||||||
|
{
|
||||||
|
register usize len = _len;
|
||||||
|
static const char* fmts[] = { "match: ", "%lu: ", SLICE_FORMAT, " (len %lu)", "failure: ", (const char*)NULL };
|
||||||
|
#define ADDSTR(s) do { debug_assert((s)); strncat(str, (s), len); w = strlen((s)); w = w > len ? len : w; ifU(len<=w) { str[len-1] = 0; return w; } len -= w; str += w; fw+=w; } while(0)
|
||||||
|
usize fw;
|
||||||
|
usize w = strlen(strncpy(str, matched ? fmts[0]
|
||||||
|
: DFLAGSET(flags, FAILURES) ? fmts[4]
|
||||||
|
: "", len)); if(len<=w || w == 0) { str[len-1] = 0; return 0; } len -= w; str += w; fw = w;
|
||||||
|
if(DFLAGSET(flags, NUMBER)) ADDSTR(fmts[1]);
|
||||||
|
if(DFLAGSET(flags, SLICE)) ADDSTR(fmts[2]);
|
||||||
|
if(DFLAGSET(flags, LENGTH)) ADDSTR(fmts[3]);
|
||||||
|
ADDSTR("\n");
|
||||||
|
TRACE("Output string written: %lu, (rlen %lu)", fw, len);
|
||||||
|
return fw;
|
||||||
|
#undef ADDSTR
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG_USE_UNSAFE_DISPLAY_FMT_ARGS
|
||||||
|
#define _display_get_fmt(...) _display_get_fmt0(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define _display_get_fmt(...) _display_get_fmt_n(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void _display_normal(FILE* output, dispin_t*pIN input, dispflags_t flags)
|
||||||
|
{
|
||||||
|
bool matched = input->matched;
|
||||||
|
slice_t cslice = input->cslice;
|
||||||
|
const void* base = input->base;
|
||||||
|
usize i = input->index;
|
||||||
|
|
||||||
|
#ifdef _DEBUG_MANCALC_OFFSETS
|
||||||
|
struct {
|
||||||
|
usize start, end;
|
||||||
|
} pos = {
|
||||||
|
.start = AS(cslice.ptr - base, usize),
|
||||||
|
};
|
||||||
|
pos.end = pos.start + cslice.len;
|
||||||
|
#else
|
||||||
|
let pos = slice_sted((slice_t[]){ SLICE(AS(cslice.ptr - base, usize), cslice.len)});
|
||||||
|
#endif
|
||||||
|
INFO("Mapped region slice for %lu, pre-transform (at %p): " SLICE_FORMAT ", len: %lu", i+1, base, SLICE_FORMAT_ARGS(&cslice), cslice.len);
|
||||||
|
|
||||||
|
// --- get format string from `flags` ---
|
||||||
|
char fmt[200]; //TODO: Find the realistic max for this.
|
||||||
|
usize fmtw = _display_get_fmt(flags, matched, sizeof(fmt)-1, fmt);
|
||||||
|
assert(fmtw+1 < sizeof(fmt));
|
||||||
|
fmt[fmtw+1] = 0;
|
||||||
|
// ---
|
||||||
|
TRACE("Format string get: `%s' (%lu)", fmt, fmtw);
|
||||||
|
|
||||||
|
fprintf(output, fmt, i+1, pos.start, pos.end, pos.end-pos.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_result(FILE* output, dispin_t input, dispkind_t how, dispflags_t flags)
|
||||||
|
{
|
||||||
|
flags = flags ?: DISPLAY_FLAGS_DEFAULT;
|
||||||
|
output = output ?: stdout;
|
||||||
|
|
||||||
|
TRACE("Outputting as %d to %p with flags 0x%08lx", (int)how, output, (usize)flags);
|
||||||
|
switch(how)
|
||||||
|
{
|
||||||
|
case DISPK_NORMAL:
|
||||||
|
_display_normal(output, &input, flags);
|
||||||
|
break;
|
||||||
|
case DISPK_CSV:
|
||||||
|
case DISPK_BINARY:
|
||||||
|
case DISPK_INI:
|
||||||
|
TODO("display kind %d is unimplemented", (int)how);
|
||||||
|
default: COLD_EXPR(FATAL("unknown display kind: %d", (int)how));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <macros.h>
|
||||||
|
#include <map.h>
|
||||||
|
|
||||||
|
// Set all bytes to 0xff (ints i.e. fd will be -1)
|
||||||
|
static inline void _map_init(map_t *pOUT map)
|
||||||
|
{
|
||||||
|
memset(map, 0xff, sizeof(map_t));
|
||||||
|
TRACE("blanked %p->%lu to 0xff", map, sizeof(map_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _map_is_file(const map_t* map)
|
||||||
|
{
|
||||||
|
TRACE("fd element of union is %x (%d)", map->file.fd, map->file.fd);
|
||||||
|
return map->file.fd > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_result_t map_fd(int fd, bool write, usize size, off_t offset, map_t *pOUT map)
|
||||||
|
{
|
||||||
|
TRACE("mapping %lu bytes of fd %d (%s) from offset %lu to %p", size, fd, write ? "rw" : "ro", offset, map);
|
||||||
|
void* ptr = mmap(NULL, size, PROT_READ | (write ? PROT_WRITE : 0), MAP_SHARED, fd, offset);
|
||||||
|
if(ptr == MAP_FAILED) return MAP_ERR_MMAP;
|
||||||
|
TRACE("mapped %p->%lu", ptr, size);
|
||||||
|
map->file = (struct mm_file){
|
||||||
|
.origin = ptr,
|
||||||
|
.len = size,
|
||||||
|
.fd = fd,
|
||||||
|
.fd_offset = offset,
|
||||||
|
};
|
||||||
|
return MAP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_result_t map_file(const char* file, bool write, usize size, off_t offset, map_t *pOUT map)
|
||||||
|
{
|
||||||
|
TRACE("mapping %lu bytes of file `%s' (%s) from %lu to %p", size, file, write ? "rw" : "ro", offset, map);
|
||||||
|
|
||||||
|
int fd = open(file, write ? O_RDWR : O_RDONLY);
|
||||||
|
if(fd < 0) return MAP_ERR_OPEN;
|
||||||
|
TRACE("opened file %d", fd);
|
||||||
|
|
||||||
|
if(!size) {
|
||||||
|
TRACE("user asked for max-size map (size: 0), stating %d to find size...", fd);
|
||||||
|
struct stat st;
|
||||||
|
// Find size of file
|
||||||
|
if(fstat(fd, &st) != 0) return (close(fd), MAP_ERR_STAT);
|
||||||
|
TRACE("fstat() reports size is %lu", st.st_size);
|
||||||
|
size = st.st_size;
|
||||||
|
}
|
||||||
|
TRACE("passing fd %d, size %lu to `map_fd()`", fd, size);
|
||||||
|
return map_fd(fd, write, size, offset, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
map_result_t map_anon(void* ptr, usize len, map_t *pOUT map)
|
||||||
|
{
|
||||||
|
TRACE("mapping anon at %p (%s) of size %lu (map %p)", ptr, ptr ? "fixed" : "dynamic", len, map);
|
||||||
|
ptr = mmap(ptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | (ptr ? MAP_FIXED : 0), -1, 0);
|
||||||
|
if(ptr==MAP_FAILED) return MAP_ERR_MMAP;
|
||||||
|
|
||||||
|
TRACE("mapped ptr %p->%lu", ptr, len);
|
||||||
|
// Make sure .file.fd is -1, so we can tell this is anon in `map_free()`.
|
||||||
|
_map_init(map);
|
||||||
|
map->anon = (struct mm_anon) {
|
||||||
|
.origin = ptr,
|
||||||
|
.len = len,
|
||||||
|
};
|
||||||
|
return MAP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_result_t map_free(map_t map)
|
||||||
|
{
|
||||||
|
TRACE("unmapping %p->%lu", map.origin, map.len);
|
||||||
|
if( munmap(map.anon.origin, map.anon.len) != 0) return MAP_ERR_UNMAP;
|
||||||
|
if( _map_is_file(&map) ) {
|
||||||
|
TRACE("map is file, closing fd %d", map.file.fd);
|
||||||
|
if( close(map.file.fd) != 0 ) return MAP_ERR_CLOSE;
|
||||||
|
}
|
||||||
|
return MAP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_result_t map_preload(map_t *pINOUT map, bool random)
|
||||||
|
{
|
||||||
|
TRACE("preloading map %p (%p->%lu) for immediate %s access", map, map->origin, map->len, random ? "random" : "sequential");
|
||||||
|
return madvise(map->origin, map->len, MADV_WILLNEED | (random ? MADV_RANDOM : MADV_SEQUENTIAL)) != 0
|
||||||
|
? MAP_ERR_ADVISE
|
||||||
|
: MAP_SUCCESS;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* map_error_str(map_result_t res)
|
||||||
|
{
|
||||||
|
switch(res)
|
||||||
|
{
|
||||||
|
#define CASE(err, str) case err: return str
|
||||||
|
CASE(MAP_ERR_MMAP, "mmap() failed (creating a map)");
|
||||||
|
CASE(MAP_ERR_UNMAP, "munmap() failed (freeing a map)");
|
||||||
|
CASE(MAP_ERR_OPEN, "open() failed (mapping a filepath)");
|
||||||
|
CASE(MAP_ERR_CLOSE, "close() failed (freeing a file-map)");
|
||||||
|
CASE(MAP_ERR_STAT, "fstat() failed: (mapping a full filepath)");
|
||||||
|
CASE(MAP_ERR_ADVISE, "madvise() failed: (preloading mapped file)");
|
||||||
|
|
||||||
|
CASE(MAP_SUCCESS, "success");
|
||||||
|
#undef CASE
|
||||||
|
default:
|
||||||
|
ERROR("unkown or invalid error code %d", (int)res);
|
||||||
|
return "unknown error";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <slice.h>
|
||||||
|
|
||||||
|
usize slice_strn(const slice_t*pIN slice, usize len, char str[static pOUT len])
|
||||||
|
{
|
||||||
|
return snprintf(str, len, SLICE_FORMAT, SLICE_FORMAT_ARGS(slice));
|
||||||
|
}
|
||||||
|
|
||||||
|
usize slice_str_sz(const slice_t*pIN slice)
|
||||||
|
{
|
||||||
|
return snprintf(NULL, 0, SLICE_FORMAT, SLICE_FORMAT_ARGS(slice));
|
||||||
|
}
|
||||||
|
|
||||||
|
usize _slice_str_max_sz()
|
||||||
|
{
|
||||||
|
const slice_t max = SLICE_MAX;
|
||||||
|
return snprintf(NULL, 0, SLICE_FORMAT, SLICE_FORMAT_ARGS(&max));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* slice_strs(const slice_t*pIN slice)
|
||||||
|
{
|
||||||
|
static _Thread_local char buf[SLICE_STR_MAX_SIZE+1];
|
||||||
|
debug_assert(sizeof(buf) == SLICE_STR_MAX_SIZE+1);
|
||||||
|
buf[snprintf(buf, sizeof(buf), SLICE_FORMAT, SLICE_FORMAT_ARGS(slice))] = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* slice_str(const slice_t*pIN slice)
|
||||||
|
{
|
||||||
|
usize ln = slice_str_sz(slice)+1;
|
||||||
|
char *buf = malloc(ln);
|
||||||
|
usize _out = slice_strn(slice, ln, buf);
|
||||||
|
debug_assert(_out == ln);
|
||||||
|
IGNORE(_out);
|
||||||
|
return buf;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <tests.h>
|
||||||
|
#include <map.h>
|
||||||
|
|
||||||
|
DEFTEST(map_anon)
|
||||||
|
{
|
||||||
|
map_t map;
|
||||||
|
TEST_ASSERT(map_anon(NULL, 1024, &map) == MAP_SUCCESS);
|
||||||
|
|
||||||
|
memset(map.origin, 0xab, map.len);
|
||||||
|
TEST_ASSERT( ((u8*)map.origin)[10] == 0xab );
|
||||||
|
|
||||||
|
TEST_ASSERT(map_free(map) == MAP_SUCCESS);
|
||||||
|
|
||||||
|
return TEST_OK;
|
||||||
|
}
|
||||||
|
RUNTEST_DEBUG(map_anon)
|
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
#include <macros.h>
|
||||||
|
#include <tests.h>
|
||||||
|
|
||||||
|
#include <slice.h>
|
||||||
|
|
||||||
|
// Internal `slice.c` function prototyped.
|
||||||
|
usize _slice_str_max_sz();
|
||||||
|
|
||||||
|
DEFTEST(slice_static_str_len)
|
||||||
|
{
|
||||||
|
const usize func = _slice_str_max_sz();
|
||||||
|
INFO("Function reports: %lu", func);
|
||||||
|
INFO("Constant is: %lu", SLICE_STR_MAX_SIZE);
|
||||||
|
TEST_ASSERT( (func == SLICE_STR_MAX_SIZE) );
|
||||||
|
|
||||||
|
return TEST_OK;
|
||||||
|
}
|
||||||
|
RUNTEST_DEBUG(slice_static_str_len)
|
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <ints.h>
|
||||||
|
#include <macros.h>
|
||||||
|
#include <tests.h>
|
||||||
|
|
||||||
|
DEFTEST(transmute_copy)
|
||||||
|
{
|
||||||
|
static const u64 EXPECTED = 18446743474098365025lu;
|
||||||
|
struct { char i[4]; i32 u; } input = { .i = {'a','b','c','d'}, .u = -140 };
|
||||||
|
let output = TRANSMUTE(input, u64);
|
||||||
|
TRACE("Transmute test, expected 0x%lx, got 0x%lx", EXPECTED, output);
|
||||||
|
//XXX: TODO: This test will fail on Big Endian machines.
|
||||||
|
TEST_ASSERT(output == EXPECTED);
|
||||||
|
TRACE("Alignment of output: %lu, alignment of input: %lu", _Alignof(output), _Alignof(input));
|
||||||
|
let input2 = TRANSMUTE(output, var(input));
|
||||||
|
TRACE("Transmute output passed, trying to transmute back");
|
||||||
|
TEST_ASSERT( memcmp(&input2, &input, sizeof(input)) == 0 );
|
||||||
|
|
||||||
|
return TEST_OK;
|
||||||
|
}
|
||||||
|
RUNTEST_DEBUG(transmute_copy);
|
@ -0,0 +1,119 @@
|
|||||||
|
// Trace messages
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <trace.h>
|
||||||
|
|
||||||
|
#include <macros.h>
|
||||||
|
#include <ints.h>
|
||||||
|
|
||||||
|
static _Atomic enum trace_level _t_level = _TRACE_LEVEL_DEFAULT;
|
||||||
|
|
||||||
|
const char* const _TRACE_CONTROL_ENV_NAME = "LOG_LEVEL";
|
||||||
|
|
||||||
|
const struct {const char* const name; const enum trace_level level; } _trace_level_names[] = {
|
||||||
|
#define X(name) { #name, TRACE_LEVEL_ ## name }
|
||||||
|
#define Y(name, val) { #name, TRACE_LEVEL_ ## val }
|
||||||
|
X(DEBUG), Y(TRACE, DEBUG),
|
||||||
|
X(INFO),
|
||||||
|
X(WARN),
|
||||||
|
X(ERROR),
|
||||||
|
X(FATAL),
|
||||||
|
#undef Y
|
||||||
|
#undef X
|
||||||
|
};
|
||||||
|
#define _trace_level_names_n (sizeof(_trace_level_names) / sizeof(_trace_level_names[0]))
|
||||||
|
|
||||||
|
const char* const _trace_level_descriptions[_TRACE_LEVEL_NUM] = {
|
||||||
|
"All trace messages regarding internals, only useful for debugging",
|
||||||
|
"Information messages regarding processes, recommended level",
|
||||||
|
"Only show warnings",
|
||||||
|
"Only show errors",
|
||||||
|
"Only show fatal (aborting) errors",
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool _trace_lookup(const char*pIN name, enum trace_level *pOUT olevel)
|
||||||
|
{
|
||||||
|
debug_assert(name);
|
||||||
|
|
||||||
|
for(usize i=0;i<_trace_level_names_n;i++) {
|
||||||
|
let level = &_trace_level_names[i];
|
||||||
|
debug_assert(level && level->name);
|
||||||
|
if(strcmp(name, level->name) == 0) {
|
||||||
|
*olevel =level->level;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _trace_reverse_lookup(enum trace_level level, const char* *pOUT name)
|
||||||
|
{
|
||||||
|
for(usize i=0;i<_trace_level_names_n;i++) {
|
||||||
|
let lv = &_trace_level_names[i];
|
||||||
|
debug_assert(lv && lv->name);
|
||||||
|
if(lv->level == level) {
|
||||||
|
*name = lv->name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* trace_name_of(enum trace_level lv)
|
||||||
|
{
|
||||||
|
const char* ret;
|
||||||
|
if(!_trace_reverse_lookup(lv, &ret)) return NULL;
|
||||||
|
else return ret;
|
||||||
|
}
|
||||||
|
inline enum trace_level trace_max_level()
|
||||||
|
{
|
||||||
|
return _t_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool _trace_hidden(enum trace_level l)
|
||||||
|
{
|
||||||
|
return l < trace_max_level();
|
||||||
|
}
|
||||||
|
|
||||||
|
int _t_fprintf(enum trace_level l, FILE* output, const char* msg, ...)
|
||||||
|
{
|
||||||
|
if(_trace_hidden(l)) return 0;
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, msg);
|
||||||
|
register int r = vfprintf(output, msg, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void trace_init_with(enum trace_level max_level)
|
||||||
|
{
|
||||||
|
_t_level = max_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _try_get_env(const char* name, const char* *pOUT value)
|
||||||
|
{
|
||||||
|
return !!(*value = (const char*)getenv(name));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int trace_init()
|
||||||
|
{
|
||||||
|
const char* eval;
|
||||||
|
if(!_try_get_env(_TRACE_CONTROL_ENV_NAME, &eval)) return 1;
|
||||||
|
else {
|
||||||
|
assert(eval);
|
||||||
|
if(*eval == 0) return 1; // Count empty string as default.
|
||||||
|
|
||||||
|
enum trace_level level;
|
||||||
|
if(!_trace_lookup(eval, &level)) return 0;
|
||||||
|
else {
|
||||||
|
trace_init_with(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
Loading…
Reference in new issue