fix: hectic C: impruve logging guidelines

This commit is contained in:
2025-04-07 05:26:30 +00:00
parent 11150151f9
commit 98a26f1f61
9 changed files with 1205 additions and 122 deletions

153
package/c/hectic/docs/logging.md Executable file
View File

@@ -0,0 +1,153 @@
# Hectic Library Logging System
This document provides guidelines on how to use the logging system in the Hectic library effectively.
## Log Levels
The Hectic library implements a graduated severity-based logging system with the following levels (from lowest to highest severity):
### TRACE
**Purpose**: Most detailed information for in-depth debugging
- Use for: Deep diagnostic details, function entry/exit, variable dumps
- Visibility: Development environments only
- Performance Impact: High
- Example: `raise_trace("Entering function with value=%d, ptr=%p", value, ptr);`
### DEBUG
**Purpose**: Detailed information useful during development
- Use for: Development-time debugging, showing variable states, internal flows
- Visibility: Development and debugging environments
- Performance Impact: Medium-High
- Example: `raise_debug("Buffer allocated with size %zu bytes at %p", size, buffer);`
### LOG
**Purpose**: General operational events
- Use for: Runtime events worth logging but not requiring attention
- Visibility: Always written to logs, useful for auditing/diagnostics
- Performance Impact: Medium
- Example: `raise_log("Processing file %s, size: %zu bytes", filename, file_size);`
### INFO
**Purpose**: Informational messages highlighting progress
- Use for: Normal but noteworthy events, state changes, startup/shutdown events
- Visibility: Visible to client applications if configured
- Performance Impact: Low-Medium
- Example: `raise_info("Connection established to %s, session ID: %s", host, session_id);`
### NOTICE
**Purpose**: More important events than INFO, but not warnings
- Use for: Important state changes, significant operations, configuration changes
- Visibility: Displayed to client by default
- Performance Impact: Low
- Example: `raise_notice("Switching to backup server due to high load");`
### WARN
**Purpose**: Potential problems that don't prevent normal operation
- Use for: Unexpected behaviors, deprecated feature usage, recoverable errors
- Visibility: Alerts both client and server logs
- Performance Impact: Low
- Example: `raise_warn("API call retry limit (%d) reached for endpoint %s", retries, endpoint);`
### EXCEPTION
**Purpose**: Serious errors requiring immediate attention
- Use for: Critical failures, data loss risks, business rule violations
- Visibility: Highest priority, often leads to operation termination
- Performance Impact: Low
- Example: `raise_exception("Failed to open database: %s", error_msg);`
## Setting the Log Level
You can control the verbosity of logs in three ways:
1. **Environment Variable**: Set `LOG_LEVEL` environment variable
```sh
export LOG_LEVEL=DEBUG
```
2. **Programmatically**: Use the `logger_level()` function
```c
logger_level(LOG_LEVEL_DEBUG);
```
3. **Compile-Time**: Define `PRECOMPILED_LOG_LEVEL` before including hectic.h
```c
#define PRECOMPILED_LOG_LEVEL LOG_LEVEL_INFO
#include <hectic.h>
```
## Logging Best Practices
### DO
- **Be specific and concise**: Include relevant details but avoid verbose descriptions
- **Include context**: Add identifiers (IDs, filenames, pointers) to help with troubleshooting
- **Use the appropriate level**: Understand the purpose of each level and use it accordingly
- **Log state transitions**: Important changes in application state should be logged
- **Use structured data**: When possible, include structured information rather than unformatted text
### DON'T
- **Log sensitive information**: Never log passwords, tokens, or personal information
- **Overuse high-severity levels**: Reserve WARN and EXCEPTION for real issues
- **Log in tight loops**: Avoid excessive logging in performance-critical paths
- **Use inconsistent formats**: Follow a consistent message format throughout your code
- **Ignore log levels**: Don't use DEBUG for important operational events or EXCEPTION for minor issues
## Examples of Good Logging
### Error Handling Pattern
```c
void *allocate_resource(size_t size) {
raise_debug("Allocating %zu bytes", size);
void *ptr = malloc(size);
if (!ptr) {
raise_exception("Memory allocation failed for %zu bytes", size);
return NULL;
}
raise_debug("Successfully allocated %zu bytes at %p", size, ptr);
return ptr;
}
```
### State Transition Pattern
```c
void change_connection_state(Connection *conn, ConnState new_state) {
raise_debug("Connection %p state change requested: %s -> %s",
conn, conn_state_to_string(conn->state), conn_state_to_string(new_state));
if (!is_valid_transition(conn->state, new_state)) {
raise_warn("Invalid state transition from %s to %s",
conn_state_to_string(conn->state), conn_state_to_string(new_state));
return;
}
ConnState old_state = conn->state;
conn->state = new_state;
raise_info("Connection %s state changed: %s -> %s",
conn->id, conn_state_to_string(old_state), conn_state_to_string(new_state));
}
```
### Operation Pattern
```c
int process_file(const char *filename) {
raise_log("Processing file: %s", filename);
FILE *f = fopen(filename, "r");
if (!f) {
raise_warn("Could not open file %s: %s", filename, strerror(errno));
return -1;
}
// Process file...
raise_info("Successfully processed file %s: %d records", filename, record_count);
return 0;
}
```

View File

