|
55 | 55 |
|
56 | 56 | #define SLICK_LOGGER_VERSION_MAJOR 1 |
57 | 57 | #define SLICK_LOGGER_VERSION_MINOR 0 |
58 | | -#define SLICK_LOGGER_VERSION_PATCH 5 |
59 | | -#define SLICK_LOGGER_VERSION "1.0.5" |
| 58 | +#define SLICK_LOGGER_VERSION_PATCH 6 |
| 59 | +#define SLICK_LOGGER_VERSION "1.0.6" |
60 | 60 |
|
61 | 61 | #ifndef SLICK_LOGGER_MAX_ARGS |
62 | 62 | #define SLICK_LOGGER_MAX_ARGS 20 |
@@ -1441,26 +1441,27 @@ inline void Logger::log_to_sink(int sink_index, LogLevel level, FormatT&& format |
1441 | 1441 | entry.sink_index = sink_index; |
1442 | 1442 | if constexpr (IS_STRING_LITERAL(format)) { |
1443 | 1443 | entry.format_ptr = format; // String literal - safe to store pointer |
1444 | | - |
1445 | | - if constexpr (is_single_format_args_v<Args...>) { |
1446 | | - // Pre-built std::format_args: unpack values into the entry on the |
1447 | | - // calling thread (format_args holds non-owning references). |
1448 | | - enqueue_format_args(entry, |
1449 | | - std::get<0>(std::forward_as_tuple(std::forward<Args>(args)...))); |
1450 | | - } else { |
1451 | | - // Normal path: push individual arguments |
1452 | | - entry.arg_count = sizeof...(args); |
1453 | | - size_t arg_idx = 0; |
1454 | | - static_assert(sizeof...(args) <= SLICK_LOGGER_MAX_ARGS, "Too many log arguments"); |
1455 | | - (enqueue_argument(entry.args[arg_idx++], std::forward<Args>(args)), ...); |
1456 | | - } |
1457 | 1444 | } |
1458 | 1445 | else { |
1459 | | - // Store dynamic string in string queue |
1460 | | - static_assert(sizeof...(args) == 0, "Dynamic format strings are only supported when there are no arguments, to avoid dangling pointers."); |
1461 | | - entry.format_ptr = "{}"; |
1462 | | - entry.arg_count = 1; |
1463 | | - enqueue_argument(entry.args[0], format); |
| 1446 | + // Non-literal format strings are copied into the string queue so their |
| 1447 | + // lifetime extends until the writer thread consumes the entry. |
| 1448 | + static_assert(std::is_convertible_v<FormatT, std::string_view>, |
| 1449 | + "Format string type must be a string literal or convertible to std::string_view."); |
| 1450 | + entry.format_ptr = store_string_in_queue( |
| 1451 | + std::string_view{std::forward<FormatT>(format)}).ptr; |
| 1452 | + } |
| 1453 | + |
| 1454 | + if constexpr (is_single_format_args_v<Args...>) { |
| 1455 | + // Pre-built std::format_args: unpack values into the entry on the |
| 1456 | + // calling thread (format_args holds non-owning references). |
| 1457 | + enqueue_format_args(entry, |
| 1458 | + std::get<0>(std::forward_as_tuple(std::forward<Args>(args)...))); |
| 1459 | + } else { |
| 1460 | + // Normal path: push individual arguments |
| 1461 | + entry.arg_count = sizeof...(args); |
| 1462 | + size_t arg_idx = 0; |
| 1463 | + static_assert(sizeof...(args) <= SLICK_LOGGER_MAX_ARGS, "Too many log arguments"); |
| 1464 | + (enqueue_argument(entry.args[arg_idx++], std::forward<Args>(args)), ...); |
1464 | 1465 | } |
1465 | 1466 |
|
1466 | 1467 |
|
@@ -1602,8 +1603,9 @@ inline StringRef Logger::store_string_in_queue(std::string_view str) { |
1602 | 1603 |
|
1603 | 1604 | char* dest = (*string_queue_)[start_index]; |
1604 | 1605 | if (length) { |
1605 | | - // Copy string data |
1606 | | - std::memcpy(dest, str.data(), len); |
| 1606 | + // Copy only the string payload, then terminate explicitly. |
| 1607 | + std::memcpy(dest, str.data(), length); |
| 1608 | + dest[length] = '\0'; |
1607 | 1609 | } |
1608 | 1610 | else { |
1609 | 1611 | *dest = '\0'; |
|
0 commit comments