@@ -0,0 +1,205 @@
# Logging Audit Guide for Hectic
This guide provides a systematic approach to auditing and improving logging in existing functions of the Hectic library.
## Checklist for Function Audit
### 1. Basic Logging Check
- [ ] Function has entry logging (DEBUG or TRACE level)
- [ ] Function has result/exit logging
- [ ] Failures and errors are logged with appropriate levels (WARN or EXCEPTION)
- [ ] Intermediate steps are logged at TRACE or DEBUG level
- [ ] Successful operation completions are logged at LOG level
### 2. Level Consistency Check
- [ ] `LOG_LEVEL_TRACE` is used for detailed execution tracking
- [ ] `LOG_LEVEL_DEBUG` is used for significant internal steps
- [ ] `LOG_LEVEL_LOG` is used for important operational events and successful operation completions
- [ ] `LOG_LEVEL_INFO` is used *rarely*, only for user-critical events
- [ ] `LOG_LEVEL_NOTICE` is used *very rarely*, almost not needed for a low-level library
- [ ] `LOG_LEVEL_WARN` is used for recoverable problems
- [ ] `LOG_LEVEL_EXCEPTION` is used only for critical errors
### 3. Message Formatting Check
- [ ] Messages start with a domain prefix (PARSE:, ALLOC:, FORMAT:, etc.)
- [ ] Message structure follows the pattern "Action: object (details)"
- [ ] Messages contain sufficient context for understanding (pointers, sizes, values)
- [ ] Messages don't contain redundant information
- [ ] Pointers use %p format
- [ ] Sizes use %zu format
- [ ] Strings use length limitation when necessary (%.20s)
- [ ] NULL pointer checks are added before using pointers in logs
## Logging Level Usage Rules
### TRACE
- Function calls and exits
- Detailed loop iteration information
- Variable values during execution
- Any detailed debugging information
### DEBUG
- Entries to public API functions
- Resource allocation and deallocation
- Key algorithm steps
- Data processing details
### LOG
- Successful component initialization
- Completion of significant operations
- State changes important for operation
- Key business logic points
### INFO (use rarely!)
- Library startup and shutdown
- Version and configuration information
- Extremely important events visible to users
- Large operations requested by users
### NOTICE (almost never use)
- Events that users should pay attention to
- Significant planned actions
### WARN
- Unexpected but handled errors
- Edge cases
- Warnings about potential problems
- Use of deprecated APIs
### EXCEPTION
- Serious errors affecting system operation
- Data integrity violations
- Resource exhaustion
- Critical security failures
## Audit and Update Process
### Step 1: Function Analysis
1. Determine the function type:
- Initialization/resource management function
- Data processing function
- Utility function
- Other
2. Identify key logging points:
- Function entry
- Parameter checks
- Main processing steps
- Error conditions
- Function exit
### Step 2: Update Planning
1. Create a list of necessary logs at each level
2. Determine the right prefixes for each message type
3. Prepare detailed messages with necessary context
4. Ensure parameter checks are logged before use
### Step 3: Implementing Changes
1. Add/update function entry logging
2. Add/update input parameter checks
3. Update intermediate step logging
4. Add/update error condition logging
5. Add/update result logging
### Step 4: Testing
1. Check message output correctness
2. Verify proper level usage
3. Check that all necessary information is included in logs
4. Compare with other already updated functions for consistency
## Examples for Typical Functions
### Memory Allocation Function:
```c
void* memory_function__(const char *file, const char *func, int line, size_t size) {
// Function entry
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"ALLOC: Requesting memory allocation (size: %zu bytes)", size);
// Parameter check
if (size == 0) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"ALLOC: Zero-sized memory allocation requested");
return NULL;
}
// Memory allocation
void *ptr = malloc(size);
if (!ptr) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"ALLOC: Memory allocation failed (requested: %zu bytes)", size);
return NULL;
}
// Result
raise_message(LOG_LEVEL_LOG, file, func, line,
"ALLOC: Memory allocated successfully (address: %p, size: %zu bytes)",
ptr, size);
return ptr;
}
```
### Data Conversion Function:
```c
char* convert_function__(const char *file, const char *func, int line,
const void *input, size_t input_size) {
// Function entry
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"CONVERT: Starting data conversion (input: %p, size: %zu)",
input, input_size);
// Parameter check
if (!input) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"CONVERT: NULL input provided");
return NULL;
}
if (input_size == 0) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"CONVERT: Zero input size provided");
return NULL;
}
// Processing start
raise_message(LOG_LEVEL_TRACE, file, func, line,
"CONVERT: Processing input data...");
// Processing...
// Result
char *result = NULL; // conversion result
if (!result) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"CONVERT: Conversion failed");
return NULL;
}
raise_message(LOG_LEVEL_LOG, file, func, line,
"CONVERT: Data conversion completed successfully (result: %p)", result);
return result;
}
```
## Update Priorities
Recommended order for updating functions:
1. Memory management functions (`arena_*`)
2. Parsing functions (`json_parse_*`)
3. Formatting functions (`json_to_string_*`)
4. Data processing functions (`slice_*`)
5. Utility functions
## Conclusion
Consistent application of these recommendations to functions in the library will significantly improve logging quality and facilitate debugging and code maintenance in the future.

View File

@@ -0,0 +1,285 @@
# Logging System Improvement Plan for Hectic Library
## Current Issues
After analyzing the logging in the hectic library, the following issues have been identified:
1. **Inconsistent message formatting** - messages are formatted differently in various parts of the code
2. **Inefficient use of logging levels** - levels don't always correspond to the nature of the message
3. **Incomplete logging** - some important operations aren't logged at all
4. **Lack of context** - messages don't always contain necessary context for debugging
5. **Mixed formats** - no unified style for logging different types of data
## Standardizing Logging by Function Types
### 1. Initialization and Resource Management Functions
**Logging pattern:**
1. `LOG_LEVEL_DEBUG` when entering a function with parameters
2. `LOG_LEVEL_DEBUG` when performing significant steps within the function
3. `LOG_LEVEL_LOG` on successful resource allocation/initialization
4. `LOG_LEVEL_WARN` for warning conditions
5. `LOG_LEVEL_EXCEPTION` for critical errors
**Example (arena_init):**
```c
Arena arena_init__(const char *file, const char *func, int line, size_t size) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Initializing arena with size %zu bytes", size);
Arena arena;
arena.begin = malloc(size);
if (!arena.begin) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"Failed to allocate memory for arena (requested %zu bytes)", size);
exit(1);
}
memset(arena.begin, 0, size);
arena.current = arena.begin;
arena.capacity = size;
raise_message(LOG_LEVEL_LOG, file, func, line,
"Arena initialized: address=%p, capacity=%zu bytes", arena.begin, size);
return arena;
}
```
### 2. Data Processing Functions (parsers, serializers)
**Logging pattern:**
1. `LOG_LEVEL_DEBUG` when entering a function with main parameters
2. `LOG_LEVEL_TRACE` for detailed tracking of parsing/serialization steps
3. `LOG_LEVEL_DEBUG` when discovering intermediate results
4. `LOG_LEVEL_WARN` for recoverable format issues
5. `LOG_LEVEL_EXCEPTION` for unrecoverable format errors
6. `LOG_LEVEL_LOG` on successful completion of a significant operation
**Example (for json_parse):**
```c
Json *json_parse__(const char* file, const char* func, int line, Arena *arena, const char **s) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Starting JSON parsing from position %p", *s);
// Start parsing
if (!s || !*s) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"Invalid input: NULL pointer provided for JSON parsing");
return NULL;
}
// Show first 20 characters for debugging
raise_message(LOG_LEVEL_TRACE, file, func, line,
"JSON input preview: '%.20s%s'", *s, strlen(*s) > 20 ? "..." : "");
Json *result = json_parse_value__(file, func, line, s, arena);
if (!result) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"JSON parsing failed at position: %p", *s);
} else {
raise_message(LOG_LEVEL_LOG, file, func, line,
"JSON parsed successfully, type: %d", result->type);
}
return result;
}
```
### 3. Utility Functions
**Logging pattern:**
1. `LOG_LEVEL_TRACE` when entering a function with full parameters
2. `LOG_LEVEL_DEBUG` for logging important intermediate steps
3. `LOG_LEVEL_LOG` for successful operation completion
4. `LOG_LEVEL_WARN` for unusual but handled situations
5. `LOG_LEVEL_TRACE` when exiting with a result
**Example (substr_clone):**
```c
void substr_clone__(const char *file, const char *func, int line,
const char * const src, char *dest, size_t from, size_t len) {
raise_message(LOG_LEVEL_TRACE, file, func, line,
"Entering substring clone: src=%p (\"%s\"), dest=%p, from=%zu, len=%zu",
src, src ? (strlen(src) < 20 ? src : "<long string>") : "<null>",
dest, from, len);
if (!src || !dest) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"Invalid NULL pointer: %s%s",
(!src ? "src " : ""), (!dest ? "dest" : ""));
if (dest) dest[0] = '\0';
return;
}
size_t srclen = strlen(src);
if (from >= srclen) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"Out of range: 'from' index (%zu) exceeds source length (%zu)",
from, srclen);
dest[0] = '\0';
return;
}
if (from + len > srclen) {
size_t old_len = len;
len = srclen - from;
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Adjusted length from %zu to %zu to fit source bounds",
old_len, len);
}
strncpy(dest, src + from, len);
dest[len] = '\0';
raise_message(LOG_LEVEL_TRACE, file, func, line,
"Substring cloned: result=\"%s\", copied_length=%zu",
dest, len);
}
```
## Log Naming Standards
### 1. Function Prefixes
To make logs easier to search, use prefixes in messages:
- **INIT:** - for initialization logs
- **ALLOC:** - for memory allocation logs
- **PARSE:** - for parsing logs
- **PROCESS:** - for data processing logs
- **FREE:** - for resource freeing logs
### 2. Message Structure
All messages should have a consistent structure:
- **Action**: What is being done (verb in present continuous)
- **Object**: What is being acted upon (noun phrase)
- **Details**: Additional information (in parentheses or after a colon)
**Examples:**
- "Initializing arena (size: %zu bytes)"
- "Processing JSON object with %d members"
- "Allocating memory block: address=%p, size=%zu"
## Logging Levels
Recommendations for using logging levels in a low-level library:
1. **TRACE (Very detailed)** - For detailed tracking of function operation:
- Function entry/exit
- Data contents
- Intermediate variable values
- Processing loop details
2. **DEBUG (Detailed)** - For programmers working with the library:
- Main algorithm steps
- Resource allocation/freeing
- Intermediate object states
- Debugging information
3. **LOG (Operational)** - For important library operations:
- Successful resource initialization
- Successful operation completion
- System state changes
- Main execution points of working algorithms
4. **INFO (Informational)** - For informing the application user:
- *This level should be used rarely in the library*
- Only truly important events for the user
- Version and configuration information
- High-level public API calls
5. **NOTICE (Notable)** - Important state changes:
- *Almost never used in a low-level library*
- Events requiring user attention
- Important business events (if applicable)
6. **WARN (Warning)** - Potentially problematic situations:
- Recoverable errors
- Edge cases in data
- Requests with potentially bad results
- Deprecated APIs
7. **EXCEPTION (Exceptional situation)** - Critical errors:
- Unrecoverable errors
- Data integrity violations
- Resource exhaustion
- Errors requiring termination
## Action Plan for Logging Improvement
1. **Automation**: Create a script to find inconsistencies in logging
2. **Prioritization**: First update the most critical components (memory management, parsers)
3. **Documentation**: Expand documentation with examples for developers
4. **Testing**: Add tests that check logging under various scenarios
5. **Review**: Conduct code reviews of all logging changes
## Examples for Different Modules
### Memory Management (arena)
```c
// Initialization
raise_message(LOG_LEVEL_DEBUG, file, func, line, "INIT: Creating new arena (size: %zu bytes)", size);
// Allocation
raise_message(LOG_LEVEL_DEBUG, file, func, line, "ALLOC: Requesting memory from arena (size: %zu bytes, available: %zu bytes)", size, available);
// Error
raise_message(LOG_LEVEL_EXCEPTION, file, func, line, "ERROR: Arena memory exhausted (requested: %zu bytes, available: %zu bytes)", size, available);
// Successful operation completion
raise_message(LOG_LEVEL_LOG, file, func, line, "ALLOC: Memory allocated successfully (address: %p, size: %zu bytes)", ptr, size);
// Freeing
raise_message(LOG_LEVEL_DEBUG, file, func, line, "FREE: Releasing arena resources (total size: %zu bytes, used: %zu bytes)", arena->capacity, used);
```
### JSON Parser
```c
// Start parsing
raise_message(LOG_LEVEL_DEBUG, file, func, line, "PARSE: Starting JSON parsing (input: %.20s%s)", *s, strlen(*s) > 20 ? "..." : "");
// Intermediate result
raise_message(LOG_LEVEL_TRACE, file, func, line, "PARSE: Found JSON %s at position %p", type_str, position);
// Parsing error
raise_message(LOG_LEVEL_WARN, file, func, line, "PARSE: Invalid JSON syntax at position %p (context: '%.10s')", *s, *s);
// Completion
raise_message(LOG_LEVEL_LOG, file, func, line, "PARSE: JSON parsing completed successfully (type: %s, size: %zu bytes)", type_str, size);
```
### Slice Operations
```c
// Creating a slice
raise_message(LOG_LEVEL_TRACE, file, func, line, "SLICE: Creating slice from array (source size: %zu, slice: %zu elements from index %zu)", array_len, len, start);
// Subslice
raise_message(LOG_LEVEL_TRACE, file, func, line, "SLICE: Extracting sub-slice (from: %zu, length: %zu)", start, len);
// Error
raise_message(LOG_LEVEL_WARN, file, func, line, "SLICE: Out of bounds slice request (start: %zu, length: %zu, available: %zu)", start, len, available);
// Successful creation
raise_message(LOG_LEVEL_LOG, file, func, line, "SLICE: Successfully created slice (length: %zu, element size: %zu)", slice.len, slice.isize);
```
## Conclusion
Implementing these standards will:
1. Simplify library debugging
2. Speed up code understanding for new developers
3. Make it easier to find problems in production
4. Make the library more professional and maintainable

View File

@@ -1,6 +1,25 @@
#include "hectic.h"
// Global color mode variable definition
ColorMode color_mode = COLOR_MODE_AUTO;
const char* color_mode_to_string(ColorMode mode) {
switch (mode) {
case COLOR_MODE_AUTO: return "AUTO";
case COLOR_MODE_FORCE: return "FORCE";
case COLOR_MODE_DISABLE: return "DISABLE";
default: return "UNKNOWN";
}
}
void set_output_color_mode(ColorMode mode) {
// Log the color mode change
const char* mode_name = color_mode_to_string(mode);
// Using fprintf since this might be called before logging is initialized
raise_message(LOG_LEVEL_INFO, __FILE__, __func__, __LINE__, "CONFIG: Setting output color mode to %s", mode_name);
// Set the mode
color_mode = mode;
}
@@ -11,7 +30,6 @@ void set_output_color_mode(ColorMode mode) {
const char* log_level_to_string(LogLevel level) {
switch (level) {
case LOG_LEVEL_TRACE: return "TRACE";
case LOG_LEVEL_ZALUPA: return "ZALUPA";
case LOG_LEVEL_DEBUG: return "DEBUG";
case LOG_LEVEL_LOG: return "LOG";
case LOG_LEVEL_INFO: return "INFO";
@@ -25,7 +43,6 @@ const char* log_level_to_string(LogLevel level) {
const char* log_level_to_color(LogLevel level) {
switch (level) {
case LOG_LEVEL_TRACE: return OPTIONAL_COLOR(COLOR_GREEN);
case LOG_LEVEL_ZALUPA: return OPTIONAL_COLOR(COLOR_MAGENTA);
case LOG_LEVEL_DEBUG: return OPTIONAL_COLOR(COLOR_BLUE);
case LOG_LEVEL_LOG: return OPTIONAL_COLOR(COLOR_CYAN);
case LOG_LEVEL_INFO: return OPTIONAL_COLOR(COLOR_GREEN);
@@ -41,8 +58,6 @@ LogLevel log_level_from_string(const char *level_str) {
if (!level_str) return LOG_LEVEL_INFO;
if (strcmp(level_str, "TRACE") == 0)
return LOG_LEVEL_TRACE;
else if (strcmp(level_str, "ZALUPA") == 0)
return LOG_LEVEL_ZALUPA;
else if (strcmp(level_str, "DEBUG") == 0)
return LOG_LEVEL_DEBUG;
else if (strcmp(level_str, "LOG") == 0)
@@ -70,7 +85,18 @@ void logger_level(LogLevel level) {
}
void init_logger(void) {
current_log_level = log_level_from_string(getenv("LOG_LEVEL"));
// Read log level from environment
const char* env_level = getenv("LOG_LEVEL");
current_log_level = log_level_from_string(env_level);
// Log initialization with appropriate message
if (env_level) {
fprintf(stderr, "INIT: Logger initialized with level %s from environment\n",
log_level_to_string(current_log_level));
} else {
fprintf(stderr, "INIT: Logger initialized with default level %s\n",
log_level_to_string(current_log_level));
}
}
char* raise_message(
@@ -80,7 +106,6 @@ char* raise_message(
int line,
const char *format,
...) {
(void)func;
if (level < current_log_level) {
return NULL;
}
@@ -91,8 +116,17 @@ char* raise_message(
static char timeStr[20];
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &tm_info);
fprintf(stderr, "%s %s%s%s %s:%d ", timeStr, log_level_to_color(level), log_level_to_string(level), OPTIONAL_COLOR(COLOR_RESET), file, line);
// Print timestamp, log level with color, location info
fprintf(stderr, "%s %s%s%s [%s:%s:%d] ",
timeStr,
log_level_to_color(level),
log_level_to_string(level),
OPTIONAL_COLOR(COLOR_RESET),
file,
func,
line);
// Print the actual message with variable arguments
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
@@ -108,85 +142,221 @@ char* raise_message(
// -----------
Arena arena_init__(const char *file, const char *func, int line, size_t size) {
// Function entry logging
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"INIT: Creating arena (size: %zu bytes)", size);
Arena arena;
arena.begin = malloc(size);
// Check for allocation failure
if (!arena.begin) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"INIT: Failed to allocate memory for arena (requested: %zu bytes)", size);
exit(1);
}
memset(arena.begin, 0, size);
arena.current = arena.begin;
arena.capacity = size;
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Initialized arena at %p with capacity %zu", arena.begin, size);
// Success logging at LOG level
raise_message(LOG_LEVEL_LOG, file, func, line,
"INIT: Arena initialized successfully (address: %p, capacity: %zu bytes)", arena.begin, size);
return arena;
}
void* arena_alloc_or_null__(const char *file, const char *func, int line, Arena *arena, size_t size) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "arena_alloc_or_null(%p, %zu)", arena, size);
// Function entry at TRACE level
raise_message(LOG_LEVEL_TRACE, file, func, line,
"ALLOC: Requesting memory from arena (arena: %p, size: %zu bytes)", arena, size);
void *mem = NULL;
if (arena->begin == 0) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"ALLOC: Arena not initialized, creating new arena");
*arena = arena_init__(file, func, line, 1024); // ARENA_DEFAULT_SIZE assumed as 1024
}
size_t current = (size_t)arena->current - (size_t)arena->begin;
if (arena->capacity <= current || arena->capacity - current < size) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Arena %p (capacity %zu) used %zu cannot allocate %zu bytes",
raise_message(LOG_LEVEL_WARN, file, func, line,
"ALLOC: Insufficient memory in arena (address: %p, capacity: %zu bytes, used: %zu bytes, requested: %zu bytes)",
arena->begin, arena->capacity, current, size);
return NULL;
} else {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Arena %p (capacity %zu) used %zu will allocate %zu bytes",
"ALLOC: Allocating from arena (address: %p, capacity: %zu bytes, used: %zu bytes, requested: %zu bytes)",
arena->begin, arena->capacity, current, size);
mem = arena->current;
arena->current = (char*)arena->current + size;
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Allocated at %p", mem);
// Success logging
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"ALLOC: Memory allocated successfully (address: %p, size: %zu bytes)", mem, size);
return mem;
}
void* arena_alloc__(const char *file, const char *func, int line, Arena *arena, size_t size) {
// Function entry logging
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"ALLOC: Allocating memory (arena: %p, size: %zu bytes)", arena, size);
void *mem = arena_alloc_or_null__(file, func, line, arena, size);
if (!mem) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Arena out of memory when trying to allocate %zu bytes", size);
"ALLOC: Allocation failed (arena: %p, requested: %zu bytes)", arena, size);
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"Arena out of memory");
"ALLOC: Arena out of memory (requested: %zu bytes)", size);
exit(1);
}
// Success logging
raise_message(LOG_LEVEL_LOG, file, func, line,
"ALLOC: Memory allocated successfully (address: %p, size: %zu bytes)", mem, size);
return mem;
}
void arena_reset__(const char *file, const char *func, int line, Arena *arena) {
arena->current = arena->begin;
// Function entry logging
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Arena %p reset", arena->begin);
"ALLOC: Resetting arena (address: %p)", arena);
// Check for NULL arena
if (!arena) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"ALLOC: Attempted to reset NULL arena");
return;
}
// Reset the arena
arena->current = arena->begin;
// Operation success logging
raise_message(LOG_LEVEL_LOG, file, func, line,
"ALLOC: Arena reset successfully (address: %p, capacity: %zu bytes)",
arena->begin, arena->capacity);
}
void arena_free__(const char *file, const char *func, int line, Arena *arena) {
// Function entry logging
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Freeing arena at %p", arena->begin);
"FREE: Releasing arena memory (address: %p)", arena);
// Check for NULL arena
if (!arena) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"FREE: Attempted to free NULL arena");
return;
}
// Check for NULL begin pointer
if (!arena->begin) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"FREE: Attempted to free arena with NULL memory block");
return;
}
// Calculate used memory for logging
size_t used = (size_t)arena->current - (size_t)arena->begin;
// Free the memory
free(arena->begin);
// Success logging
raise_message(LOG_LEVEL_LOG, file, func, line,
"FREE: Arena released successfully (address: %p, capacity: %zu bytes, used: %zu bytes)",
arena->begin, arena->capacity, used);
// Clear the pointers
arena->begin = NULL;
arena->current = NULL;
arena->capacity = 0;
}
char* arena_strdup__(const char *file, const char *func, int line, Arena *arena, const char *s) {
char *result;
if (s) {
size_t len = strlen(s) + 1;
result = (char*)arena_alloc__(file, func, line, arena, len);
memcpy(result, s, len);
} else {
result = NULL;
// Function entry logging
raise_message(LOG_LEVEL_TRACE, file, func, line,
"ALLOC: Duplicating string (arena: %p, source: %p, preview: %.20s%s)",
arena, s, s ? s : "", s && strlen(s) > 20 ? "..." : "");
// Check for NULL string
if (!s) {
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"ALLOC: Source string is NULL, returning NULL");
return NULL;
}
// Calculate string length and allocate memory
size_t len = strlen(s) + 1;
// Success case
char *result = (char*)arena_alloc__(file, func, line, arena, len);
// Copy the string
memcpy(result, s, len);
// Success logging
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"ALLOC: String duplicated successfully (result: %p, length: %zu bytes)",
result, len);
return result;
}
char* arena_repstr__(const char *file, const char *func, int line, Arena *arena,
const char *src, size_t start, size_t len, const char *rep) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "arena_repstr__(%p, %p, %zu, \"%s\")", src, start, len, rep);
// Function entry logging
raise_message(LOG_LEVEL_TRACE, file, func, line,
"STRING: Replacing substring (source: %p, start: %zu, length: %zu, replacement: %.20s%s)",
src, start, len, rep, strlen(rep) > 20 ? "..." : "");
// Check inputs
if (!src) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"STRING: Source string is NULL");
return NULL;
}
if (!rep) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"STRING: Replacement string is NULL");
return NULL;
}
// Calculate lengths
int src_len = strlen(src);
int rep_len = strlen(rep);
// Validate start and length
if (start > (size_t)src_len) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"STRING: Start position %zu exceeds source length %d", start, src_len);
// Return a copy of the source string
return arena_strdup__(file, func, line, arena, src);
}
if (start + len > (size_t)src_len) {
size_t old_len = len;
len = src_len - start;
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"STRING: Adjusted length from %zu to %zu to fit source bounds", old_len, len);
}
// Calculate new length and allocate memory
int new_len = src_len - (int)len + rep_len;
char *new_str = (char*)arena_alloc__(file, func, line, arena, new_len + 1);
// Perform the replacement operation
memcpy(new_str, src, start);
memcpy(new_str + start, rep, rep_len);
strcpy(new_str + start + rep_len, src + start + len);
// Success logging
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"STRING: Replacement complete (result: %p, new length: %d)", new_str, new_len);
return new_str;
}
@@ -210,36 +380,66 @@ void* arena_realloc_copy__(const char *file, const char *func, int line, Arena *
// ----------
void substr_clone__(const char *file, const char *func, int line, const char * const src, char *dest, size_t from, size_t len) {
// Log function entry with all parameters.
// Log function entry at TRACE level
raise_message(LOG_LEVEL_TRACE, file, func, line,
"substr_cloning(src=\"%s\", src_ptr=%p, dest=%p, from=%zu, len=%zu)",
src, src, dest, from, len);
"Function called with src=%p, dest=%p, from=%zu, len=%zu",
src, dest, from, len);
if (!src || !dest) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"Invalid NULL pointer: %s%s",
(!src ? "src " : ""),
(!dest ? "dest" : ""));
if (dest) dest[0] = '\0';
return;
}
size_t srclen = strlen(src);
if (from >= srclen) {
// Log warning with context when 'from' is out of range.
// Log warning with context when 'from' is out of range
raise_message(LOG_LEVEL_WARN, file, func, line,
"Invalid 'from' index (%zu): exceeds source length (%zu)",
"Out of range: 'from' index (%zu) exceeds source length (%zu)",
from, srclen);
dest[0] = '\0';
return;
}
if (from + len > srclen)
len = srclen - from;
// Adjust length if needed
if (from + len > srclen) {
size_t old_len = len;
len = srclen - from;
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"Adjusted length from %zu to %zu to fit source bounds",
old_len, len);
}
// Copy the substring
strncpy(dest, src + from, len);
dest[len] = '\0';
// Log success message with result.
// Log success at TRACE level
raise_message(LOG_LEVEL_TRACE, file, func, line,
"Completed substr_cloning: result=\"%s\", copied_length=%zu",
dest, len);
"Successfully copied %zu bytes: \"%.*s\"",
len, (int)len, dest);
}
// ----------
// -- Json --
// ----------
const char* json_type_to_string(JsonType type) {
switch (type) {
case JSON_NULL: return "NULL";
case JSON_BOOL: return "BOOL";
case JSON_NUMBER: return "NUMBER";
case JSON_STRING: return "STRING";
case JSON_ARRAY: return "ARRAY";
case JSON_OBJECT: return "OBJECT";
default: return "UNKNOWN";
}
}
/* Utility: Skip whitespace */
static const char *skip_whitespace(const char *s) {
while (*s && isspace((unsigned char)*s))
@@ -458,9 +658,40 @@ static Json *json_parse_value__(const char *file, const char *func, int line, co
}
Json *json_parse__(const char* file, const char* func, int line, Arena *arena, const char **s) {
// Function entry logging with DEBUG level
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"PARSE: Starting JSON parsing (input: %p)", *s);
// Check input parameters
if (!s || !*s) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"PARSE: Invalid input parameters (NULL pointer provided for JSON parsing)");
return NULL;
}
if (!arena) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"PARSE: Invalid arena (NULL) provided for JSON parsing");
return NULL;
}
// Show input preview for debugging with TRACE level
raise_message(LOG_LEVEL_TRACE, file, func, line,
"PARSE: Input preview: '%.20s%s'", *s, strlen(*s) > 20 ? "..." : "");
// Process JSON value
Json *result = json_parse_value__(file, func, line, s, arena);
if (!result)
raise_message(LOG_LEVEL_DEBUG, file, func, line, "json_parse failed at position: %p", *s);
// Log parsing result
if (!result) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"PARSE: Failed to parse JSON at position %p (context: '%.10s')",
*s, *s && strlen(*s) > 0 ? *s : "<empty>");
} else {
raise_message(LOG_LEVEL_LOG, file, func, line,
"PARSE: JSON parsing completed successfully (type: %s)", json_type_to_string(result->type));
}
return result;
}
@@ -472,68 +703,185 @@ char *json_to_string__(const char* file, const char* func, int line, Arena *aren
When raw is non-zero and the item is a JSON_STRING, it is printed without quotes.
*/
char *json_to_string_with_opts__(const char* file, const char* func, int line, Arena *arena, const Json * const item, JsonRawOpt raw) {
char *out = arena_alloc__(file, func, line, arena, 1024);
if (!out) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Memory allocation failed in json_to_string_with_opts");
// Function entry with DEBUG level
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"FORMAT: Starting JSON conversion to string (item: %p, raw_mode: %s)",
item, raw == JSON_RAW ? "enabled" : "disabled");
// Check input parameters
if (!item) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"FORMAT: Invalid JSON object (NULL) provided for string conversion");
return NULL;
}
if (!arena) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"FORMAT: Invalid arena (NULL) provided for string conversion");
return NULL;
}
// Allocate memory for the string
char *out = arena_alloc__(file, func, line, arena, 1024);
if (!out) {
raise_message(LOG_LEVEL_EXCEPTION, file, func, line,
"FORMAT: Memory allocation failed during JSON string conversion");
return NULL;
}
char *ptr = out;
const char* type_name = "unknown";
// Formatting based on type
if (item->type == JSON_OBJECT) {
ptr += sprintf(ptr, "{");
type_name = "object";
Json *child = item->child;
int child_count = 0;
raise_message(LOG_LEVEL_TRACE, file, func, line,
"FORMAT: Processing JSON object children");
while (child) {
ptr += sprintf(ptr, "\"%s\":", child->key ? child->key : "");
char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw);
ptr += sprintf(ptr, "%s", child_str);
if (child->next)
if (child_str) {
ptr += sprintf(ptr, "%s", child_str);
} else {
raise_message(LOG_LEVEL_WARN, file, func, line,
"FORMAT: Failed to stringify child element (key=%s)",
child->key ? child->key : "<null>");
}
if (child->next) {
ptr += sprintf(ptr, ",");
}
child = child->next;
child_count++;
}
sprintf(ptr, "}");
raise_message(LOG_LEVEL_TRACE, file, func, line,
"FORMAT: Object conversion complete with %d child elements", child_count);
} else if (item->type == JSON_ARRAY) {
ptr += sprintf(ptr, "[");
type_name = "array";
Json *child = item->child;
int child_count = 0;
raise_message(LOG_LEVEL_TRACE, file, func, line,
"FORMAT: Processing JSON array elements");
while (child) {
char *child_str = json_to_string_with_opts__(file, func, line, arena, child, raw);
ptr += sprintf(ptr, "%s", child_str);
if (child->next)
if (child_str) {
ptr += sprintf(ptr, "%s", child_str);
} else {
raise_message(LOG_LEVEL_WARN, file, func, line,
"FORMAT: Failed to stringify array element at index %d", child_count);
}
if (child->next) {
ptr += sprintf(ptr, ",");
}
child = child->next;
child_count++;
}
sprintf(ptr, "]");
raise_message(LOG_LEVEL_TRACE, file, func, line,
"FORMAT: Array conversion complete with %d elements", child_count);
} else if (item->type == JSON_STRING) {
if ((int)raw)
sprintf(ptr, "%s", item->JsonValue.string);
else
sprintf(ptr, "\"%s\"", item->JsonValue.string);
type_name = "string";
if ((int)raw) {
sprintf(ptr, "%s", item->JsonValue.string ? item->JsonValue.string : "");
} else {
sprintf(ptr, "\"%s\"", item->JsonValue.string ? item->JsonValue.string : "");
}
} else if (item->type == JSON_NUMBER) {
type_name = "number";
sprintf(ptr, "%g", item->JsonValue.number);
} else if (item->type == JSON_BOOL) {
type_name = "boolean";
sprintf(ptr, item->JsonValue.boolean ? "true" : "false");
} else if (item->type == JSON_NULL) {
type_name = "null";
sprintf(ptr, "null");
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Converted JSON to string: %s", out);
raise_message(LOG_LEVEL_LOG, file, func, line,
"FORMAT: JSON %s converted to string (length=%zu)",
type_name, strlen(out));
return out;
}
/* Retrieve an object item by key (case-sensitive) */
Json *json_get_object_item__(const char* file, const char* func, int line, const Json * const object, const char * const key) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "json_get_object_item: Searching for key \"%s\"", key);
if (!object || object->type != JSON_OBJECT) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Invalid object passed to json_get_object_item");
raise_message(LOG_LEVEL_TRACE, file, func, line,
"ACCESS: Searching for key \"%s\" in JSON object %p",
key ? key : "<null>", object);
// Check input parameters
if (!object) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"ACCESS: Invalid object (NULL) passed to json_get_object_item");
return NULL;
}
Json *child = object->child;
while (child) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Comparing child key \"%s\" with \"%s\"", child->key, key);
if (child->key && strcmp(child->key, key) == 0) {
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" found", key);
return child;
}
child = child->next;
if (!key) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"ACCESS: Invalid key (NULL) passed to json_get_object_item");
return NULL;
}
raise_message(LOG_LEVEL_DEBUG, file, func, line, "Key \"%s\" not found in object", key);
if (object->type != JSON_OBJECT) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"ACCESS: JSON value is not an object (actual type: %d)", object->type);
return NULL;
}
// Count the total number of keys for debugging
int total_keys = 0;
Json *debug_scan = object->child;
while (debug_scan) {
total_keys++;
debug_scan = debug_scan->next;
}
raise_message(LOG_LEVEL_TRACE, file, func, line,
"ACCESS: Object has %d key-value pairs", total_keys);
// Perform key search
Json *child = object->child;
int position = 0;
while (child) {
if (child->key) {
raise_message(LOG_LEVEL_TRACE, file, func, line,
"ACCESS: Comparing key \"%s\" with \"%s\" at position %d",
child->key, key, position);
if (strcmp(child->key, key) == 0) {
raise_message(LOG_LEVEL_LOG, file, func, line,
"ACCESS: Found value for key \"%s\" (type: %s)",
key, json_type_to_string(child->type));
return child;
}
} else {
raise_message(LOG_LEVEL_TRACE, file, func, line,
"ACCESS: Skipping element at position %d with NULL key", position);
}
child = child->next;
position++;
}
raise_message(LOG_LEVEL_DEBUG, file, func, line,
"ACCESS: Key \"%s\" not found in object (checked %d items)",
key, position);
return NULL;
}
@@ -543,18 +891,54 @@ Json *json_get_object_item__(const char* file, const char* func, int line, const
// Create a slice from an array with boundary check.
Slice slice_create__(const char *file, const char *func, int line, size_t isize, void *array, size_t array_len, size_t start, size_t len) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "slice_create(<optimized>, <optimized>, <optimized>, <optimized>, <optimized>)");
if (start + len > array_len)
// Function entry logging
raise_message(LOG_LEVEL_TRACE, file, func, line,
"SLICE: Creating slice (source: %p, array_length: %zu, start: %zu, length: %zu, item_size: %zu)",
array, array_len, start, len, isize);
// Boundary check
if (start + len > array_len) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"SLICE: Slice boundaries exceed array length (start: %zu, length: %zu, array_length: %zu)",
start, len, array_len);
return (Slice){NULL, 0, isize};
return (Slice){ (char *)array + start * isize, len, isize };
}
// Create valid slice
Slice result = (Slice){ (char *)array + start * isize, len, isize };
// Success logging
raise_message(LOG_LEVEL_TRACE, file, func, line,
"SLICE: Slice created successfully (data: %p, length: %zu, item_size: %zu)",
result.data, result.len, result.isize);
return result;
}
// Return a subslice from an existing slice.
Slice slice_subslice__(const char *file, const char *func, int line, Slice s, size_t start, size_t len) {
raise_message(LOG_LEVEL_TRACE, file, func, line, "slice_subslice(<optimized>, <optimized>, <optimized>)");
if (start + len > s.len)
// Function entry logging
raise_message(LOG_LEVEL_TRACE, file, func, line,
"SLICE: Creating subslice (source: %p, source_length: %zu, start: %zu, length: %zu)",
s.data, s.len, start, len);
// Boundary check
if (start + len > s.len) {
raise_message(LOG_LEVEL_WARN, file, func, line,
"SLICE: Subslice boundaries exceed source slice length (start: %zu, length: %zu, source_length: %zu)",
start, len, s.len);
return (Slice){NULL, 0, s.isize};
return (Slice){(char*)s.data + start * s.isize, len, s.isize};
}
// Create valid subslice
Slice result = (Slice){(char*)s.data + start * s.isize, len, s.isize};
// Success logging
raise_message(LOG_LEVEL_TRACE, file, func, line,
"SLICE: Subslice created successfully (data: %p, length: %zu, item_size: %zu)",
result.data, result.len, result.isize);
return result;
}
int* arena_slice_copy__(const char *file, const char *func, int line, Arena *arena, Slice s) {
@@ -570,14 +954,14 @@ int* arena_slice_copy__(const char *file, const char *func, int line, Arena *are
// ------------
char* slice_to_debug_str(Arena *arena, Slice slice) {
// Создадим полную информацию о структуре Slice
// Create complete information about the Slice structure
char buffer_meta[128];
snprintf(buffer_meta, sizeof(buffer_meta), "Slice{addr=%p, data=%p, len=%zu, isize=%zu, content=",
(void*)&slice, slice.data, slice.len, slice.isize);
size_t meta_len = strlen(buffer_meta);
// Для NULL-данных выведем простое сообщение
// For NULL data, output a simple message
if (!slice.data) {
char* result = arena_alloc(arena, meta_len + 6);
strcpy(result, buffer_meta);
@@ -589,7 +973,7 @@ char* slice_to_debug_str(Arena *arena, Slice slice) {
size_t buffer_size = meta_len + slice.len * 4 + 20; // Extra space for escaping and closing brace
char* buffer = arena_alloc(arena, buffer_size);
// Копируем метаданные
// Copy metadata
strcpy(buffer, buffer_meta);
char* pos = buffer + meta_len;
@@ -625,7 +1009,7 @@ char* slice_to_debug_str(Arena *arena, Slice slice) {
}
*pos++ = '"';
*pos++ = '}'; // Закрывающая скобка для структуры
*pos++ = '}'; // Closing brace for the structure
*pos = '\0';
raise_trace("slice_to_debug_str: %s", buffer);
@@ -634,22 +1018,11 @@ char* slice_to_debug_str(Arena *arena, Slice slice) {
}
char* json_to_debug_str(Arena *arena, Json json) {
// Добавляем информацию о самой структуре JSON
// Add information about the JSON structure itself
char meta_buffer[256];
const char* type_str = "";
switch (json.type) {
case JSON_NULL: type_str = "NULL"; break;
case JSON_BOOL: type_str = "BOOL"; break;
case JSON_NUMBER: type_str = "NUMBER"; break;
case JSON_STRING: type_str = "STRING"; break;
case JSON_ARRAY: type_str = "ARRAY"; break;
case JSON_OBJECT: type_str = "OBJECT"; break;
default: type_str = "UNKNOWN";
}
snprintf(meta_buffer, sizeof(meta_buffer), "Json{addr=%p, type=%s, key=%s, child=%p, next=%p, value=",
(void*)&json, type_str, json.key ? json.key : "NULL", (void*)json.child, (void*)json.next);
(void*)&json, json_type_to_string(json.type), json.key ? json.key : "NULL", (void*)json.child, (void*)json.next);
size_t meta_len = strlen(meta_buffer);
char value_buffer[256] = {0};
@@ -677,7 +1050,7 @@ char* json_to_debug_str(Arena *arena, Json json) {
}
case JSON_ARRAY: {
// Для массивов просто отметим количество элементов
// For arrays, simply note the number of elements
size_t count = 0;
Json *item = json.child;
while (item) {
@@ -689,7 +1062,7 @@ char* json_to_debug_str(Arena *arena, Json json) {
}
case JSON_OBJECT: {
// Для объектов отметим количество пар ключ-значение
// For objects, note the number of key-value pairs
size_t count = 0;
Json *item = json.child;
while (item) {
@@ -704,8 +1077,8 @@ char* json_to_debug_str(Arena *arena, Json json) {
strcpy(value_buffer, "<UNKNOWN JSON TYPE>");
}
// Создаем итоговую строку
size_t result_len = meta_len + strlen(value_buffer) + 2; // +2 для закрывающей скобки и нулевого символа
// Create final string
size_t result_len = meta_len + strlen(value_buffer) + 2; // +2 for closing brace and null character
char* result = arena_alloc(arena, result_len);
strcpy(result, meta_buffer);

View File

@@ -32,8 +32,10 @@ typedef enum {
COLOR_MODE_DISABLE
} ColorMode;
// Static color mode variable
static ColorMode color_mode __attribute__((unused)) = COLOR_MODE_AUTO;
// External color mode variable declaration
extern ColorMode color_mode;
const char* color_mode_to_string(ColorMode mode);
// Function to set color mode
void set_output_color_mode(ColorMode mode);
@@ -72,15 +74,59 @@ void set_output_color_mode(ColorMode mode);
// -- Logger --
// ------------
/**
* Log levels following a consistent severity-based hierarchy.
* Each level includes specific guidance on when it should be used.
*/
typedef enum {
/**
* TRACE: Most detailed information for in-depth debugging
* Use for: Deep diagnostic details, function entry/exit, variable dumps
* Visibility: Development environments only, rarely used in production
*/
LOG_LEVEL_TRACE,
LOG_LEVEL_ZALUPA,
/**
* DEBUG: Detailed information useful during development
* Use for: Development-time debugging, showing variable states, internal flows
* Visibility: Development and debugging environments, rarely in production
*/
LOG_LEVEL_DEBUG,
/**
* LOG: General operational events
* Use for: Runtime events worth logging but not requiring attention
* Visibility: Always written to logs, useful for auditing/diagnostics
*/
LOG_LEVEL_LOG,
/**
* INFO: Informational messages highlighting progress
* Use for: Normal but noteworthy events, state changes, startup/shutdown events
* Visibility: Visible to client applications if configured
*/
LOG_LEVEL_INFO,
/**
* NOTICE: More important events than INFO, but not warnings
* Use for: Important state changes, significant operations, configuration changes
* Visibility: Displayed to client by default, meant to be seen
*/
LOG_LEVEL_NOTICE,
/**
* WARN: Potential problems that don't prevent normal operation
* Use for: Unexpected behaviors, deprecated feature usage, recoverable errors
* Visibility: Alerts both client and server logs, needs attention
*/
LOG_LEVEL_WARN,
LOG_LEVEL_EXCEPTION,
/**
* EXCEPTION: Serious errors requiring immediate attention
* Use for: Critical failures, data loss risks, business rule violations
* Visibility: Highest priority, often leads to operation termination
*/
LOG_LEVEL_EXCEPTION
} LogLevel;
void logger_level_reset();
@@ -91,6 +137,17 @@ void logger_level(LogLevel level);
LogLevel log_level_from_string(const char *level_str);
/**
* Core logging function that formats and outputs log messages.
*
* @param level Severity level of the message
* @param file Source file where log was generated
* @param func Function where log was generated
* @param line Line number where log was generated
* @param format Printf-style format string
* @param ... Variable arguments for format string
* @return Timestamp string for the log message
*/
char* raise_message(LogLevel level, const char *file, const char *func, int line, const char *format, ...);
#ifndef PRECOMPILED_LOG_LEVEL
@@ -139,11 +196,24 @@ char* raise_message(LogLevel level, const char *file, const char *func, int line
#define raise_exception(...) raise_message(LOG_LEVEL_EXCEPTION, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#endif
#if PRECOMPILED_LOG_LEVEL > LOG_LEVEL_ZALUPA
#define raise_zalupa(...) ((void)0)
#else
#define raise_zalupa(...) raise_message(LOG_LEVEL_ZALUPA, __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#endif
// ----------
// -- misc --
// ----------
#define MEM_b 1
#define MEM_KiB 1024
#define MEM_MiB (MEM_KiB * 1024)
#define MEM_GiB (MEM_MiB * 1024)
#define MEM_TiB (MEM_TiB * 1024)
#define MEM_PiB (MEM_TiB * 1024)
#define MEM_EiB (MEM_PiB * 1024)
#define MEM_ZiB (MEM_EiB * 1024)
#define MEM_YiB (MEM_ZiB * 1024)
#define MEM_RiB (MEM_YiB * 1024)
#define MEM_QiB (MEM_RiB * 1024)
void substr_clone__(const char *file, const char *func, int line, const char * const src, char *dest, size_t from, size_t len);
#define substr_clone(src, dest, from, len) substr_clone__(__FILE__, __func__, __LINE__, src, dest, from, len)
// -----------
// -- arena --
@@ -201,24 +271,17 @@ void* arena_realloc_copy__(const char *file, const char *func, int line, Arena *
#define arena_realloc_copy(arena, old_ptr, old_size, new_size) \
arena_realloc_copy__(__FILE__, __func__, __LINE__, arena, old_ptr, old_size, new_size)
// ----------
// -- misc --
// ----------
#define MEM_b 1
#define MEM_KiB 1024
#define MEM_MiB (MEM_KiB * 1024)
#define MEM_GiB (MEM_MiB * 1024)
#define MEM_TiB (MEM_TiB * 1024)
#define MEM_PiB (MEM_TiB * 1024)
#define MEM_EiB (MEM_PiB * 1024)
#define MEM_ZiB (MEM_EiB * 1024)
#define MEM_YiB (MEM_ZiB * 1024)
#define MEM_RiB (MEM_YiB * 1024)
#define MEM_QiB (MEM_RiB * 1024)
static Arena disposable_arena __attribute__((unused)) = {0};
void substr_clone__(const char *file, const char *func, int line, const char * const src, char *dest, size_t from, size_t len);
#define substr_clone(src, dest, from, len) substr_clone__(__FILE__, __func__, __LINE__, src, dest, from, len)
#define DISPOSABLE_ARENA __extension__ ({ \
if (disposable_arena.begin == NULL) { \
disposable_arena = arena_init__(__FILE__, __func__, __LINE__, MEM_MiB); \
} else { \
arena_reset(&disposable_arena); \
} \
&disposable_arena; \
})
// ----------
// -- Json --
@@ -310,6 +373,10 @@ int* arena_slice_copy__(const char *file, const char *func, int line, Arena *are
buf; \
})
// ------------
// -- Debug --
// ------------
// Utility functions for debug output of Slice and Json structures
char* slice_to_debug_str(Arena *arena, Slice slice);
char* json_to_debug_str(Arena *arena, Json json);

View File

@@ -26,7 +26,7 @@
} while(0)
int main(void) {
set_output_color_mode(COLOR_MODE_DISABLE);
logger_level(LOG_LEVEL_DEBUG);
TEST_RAISE_GENERIC(raise_debug, LOG_LEVEL_DEBUG, "DEBUG");
TEST_RAISE_GENERIC(raise_log, LOG_LEVEL_LOG, "LOG");
@@ -35,6 +35,6 @@ int main(void) {
TEST_RAISE_GENERIC(raise_warn, LOG_LEVEL_WARN, "WARN");
TEST_RAISE_GENERIC(raise_exception, LOG_LEVEL_EXCEPTION, "EXCEPTION");
printf("%s all tests passed.\n", __FILE__);
printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
return 0;
}

View File

@@ -91,7 +91,6 @@ void test_arena_overwrite_detection() {
}
int main() {
set_output_color_mode(COLOR_MODE_DISABLE);
logger_level(LOG_LEVEL_DEBUG);
test_arena_init();
@@ -102,5 +101,5 @@ int main() {
test_arena_strdup();
test_arena_repstr();
test_arena_overwrite_detection();
printf("%s all tests passed.\n", __FILE__);
printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
}

View File

@@ -124,6 +124,8 @@ static void test_arena_reset_reuse(void) {
}
int main(void) {
logger_level(LOG_LEVEL_DEBUG);
test_parse_json_object();
test_parse_json_number();
test_parse_json_string();
@@ -134,6 +136,6 @@ int main(void) {
test_nested_json_object();
test_arena_reset_reuse();
printf("%s all tests passed.\n", __FILE__);
printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
return 0;
}

View File

@@ -83,7 +83,6 @@ void test_slice_string() {
}
int main() {
set_output_color_mode(COLOR_MODE_DISABLE);
logger_level(LOG_LEVEL_DEBUG);
test_slice_create();
@@ -92,6 +91,6 @@ int main() {
test_slice_edge_cases();
test_slice_string();
printf("%s all tests passed.\n", __FILE__);
printf("%s%s all tests passed.%s\n", OPTIONAL_COLOR(COLOR_GREEN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
return 0;
}