Import srslog into srsue (#1556)

- Import the srslog project into srslte.
- Ported srsue app to use the new logging framework.
- Implemented a wrapper that dispatches log entries to srslog.
- Renamed an existing log test to be more specific to avoid name clashes.
This commit is contained in:
faluco 2020-07-22 16:04:18 +02:00 committed by GitHub
parent d441486f76
commit efdff8ba4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 15080 additions and 19 deletions

View File

@ -0,0 +1,49 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLTE_LOGGER_SRSLOG_WRAPPER_H
#define SRSLTE_LOGGER_SRSLOG_WRAPPER_H
#include "srslte/common/logger.h"
namespace srslog {
class log_channel;
} // namespace srslog
namespace srslte {
/// This logger implementation uses the srsLog framework to write log entries.
class srslog_wrapper : public logger
{
public:
explicit srslog_wrapper(srslog::log_channel& chan) : chan(chan) {}
void log(unique_log_str_t msg) override;
private:
srslog::log_channel& chan;
};
} // namespace srslte
#endif // SRSLTE_LOGGER_SRSLOG_WRAPPER_H

View File

@ -28,6 +28,7 @@
#define SRSLTE_SIGNAL_HANDLER_H
#include "srslte/common/logger_file.h"
#include "srslte/srslog/sink.h"
#include <signal.h>
#include <stdio.h>
@ -38,6 +39,7 @@ extern "C" {
#define SRSLTE_TERM_TIMEOUT_S (5)
// static vars required by signal handling
static srslog::sink* log_sink = nullptr;
static srslte::logger_file logger_file;
static bool running = true;
@ -46,7 +48,11 @@ static void srslte_signal_handler(int signal)
switch (signal) {
case SIGALRM:
fprintf(stderr, "Couldn't stop after %ds. Forcing exit.\n", SRSLTE_TERM_TIMEOUT_S);
//:TODO: refactor the sighandler, should not depend on log utilities
logger_file.stop();
if (log_sink) {
log_sink->flush();
}
raise(SIGKILL);
default:
// all other registered signals try to stop the app gracefully

View File

@ -0,0 +1,27 @@
Copyright (c) 2012 - present, Victor Zverovich
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- Optional exception to the license ---
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,568 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COLOR_H_
#define FMT_COLOR_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
bright_black = 90,
bright_red,
bright_green,
bright_yellow,
bright_blue,
bright_magenta,
bright_cyan,
bright_white
};
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
};
// rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
namespace internal {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
}
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
}
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
};
} // namespace internal
// Experimental text formatting support.
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
return lhs |= rhs;
}
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
const text_style& rhs) {
return lhs &= rhs;
}
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == internal::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits<Char>::length(buffer);
}
private:
Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::background_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
}
template <>
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream);
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
}
template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background =
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf);
}
} // namespace internal
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
}
/**
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
}
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return vformat(ts, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View File

@ -0,0 +1,595 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
kind part_kind;
union value {
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
} else {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(range(out), cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(internal::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,78 @@
// Formatting library for C++ - std::locale support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
} // namespace internal
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<type_identity_t<OutputIt>, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_

View File

@ -0,0 +1,392 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno>
#include <clocale> // for locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
#ifdef _WIN32
namespace internal {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(wstring_view s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace internal
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args&... args) {
init(error_code, message, make_format_args(args...));
}
};
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
#endif // _WIN32
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@ -0,0 +1,166 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
template <typename CHar> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace internal {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
// Hide all operator<< from std::basic_ostream<Char>.
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
};
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
using result = decltype(test<T>(0));
public:
static const bool value = result::value;
};
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: private formatter<basic_string_view<Char>, Char> {
auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace internal
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_

View File

@ -0,0 +1,2 @@
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

View File

@ -0,0 +1,726 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
class printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = internal::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = internal::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types.
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = internal::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types.
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
private:
using format_specs = basic_format_specs<Char>;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
return 0;
}
};
template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
}
template <typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
basic_format_args<Context> args) {
return Context(it, format, args).format();
}
} // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
\rst
The ``printf`` argument formatter.
\endrst
*/
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
public:
using iterator = typename Range::iterator;
private:
using char_type = typename Range::value_type;
using base = internal::arg_formatter_base<Range>;
using context_type = basic_printf_context<iterator, char_type>;
context_type& context_;
void write_null_pointer(char) {
this->specs()->type = 0;
this->write("(nil)");
}
void write_null_pointer(wchar_t) {
this->specs()->type = 0;
this->write(L"(nil)");
}
public:
using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the output buffer and *specs* contains format
specifier information for standard argument types.
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
}
return this->out();
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
return base::operator()(value);
}
/** Formats a null-terminated C string. */
iterator operator()(const char* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
}
/** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
}
iterator operator()(basic_string_view<char_type> value) {
return base::operator()(value);
}
iterator operator()(monostate value) { return base::operator()(value); }
/** Formats a pointer. */
iterator operator()(const void* value) {
if (value) return base::operator()(value);
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
}
/** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(context_.parse_context(), context_);
return this->out();
}
};
template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
using format_specs = basic_format_specs<char_type>;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args), parse_ctx_(format_str) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
internal::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
OutputIt format();
};
template <typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
break;
case ' ':
specs.sign = sign::space;
break;
case '#':
specs.alt = true;
break;
default:
return;
}
}
}
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
const Char* end,
format_specs& specs) {
int arg_index = -1;
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
} else {
if (c == '0') specs.fill[0] = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
specs.width = value;
return arg_index;
}
}
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
internal::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
}
template <typename OutputIt, typename Char>
template <typename ArgFormatter>
OutputIt basic_printf_context<OutputIt, Char>::format() {
auto out = this->out();
const Char* start = parse_ctx_.begin();
const Char* end = parse_ctx_.end();
auto it = start;
while (it != end) {
char_type c = *it++;
if (c != '%') continue;
if (it != end && *it == c) {
out = std::copy(start, it, out);
start = ++it;
continue;
}
out = std::copy(start, it - 1, out);
format_specs specs;
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision = static_cast<int>(
visit_format_arg(internal::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic())
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using internal::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j':
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<std::size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--it;
convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
switch (specs.type) {
case 'i':
case 'u':
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
arg);
break;
}
}
start = it;
// Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<printf_context, Args...> make_printf_args(
const Args&... args) {
return {args...};
}
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
const Args&... args) {
return {args...};
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
return to_string(buffer);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(
std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
return vfprintf(stdout, to_string_view(format), args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(
std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
return static_cast<int>(buffer.size());
}
/** Formats arguments and writes the output to the range. */
template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(
internal::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
make_format_args<context>(args...));
}
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

View File

@ -0,0 +1,387 @@
// Formatting library for C++ - experimental range support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE
template <typename Char> struct formatting_base {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
Char delimiter;
Char postfix;
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> {
Char prefix;
Char delimiter;
Char postfix;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIterator>
OutputIterator copy(const char* str, OutputIterator out) {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIterator>
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string interface, like std::string_view.
template <typename T> class is_like_std_string {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T>
struct is_range_<
T, conditional_t<false,
conditional_helper<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>> : std::true_type {};
#endif
/// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template <typename T, std::size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
}
template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
T const&) {
return {};
}
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " {}" : "{}";
}
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
};
template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private:
// C++11 generic lambda for format()
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = internal::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
public:
formatting_tuple<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
}
};
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
formatting_range<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
out = format_to(out, " ... <other elements>");
break;
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
std::initializer_list<T> list, string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
std::initializer_list<T> list, wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@ -0,0 +1,51 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_DETAIL_LOG_BACKEND_H
#define SRSLOG_DETAIL_LOG_BACKEND_H
#include "srslte/srslog/detail/log_entry.h"
namespace srslog {
namespace detail {
/// The log backend receives generated log entries from the application. Each
/// entry gets distributed to the corresponding sinks.
/// NOTE: Thread safe class.
class log_backend
{
public:
virtual ~log_backend() = default;
/// Starts the processing of incoming log entries.
/// NOTE: Calling this function more than once has no side effects.
virtual void start() = 0;
/// Pushes a log entry into the backend.
virtual void push(detail::log_entry&& entry) = 0;
};
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_LOG_BACKEND_H

View File

@ -0,0 +1,45 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_DETAIL_LOG_ENTRY_H
#define SRSLOG_DETAIL_LOG_ENTRY_H
#include "srslte/srslog/bundled/fmt/printf.h"
namespace srslog {
class sink;
namespace detail {
/// This structure packs all the required data required to create a log entry in
/// the backend.
struct log_entry {
sink* s;
std::string fmtstring;
fmt::dynamic_format_arg_store<fmt::printf_context> store;
};
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_LOG_ENTRY_H

View File

@ -0,0 +1,209 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_DETAIL_SUPPORT_ANY_H
#define SRSLOG_DETAIL_SUPPORT_ANY_H
#include <memory>
#include <type_traits>
namespace srslog {
namespace detail {
/// Tag for in place construction of the any class.
template <typename T>
struct in_place_type_t {
explicit in_place_type_t() = default;
};
/// Type trait to detect if T is a in_place_type_t tag.
template <typename T>
struct is_in_place_type_t : std::false_type {};
template <typename T>
struct is_in_place_type_t<in_place_type_t<T>> : std::true_type {};
/// This is a very minimalist and non compliant implementation of std::any which
/// is included in C++17.
/// From the standard: "The class any describes a type-safe container for single
/// values of any type".
class any
{
public:
//:FIXME: Clang 3.8 does not compile when default constructing a const object
// due to DR253. Declare the defaulted constructor out of the class.
any();
/// Disallow copies for simplicity.
any(const any& other) = delete;
any& operator=(const any& other) = delete;
/// Constructs an object of type decayed T with the provided argument.
/// This constructor only participates in overload resolution when the decayed
/// T meets all of the following conditions:
/// a) is not of class any.
/// b) is move constructible.
/// c) is not an specialization of in_place_type_t.
/// Otherwise the rest of special member functions are considered.
template <
typename T,
typename std::enable_if<
!std::is_same<typename std::decay<T>::type, any>{} &&
std::is_move_constructible<typename std::decay<T>::type>{} &&
!is_in_place_type_t<typename std::decay<T>::type>{},
int>::type = 0>
explicit any(T&& t) :
storage(new storage_impl<typename std::decay<T>::type>(std::forward<T>(t)))
{}
/// Constructs an object of type decayed T directly into the internal storage
/// forwarding the provided arguments.
/// This constructor only participates in overload resolution when the decayed
/// T meets all of the following conditions:
/// a) T(args...) is constructible.
/// Otherwise the rest of special member functions are considered.
template <typename T,
typename... Args,
typename std::enable_if<
std::is_constructible<typename std::decay<T>::type, Args...>{},
int>::type = 0>
explicit any(in_place_type_t<T>, Args&&... args) :
storage(new storage_impl<typename std::decay<T>::type>(
std::forward<Args>(args)...))
{}
any(any&& other) : storage(std::move(other.storage)) {}
any& operator=(any&& other)
{
storage = std::move(other.storage);
return *this;
}
/// Checks whether the object contains a value.
bool has_value() const { return (storage != nullptr); }
/// If not empty, destroys the contained object.
void reset() { storage.reset(); }
/// Swaps the content of two any objects.
void swap(any& other)
{
using std::swap;
swap(storage, other.storage);
}
private:
template <typename T>
friend T* any_cast(any* operand);
template <typename T>
friend const T* any_cast(const any* operand);
/// Type erased interface for type identification purposes.
struct type_interface {
virtual ~type_interface() = default;
virtual const void* type() const = 0;
};
/// Concrete type implementation with data storage.
template <typename T>
struct storage_impl : public type_interface {
template <typename... Args>
explicit storage_impl(Args&&... args) : data(std::forward<Args>(args)...)
{}
storage_impl(const storage_impl& other) = delete;
storage_impl& operator=(const storage_impl& other) = delete;
const void* type() const override { return &type_tag<T>::tag; }
T data;
};
/// Discriminant tag for type identification.
template <typename T>
struct type_tag {
static const char tag;
};
private:
std::unique_ptr<type_interface> storage;
};
//:FIXME: declared out of line, see FIXME above.
inline any::any() = default;
/// Constructs an any object containing an object of type T, passing the
/// provided arguments to T's constructor.
template <typename T, typename... Args>
inline any make_any(Args&&... args)
{
return any(in_place_type_t<T>{}, std::forward<Args>(args)...);
}
/// When the requested T matches that of the contents of operand, a pointer to
/// the value contained by operand is returned, otherwise returns a nullptr.
/// Non-const overload.
template <typename T>
inline T* any_cast(any* operand)
{
if (!operand || !operand->storage)
return nullptr;
using U =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
if (operand->storage->type() != &any::type_tag<U>::tag)
return nullptr;
return &static_cast<any::storage_impl<U>*>(operand->storage.get())->data;
}
/// When the requested T matches that of the contents of operand, a pointer to
/// the value contained by operand is returned, otherwise returns a nullptr.
/// Const overload.
template <typename T>
inline const T* any_cast(const any* operand)
{
if (!operand || !operand->storage)
return nullptr;
using U =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
if (operand->storage->type() != &any::type_tag<U>::tag)
return nullptr;
return &static_cast<any::storage_impl<U>*>(operand->storage.get())->data;
}
inline void swap(any& lhs, any& rhs)
{
lhs.swap(rhs);
}
template <typename T>
const char any::type_tag<T>::tag = 0;
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_SUPPORT_ANY_H

View File

@ -0,0 +1,53 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_DETAIL_SUPPORT_ERROR_STRING_H
#define SRSLOG_DETAIL_SUPPORT_ERROR_STRING_H
#include <string>
namespace srslog {
namespace detail {
/// This is a lightweight error class that encapsulates a string for error
/// reporting.
class error_string
{
std::string error;
public:
error_string() = default;
/*implicit*/ error_string(std::string error) : error(std::move(error)) {}
/*implicit*/ error_string(const char* error) : error(error) {}
/// Returns the error string.
const std::string& get_error() const { return error; }
explicit operator bool() const { return !error.empty(); }
};
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_SUPPORT_ERROR_STRING_H

View File

@ -0,0 +1,60 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_DETAIL_SUPPORT_MEMORY_BUFFER_H
#define SRSLOG_DETAIL_SUPPORT_MEMORY_BUFFER_H
#include <string>
namespace srslog {
namespace detail {
/// This class wraps a read-only and non owning memory block, providing simple
/// methods to access its contents.
class memory_buffer
{
const char* const buffer;
const size_t length;
public:
memory_buffer(const char* buffer, size_t length) :
buffer(buffer),
length(length)
{}
explicit memory_buffer(const std::string& s) :
buffer(s.data()),
length(s.size())
{}
/// Returns a pointer to the start of the memory block.
const char* data() const { return buffer; }
/// Returns the size of the memory block.
size_t size() const { return length; }
};
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_SUPPORT_MEMORY_BUFFER_H

View File

@ -0,0 +1,180 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_DETAIL_SUPPORT_THREAD_UTILS_H
#define SRSLOG_DETAIL_SUPPORT_THREAD_UTILS_H
#include <pthread.h>
namespace srslog {
namespace detail {
//:TODO: these are temp helpers that will be replaced by std utils.
/// Abstraction of a pthread mutex.
class mutex
{
public:
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
mutex() { ::pthread_mutex_init(&m, nullptr); }
~mutex() { ::pthread_mutex_destroy(&m); }
/// Mutex lock.
void lock() { ::pthread_mutex_lock(&m); }
/// Mutex unlock.
void unlock() { ::pthread_mutex_unlock(&m); }
/// Mutex try lock. Returns true if the lock was obtained, false otherwise.
bool try_lock() { return (::pthread_mutex_trylock(&m) == 0); }
/// Accessor to the raw mutex structure.
pthread_mutex_t* raw() { return &m; }
const pthread_mutex_t* raw() const { return &m; }
private:
pthread_mutex_t m;
};
/// RAII style object for automatically locking and unlocking a mutex.
class scoped_lock
{
mutex& m;
public:
explicit scoped_lock(mutex& m) : m(m) { m.lock(); }
scoped_lock(const scoped_lock&) = delete;
scoped_lock& operator=(const scoped_lock&) = delete;
~scoped_lock() { m.unlock(); }
};
/// This class allows accessing and modifying data atomically.
template <typename T>
class shared_variable
{
T value;
mutable mutex m;
public:
shared_variable(const T& value) : value(value) {}
shared_variable(const shared_variable&) = delete;
shared_variable& operator=(const shared_variable&) = delete;
/// Set this shared variable to a new value guarded by the associated mutex.
shared_variable<T>& operator=(const T& other)
{
scoped_lock lock(m);
value = other;
return *this;
}
/// Get the value of this shared variable guarded by the associated mutex.
operator T() const
{
scoped_lock lock(m);
return value;
}
};
/// Abstraction of a pthread condition variable.
class condition_variable
{
public:
condition_variable() { ::pthread_cond_init(&cond_var, nullptr); }
condition_variable(const condition_variable&) = delete;
condition_variable& operator=(const condition_variable&) = delete;
~condition_variable() { ::pthread_cond_destroy(&cond_var); }
/// Internal mutex control.
void lock() { m.lock(); }
void unlock() { m.unlock(); }
/// Blocks the calling thread on this condition variable.
void wait() { ::pthread_cond_wait(&cond_var, m.raw()); }
/// Unblocks at least one waiting thread that is blocked on this condition
/// variable.
void signal() { ::pthread_cond_signal(&cond_var); }
/// Unblocks all waiting threads being blocked on this condition variable.
void broadcast() { ::pthread_cond_broadcast(&cond_var); }
/// Blocks the calling thread on this condition variable up to the specified
/// timeout. Returns true on timeout expiration, otherwise false.
bool wait(timespec ts)
{
return (::pthread_cond_timedwait(&cond_var, m.raw(), &ts) == ETIMEDOUT);
}
/// Builds an absolute time timespec structure adding the specified time out
/// in ms.
static timespec build_timeout(unsigned timeout_ms)
{
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += timeout_ms / 1000;
ts.tv_nsec += (timeout_ms % 1000) * 1000000;
if (ts.tv_nsec >= 1000000000) {
++ts.tv_sec;
ts.tv_nsec -= 1000000000;
}
return ts;
}
private:
mutable mutex m;
pthread_cond_t cond_var;
};
/// RAII style object for automatically locking and unlocking a condition
/// variable.
class cond_var_scoped_lock
{
condition_variable& cond_var;
public:
explicit cond_var_scoped_lock(condition_variable& cond_var) :
cond_var(cond_var)
{
cond_var.lock();
}
cond_var_scoped_lock(const cond_var_scoped_lock&) = delete;
cond_var_scoped_lock& operator=(const cond_var_scoped_lock&) = delete;
~cond_var_scoped_lock() { cond_var.unlock(); }
};
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_SUPPORT_THREAD_UTILS_H

View File

@ -0,0 +1,145 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_DETAIL_SUPPORT_WORK_QUEUE_H
#define SRSLOG_DETAIL_SUPPORT_WORK_QUEUE_H
#include "srslte/srslog/detail/support/thread_utils.h"
#include <queue>
#ifndef SRSLOG_QUEUE_CAPACITY
#define SRSLOG_QUEUE_CAPACITY 2048
#endif
namespace srslog {
namespace detail {
//:TODO: this is a temp work queue.
/// Thread safe generic data type work queue.
template <typename T, size_t capacity = SRSLOG_QUEUE_CAPACITY>
class work_queue
{
std::queue<T> queue;
mutable condition_variable cond_var;
static constexpr size_t threshold = capacity * 0.98;
public:
work_queue() = default;
work_queue(const work_queue&) = delete;
work_queue& operator=(const work_queue&) = delete;
/// Inserts a new element into the back of the queue.
void push(const T& value)
{
cond_var.lock();
// Discard the new element if we reach the maximum capacity.
if (queue.size() > capacity) {
cond_var.unlock();
return;
}
queue.push(value);
cond_var.signal();
cond_var.unlock();
}
/// Inserts a new element into the back of the queue.
void push(T&& value)
{
cond_var.lock();
// Discard the new element if we reach the maximum capacity.
if (queue.size() > capacity) {
cond_var.unlock();
return;
}
queue.push(std::move(value));
cond_var.signal();
cond_var.unlock();
}
/// Extracts the top most element from the queue.
/// NOTE: This method blocks while the queue is empty.
T pop()
{
cond_var.lock();
while (queue.empty()) {
cond_var.wait();
}
T elem = std::move(queue.front());
queue.pop();
cond_var.unlock();
return elem;
}
/// Extracts the top most element from the queue.
/// NOTE: This method blocks while the queue is empty or or until the
/// programmed timeout expires. Returns a pair with a bool indicating if the
/// pop has been successful.
std::pair<bool, T> timed_pop(unsigned timeout_ms)
{
// Build an absolute time reference for the expiration time.
timespec ts = condition_variable::build_timeout(timeout_ms);
cond_var.lock();
bool timedout = false;
while (queue.empty() && !timedout) {
timedout = cond_var.wait(ts);
}
// Did we wake up on timeout?
if (timedout && queue.empty()) {
cond_var.unlock();
return {false, T{}};
}
// Here we have been woken up normally.
T Item = std::move(queue.front());
queue.pop();
cond_var.unlock();
return {true, std::move(Item)};
}
/// Capacity of the queue.
size_t get_capacity() const { return capacity; }
/// Returns true when the queue is almost full, otherwise returns false.
bool is_almost_full() const
{
cond_var_scoped_lock lock(cond_var);
return queue.size() > threshold;
}
};
} // namespace detail
} // namespace srslog
#endif // SRSLOG_DETAIL_SUPPORT_WORK_QUEUE_H

View File

@ -0,0 +1,87 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_LOG_CHANNEL_H
#define SRSLOG_LOG_CHANNEL_H
#include "srslte/srslog/detail/log_backend.h"
#include "srslte/srslog/detail/support/thread_utils.h"
namespace srslog {
/// A log channel is the entity used for logging messages.
///
/// It can deliver a log entry to one or more different sinks, for example a
/// file or a console.
/// Generated log entries by the application will be ignored by the channel when
/// set to disabled.
/// NOTE: Thread safe class.
class log_channel
{
public:
log_channel(std::string id, sink& s, detail::log_backend& backend) :
log_id(std::move(id)),
log_sink(s),
backend(backend),
is_enabled(true)
{}
log_channel(const log_channel& other) = delete;
log_channel& operator=(const log_channel& other) = delete;
/// Controls when the channel accepts incoming log entries.
void set_enabled(bool enabled) { is_enabled = enabled; }
/// Returns true if the channel is accepting incoming log entries, otherwise
/// false.
bool enabled() const { return is_enabled; }
/// Returns the id string of the channel.
const std::string& id() const { return log_id; }
/// Builds the provided log entry and passes it to the backend. When the
/// channel is disabled the log entry will be discarded.
template <typename... Args>
void operator()(const std::string& fmtstr, Args&&... args)
{
if (!enabled()) {
return;
}
// Populate the store with all incoming arguments.
fmt::dynamic_format_arg_store<fmt::printf_context> store;
(void)std::initializer_list<int>{(store.push_back(args), 0)...};
// Send the log entry to the backend.
detail::log_entry entry = {&log_sink, fmtstr, std::move(store)};
backend.push(std::move(entry));
}
private:
const std::string log_id;
sink& log_sink;
detail::log_backend& backend;
detail::shared_variable<bool> is_enabled;
};
} // namespace srslog
#endif // SRSLOG_LOG_CHANNEL_H

View File

@ -0,0 +1,131 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_LOGGER_H
#define SRSLOG_LOGGER_H
#include "srslte/srslog/log_channel.h"
namespace srslog {
namespace detail {
/// The logger_impl template class contains the common functionality to loggers.
/// Its main responsibility is to control the logging level by individually
/// enabling or disabling the logging channels that form the logger.
template <typename T, typename Enum>
class logger_impl : public T
{
static_assert(std::is_enum<Enum>::value, "Expected enum type");
using enum_base_type = typename std::underlying_type<Enum>::type;
static constexpr unsigned size = static_cast<enum_base_type>(Enum::LAST);
public:
template <typename... Args>
explicit logger_impl(std::string id, Args&&... args) :
T{std::forward<Args>(args)...},
logger_id(std::move(id)),
channels{&args...}
{
static_assert(
sizeof...(args) == size,
"Number of levels in enum does not match number of log channels");
}
logger_impl(const logger_impl& other) = delete;
logger_impl& operator=(const logger_impl& other) = delete;
/// Returns the id string of the logger.
const std::string& id() const { return logger_id; }
/// Change the logging level.
void set_level(Enum lvl)
{
for (unsigned i = 0, e = channels.size(); i != e; ++i) {
channels[i]->set_enabled(static_cast<Enum>(i) <= lvl);
}
}
private:
/// Comparison operator for enum types, used by the set_level method.
friend bool operator<=(Enum lhs, Enum rhs)
{
return static_cast<enum_base_type>(lhs) <= static_cast<enum_base_type>(rhs);
}
private:
const std::string logger_id;
const std::array<log_channel*, size> channels;
};
/// Type trait to detect if T is a logger.
template <typename T>
struct is_logger : std::false_type {};
template <typename T, typename Enum>
struct is_logger<logger_impl<T, Enum>> : std::true_type {};
} // namespace detail
/// The build_logger_type type alias allows developers to define new logger
/// types in an application.
///
/// To create a new logger type simply follow these steps:
/// 1) Define an enum class where each element will represent a logging level.
/// Order the elements from highest to lowest logging level. The last
/// element should be called LAST as it is a sentinel value.
/// 2) Define a struct composed by only log_channel references. Declare the
/// members in the same order as done in the enum.
/// 3) Define the new logger type by using the build_logger_type alias. Pass
/// the previous defined types as template parameters.
///
/// Example to declare a logger with three logging levels: error, warning and
/// info, being error the highest logging level and info the lowest:
/// 1) Define the logging level enum:
/// enum class three_level_logger_levels { error, warning, info, LAST };
/// 2) Define the struct of three channels (same order as in the enum):
/// struct three_level_logger {
/// log_channel &error;
/// log_channel &warning;
/// log_channel &info;
/// };
/// 3) Define the new logger type:
/// using my_new_logger =
/// build_logger_type<three_level_logger, three_level_logger_levels>;
template <typename T, typename Enum>
using build_logger_type = detail::logger_impl<T, Enum>;
///
/// Common logger types.
///
/// Basic logger with three levels.
enum class basic_levels { error, warning, info, LAST };
struct basic_logger_channels {
log_channel& error;
log_channel& warning;
log_channel& info;
};
using basic_logger = build_logger_type<basic_logger_channels, basic_levels>;
} // namespace srslog
#endif // SRSLOG_LOGGER_H

View File

@ -0,0 +1,34 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_SHARED_TYPES_H
#define SRSLOG_SHARED_TYPES_H
#include <functional>
namespace srslog {
/// Generic error handler callback.
using error_handler = std::function<void(const std::string&)>;
} // namespace srslog
#endif // SRSLOG_SHARED_TYPES_H

View File

@ -0,0 +1,46 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_SINK_H
#define SRSLOG_SINK_H
#include "srslte/srslog/detail/support/error_string.h"
#include "srslte/srslog/detail/support/memory_buffer.h"
namespace srslog {
/// This interface provides the way to write incoming memory buffers to any kind
/// of backing store.
class sink
{
public:
virtual ~sink() = default;
/// Writes the provided memory buffer into the sink.
virtual detail::error_string write(detail::memory_buffer buffer) = 0;
/// Flushes any buffered contents to the backing store.
virtual detail::error_string flush() = 0;
};
} // namespace srslog
#endif // SRSLOG_SINK_H

View File

@ -0,0 +1,133 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_SRSLOG_H
#define SRSLOG_SRSLOG_H
#include "srslte/srslog/detail/support/any.h"
#include "srslte/srslog/logger.h"
#include "srslte/srslog/shared_types.h"
namespace srslog {
///
/// Log channel management functions.
///
/// Creates a new log channel instance with the specified id string and sink,
/// then registers it in the log channel repository so that it can be later
/// retrieved in other parts of the application. Returns a pointer to the
/// newly created channel, otherwise when a channel is already registered with
/// the same id it returns nullptr.
log_channel* create_log_channel(const std::string& id, sink& s);
/// Finds a log channel with the specified id string in the repository. On
/// success returns a pointer to the requested log channel, otherwise nullptr.
log_channel* find_log_channel(const std::string& id);
///
/// Logger management functions.
///
namespace detail {
/// Internal helper functions.
detail::any* create_logger(const std::string& id, detail::any&& logger);
detail::any* find_logger(const std::string& id);
} // namespace detail
/// Creates a new logger instance with the specified id string, type and channel
/// references, registering it into the logger repository so that it can be
/// later retrieved in other parts of the application. Returns a pointer to the
/// newly created logger, otherwise when a logger is already registered with the
/// same id it returns nullptr.
/// NOTE: T should be a type that is a logger.
template <typename T, typename... Args>
inline T* create_logger(const std::string& id, Args&&... args)
{
static_assert(detail::is_logger<T>::value, "T should be a logger type");
auto logger = detail::make_any<T>(id, std::forward<Args>(args)...);
detail::any* p = detail::create_logger(id, std::move(logger));
return detail::any_cast<T>(p);
}
/// Finds a logger with the specified id string and type in the repository. On
/// success returns a pointer to the requested logger, otherwise nullptr.
/// NOTE: T should be a type that is a logger.
template <typename T>
inline T* find_logger(const std::string& id)
{
static_assert(detail::is_logger<T>::value, "T should be a logger type");
detail::any* p = detail::find_logger(id);
return detail::any_cast<T>(p);
}
///
/// Sink management functions.
///
/// Finds a sink with the specified id string in the repository. On
/// success returns a pointer to the requested sink, otherwise nullptr.
sink* find_sink(const std::string& id);
/// Creates a new sink that writes into the stdout stream and registers it into
/// a sink repository so that it can be later retrieved in other parts of the
/// application. Different stdout sinks can be created by providing different
/// names. Returns a pointer to the newly created sink or nullptr if a sink with
/// the same name was already registered.
sink* create_stdout_sink(const std::string& name = "stdout");
/// Creates a new sink that writes into the stderr stream and registers it into
/// a sink repository so that it can be later retrieved in other parts of the
/// application. Different stderr sinks can be created by providing different
/// names. Returns a pointer to the newly created sink or nullptr if a sink with
/// the same name was already registered.
sink* create_stderr_sink(const std::string& name = "stderr");
/// Creates a new sink that writes into the a file in the specified path and
/// registers it into a sink repository so that it can be later retrieved in
/// other parts of the application. Returns a pointer to the newly created sink
/// or nullptr if a sink with the same path was already registered.
/// Specifying a max_size value different to zero will make the sink create a
/// new file each time the current file exceeds this value. The units of
/// max_size are bytes.
sink* create_file_sink(const std::string& path, size_t max_size = 0);
///
/// Framework configuration and control functions.
///
/// This function initializes the logging framework. It must be called before
/// any log entry is generated.
/// NOTE: Calling this function more than once has no side effects.
void init();
/// Installs the specified error handler to receive any error messages generated
/// by the framework.
/// NOTE: This function should be called before init().
void set_error_handler(error_handler handler);
} // namespace srslog
#endif // SRSLOG_SRSLOG_H

View File

@ -23,4 +23,5 @@ add_subdirectory(common)
add_subdirectory(mac)
add_subdirectory(phy)
add_subdirectory(radio)
add_subdirectory(srslog)
add_subdirectory(upper)

View File

@ -28,6 +28,7 @@ set(SOURCES arch_select.cc
log_filter.cc
logmap.cc
logger_file.cc
logger_srslog_wrapper.cc
mac_pcap.cc
nas_pcap.cc
network_utils.cc

View File

@ -0,0 +1,30 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/common/logger_srslog_wrapper.h"
#include "srslte/srslog/log_channel.h"
using namespace srslte;
void srslog_wrapper::log(unique_log_str_t msg)
{
chan("%s", msg->str());
}

View File

@ -0,0 +1,30 @@
#
# Copyright 2013-2020 Software Radio Systems Limited
#
# This file is part of srsLTE
#
# srsLTE is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# srsLTE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# A copy of the GNU Affero General Public License can be found in
# the LICENSE file in the top-level directory of this distribution
# and at http://www.gnu.org/licenses/.
#
set(SOURCES
srslog.cpp
backend_worker.cpp)
add_subdirectory(bundled/fmt)
find_package(Threads REQUIRED)
add_library(srslog STATIC ${SOURCES})
target_link_libraries(srslog fmt "${CMAKE_THREAD_LIBS_INIT}")

View File

@ -0,0 +1,107 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "backend_worker.h"
#include "srslte/srslog/sink.h"
#include <cassert>
using namespace srslog;
void backend_worker::stop()
{
// Signal the worker thread to stop.
running_flag = false;
if (worker_thread.joinable()) {
worker_thread.join();
}
}
void backend_worker::create_worker()
{
assert(!running_flag && "Only one worker thread should be created");
std::thread t([this]() {
running_flag = true;
do_work();
});
worker_thread = std::move(t);
// Block the caller thread until we are signaled that the worker is running.
while (!running_flag) {
std::this_thread::sleep_for(std::chrono::microseconds(10));
}
}
void backend_worker::start()
{
// Ensure we only create the worker thread once.
std::call_once(start_once_flag, [this]() { create_worker(); });
}
void backend_worker::do_work()
{
assert(running_flag && "Thread entry function called without running thread");
while (running_flag) {
auto item = queue.timed_pop(sleep_period_ms);
// Spin again when the timeout expires.
if (!item.first) {
continue;
}
report_queue_on_full();
process_log_entry(std::move(item.second));
}
// When we reach here, the thread is about to terminate, last chance to
// process the last log entries.
process_outstanding_entries();
}
void backend_worker::process_log_entry(detail::log_entry&& entry)
{
std::string result = fmt::vsprintf(entry.fmtstring, std::move(entry.store));
detail::memory_buffer buffer(result);
if (auto err_str = entry.s->write(buffer)) {
err_handler(err_str.get_error());
}
}
void backend_worker::process_outstanding_entries()
{
assert(!running_flag &&
"Cannot process outstanding entries while thread is running");
while (true) {
auto item = queue.timed_pop(1);
// Check if the queue is empty.
if (!item.first) {
break;
}
process_log_entry(std::move(item.second));
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_BACKEND_WORKER_H
#define SRSLOG_BACKEND_WORKER_H
#include "srslte/srslog/detail/log_entry.h"
#include "srslte/srslog/detail/support/work_queue.h"
#include "srslte/srslog/shared_types.h"
#include <mutex>
#include <thread>
namespace srslog {
/// The backend worker runs in a secondary thread a routine that endlessly pops
/// log entries from a work queue and dispatches them to the selected sinks.
class backend_worker
{
/// This period defines the maximum time the worker will sleep while waiting
/// for new entries. This is required to check the termination variable
/// periodically.
static constexpr unsigned sleep_period_ms = 500;
public:
explicit backend_worker(detail::work_queue<detail::log_entry>& queue) :
queue(queue),
running_flag(false)
{}
backend_worker(const backend_worker&) = delete;
backend_worker& operator=(const backend_worker&) = delete;
~backend_worker() { stop(); }
/// Starts the backend worker thread. After returning from this function the
/// secondary thread is ensured to be running. Calling this function more than
/// once has no effect.
void start();
/// Stops the backend worker thread if it is running, otherwise the call has
/// no effect. After returning from this function the secondary thread is
/// ensured to have terminated. Calling this function more than once has no
/// effect.
void stop();
/// Returns true if the worker thread is currently running, otherwise
/// returns false.
bool is_running() const { return running_flag; }
/// Uses the specified error handler to receive error notifications. Calls to
/// this method when the worker is running will get ignored.
void set_error_handler(error_handler new_err_handler)
{
// Ignore new handlers when the worker is running.
if (is_running()) {
return;
}
// Install a dummy callback if the input one is empty. This avoids having to
// check for null at each call site.
if (!new_err_handler) {
err_handler = [](const std::string&) {};
return;
}
err_handler = std::move(new_err_handler);
}
private:
/// Creates the worker thread.
/// NOTE: This function should be only called once.
void create_worker();
/// Entry function used by the secondary thread.
void do_work();
/// Processes the log entry.
void process_log_entry(detail::log_entry&& entry);
/// Processes outstanding entries in the queue until it gets empty.
void process_outstanding_entries();
/// Checks the current size of the queue reporting an error message if it is
/// about to reach its maximum capacity.
void report_queue_on_full() const
{
if (queue.is_almost_full()) {
err_handler(
fmt::format("The backend queue size is about to reach its maximum "
"capacity of {} elements, new log entries will get "
"discarded.\nConsider increasing the queue capacity.",
queue.get_capacity()));
}
}
private:
detail::work_queue<detail::log_entry>& queue;
detail::shared_variable<bool> running_flag;
error_handler err_handler = [](const std::string& error) {
fmt::print(stderr, "srsLog error - {}\n", error);
};
std::once_flag start_once_flag;
std::thread worker_thread;
};
} // namespace srslog
#endif // SRSLOG_BACKEND_WORKER_H

View File

@ -0,0 +1,26 @@
#
# Copyright 2013-2020 Software Radio Systems Limited
#
# This file is part of srsLTE
#
# srsLTE is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# srsLTE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# A copy of the GNU Affero General Public License can be found in
# the LICENSE file in the top-level directory of this distribution
# and at http://www.gnu.org/licenses/.
#
set(SOURCES
format.cc
os.cc)
add_library(fmt STATIC ${SOURCES})
target_include_directories(fmt PUBLIC ../../../../include/srslte/srslog/bundled)

View File

@ -0,0 +1,176 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
struct sprintf_specs {
int precision;
char type;
bool alt : 1;
template <typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
constexpr bool has_precision() const { return precision >= 0; }
};
// This is deprecated and is kept only to preserve ABI compatibility.
template <typename Double>
char* sprintf_format(Double value, internal::buffer<char>& buf,
sprintf_specs specs) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string.
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type) *format_ptr++ = '#';
if (specs.precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F') {
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char* start = nullptr;
char* decimal_point_pos = nullptr;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result =
format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-') ++p;
if (specs.type != 'a' && specs.type != 'A') {
while (p < end && *p >= '0' && *p <= '9') ++p;
if (p < end && *p != 'e' && *p != 'E') {
decimal_point_pos = p;
if (!specs.type) {
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0') ++p;
while (p != end && *p >= '1' && *p <= '9') ++p;
char* where = p;
while (p != end && *p == '0') ++p;
if (p == end || *p < '0' || *p > '9') {
if (p != end) std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
}
} // namespace internal
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) =
internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context>& args);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE

View File

@ -0,0 +1,316 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
// Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/os.h"
#include <climits>
#if FMT_USE_FCNTL
# include <sys/stat.h>
# include <sys/types.h>
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# include <windows.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
# endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef _WIN32
# include <windows.h>
#endif
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
using RWResult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
// Return type of read and write functions.
using RWResult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
int internal::utf16_to_utf8::convert(wstring_view s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
// WideCharToMultiByte does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return 0;
}
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
length, nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_[length] = 0;
return 0;
}
void windows_error::init(int err_code, string_view format_str,
format_args args) {
error_code_ = err_code;
memory_buffer buffer;
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer));
}
void internal::format_windows_error(internal::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
wmemory_buffer buf;
buf.resize(inline_buffer_size);
for (;;) {
wchar_t* system_message = &buf[0];
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
static_cast<uint32_t>(buf.size()), nullptr);
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(utf8_message);
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2);
}
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
void report_windows_error(int error_code,
fmt::string_view message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message);
}
#endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file");
}
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
}
void buffered_file::close() {
if (!file_) return;
int result = FMT_SYSTEM(fclose(file_));
file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
# if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
# else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
# endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
}
file::~file() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
report_system_error(errno, "cannot close file");
}
void file::close() {
if (fd_ == -1) return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
long long file::size() const {
# ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
# else
using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
return file_stat.st_size;
# endif
}
std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
file file::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
return file(new_fd);
}
void file::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
fd_, fd));
}
}
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = error_code(errno);
}
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
# ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
# else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
# endif
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
buffered_file bf(f);
fd_ = -1;
return bf;
}
long getpagesize() {
# ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
# else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
return size;
# endif
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

View File

@ -0,0 +1,67 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_LOG_BACKEND_IMPL_H
#define SRSLOG_LOG_BACKEND_IMPL_H
#include "backend_worker.h"
#include "srslte/srslog/detail/log_backend.h"
namespace srslog {
/// This class implements the log backend interface. It internally manages a
/// worker thread to process incoming log entries.
/// NOTE: Thread safe class.
class log_backend_impl : public detail::log_backend
{
public:
log_backend_impl() = default;
log_backend_impl(const log_backend_impl& other) = delete;
log_backend_impl& operator=(const log_backend_impl& other) = delete;
void start() override { worker.start(); }
void push(detail::log_entry&& entry) override
{
queue.push(std::move(entry));
}
/// Installs the specified error handler into the backend worker.
void set_error_handler(error_handler err_handler)
{
worker.set_error_handler(std::move(err_handler));
}
/// Returns true if the backend has been started, otherwise returns false.
bool is_started() const { return worker.is_running(); }
/// Stops the backend worker thread.
void stop() { worker.stop(); }
private:
detail::work_queue<detail::log_entry> queue;
backend_worker worker{queue};
};
} // namespace srslog
#endif // SRSLOG_LOG_BACKEND_IMPL_H

View File

@ -0,0 +1,70 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_OBJECT_REPOSITORY_H
#define SRSLOG_OBJECT_REPOSITORY_H
#include "srslte/srslog/detail/support/thread_utils.h"
#include <unordered_map>
namespace srslog {
/// This template class implements a very basic object repository with arbitrary
/// key and value types. It allows registering new objects of type V indexed by
/// key K, no element removal supported.
/// NOTE: Thread safe class.
template <typename K, typename V>
class object_repository
{
mutable detail::mutex m;
std::unordered_map<K, V> repo;
public:
/// Inserts a new entry into the repository. A pointer to the new
/// entry is returned or nullptr when the key already exists.
V* insert(const K& key, V&& value)
{
detail::scoped_lock lock(m);
const auto& insertion = repo.emplace(key, std::move(value));
if (!insertion.second)
return nullptr;
return &insertion.first->second;
}
/// Finds a value with the specified key in the repository. Returns a
/// pointer to the value, otherwise nullptr if not found.
V* find(const K& key)
{
detail::scoped_lock lock(m);
auto it = repo.find(key);
return (it != repo.end()) ? &it->second : nullptr;
}
const V* find(const K& key) const
{
detail::scoped_lock lock(m);
const auto it = repo.find(key);
return (it != repo.cend()) ? &it->second : nullptr;
}
};
} // namespace srslog
#endif // SRSLOG_OBJECT_REPOSITORY_H

View File

@ -0,0 +1,107 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_FILE_SINK_H
#define SRSLOG_FILE_SINK_H
#include "file_utils.h"
#include "srslte/srslog/sink.h"
#include <cassert>
namespace srslog {
/// This sink implementation writes to files. Includes the optional feature of
/// file rotation: a new file is created when file size exceeds an established
/// threshold.
class file_sink : public sink
{
public:
file_sink(std::string name, size_t max_size) :
base_filename(std::move(name)),
max_size((max_size == 0) ? 0 : std::max<size_t>(max_size, 4 * 1024))
{}
file_sink(const file_sink& other) = delete;
file_sink& operator=(const file_sink& other) = delete;
~file_sink() override { handler.close(); }
detail::error_string write(detail::memory_buffer buffer) override
{
// Create a new file the first time we hit this method.
if (is_first_write()) {
assert(!handler && "No handler should be created yet");
if (auto err_str = create_file()) {
return err_str;
}
}
// Do not bother doing any work when the file was closed on a previous
// error.
if (!handler) {
return {};
}
if (auto err_str = handle_rotation(buffer.size())) {
return err_str;
}
return handler.write(buffer);
}
detail::error_string flush() override { return handler.flush(); }
private:
/// Returns true when the sink has never written data to a file, otherwise
/// returns false.
bool is_first_write() const { return file_index == 0; }
/// Creates a new file and increments the file index counter.
detail::error_string create_file()
{
return handler.create(
file_utils::build_filename_with_index(base_filename, file_index++));
}
/// Handles the file rotation feature when it is activated.
/// NOTE: The file handler must be valid.
detail::error_string handle_rotation(size_t size)
{
assert(handler && "Expected a valid file handle");
current_size += size;
if (max_size && current_size >= max_size) {
current_size = size;
return create_file();
}
return {};
}
private:
const size_t max_size;
const std::string base_filename;
file_utils::file handler;
size_t current_size = 0;
uint32_t file_index = 0;
};
} // namespace srslog
#endif // SRSLOG_FILE_SINK_H

View File

@ -0,0 +1,160 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_FILE_UTILS_H
#define SRSLOG_FILE_UTILS_H
#include "srslte/srslog/bundled/fmt/format.h"
#include "srslte/srslog/detail/support/error_string.h"
#include "srslte/srslog/detail/support/memory_buffer.h"
namespace srslog {
namespace file_utils {
/// Helper function that formats errnos nicely.
inline std::string format_error(const std::string& error, int error_code)
{
fmt::memory_buffer result;
fmt::format_system_error(result, error_code, error);
return fmt::to_string(result);
}
/// Splits the specified path into a filename and its extension (if present).
inline std::pair<std::string, std::string>
split_filename_extension(const std::string& filename)
{
// Search for the last dot.
auto dot_pos = filename.find_last_of('.');
// Check for the following corner cases (returns {filename, ""}):
// a) No dot found: my_file
// b) Dot found at the beginning: .my_file
// c) Dot found at the end: my_file.
if (dot_pos == std::string::npos || dot_pos == 0 ||
dot_pos == filename.size() - 1) {
return {filename, ""};
}
// Handle directories that contain dots, search for the last separator
// character.
auto separator_pos = filename.find_last_of('/');
// Check for the following corner cases (returns {filename, ""}):
// a) /my_folder.1/my_file
// b) /my_folder.1/.my_file
if (separator_pos != std::string::npos && separator_pos >= dot_pos - 1) {
return {filename, ""};
}
return {filename.substr(0, dot_pos), filename.substr(dot_pos)};
}
/// Builds a file name formatting the input base name and file index.
inline std::string build_filename_with_index(const std::string& basename,
size_t index)
{
if (index == 0) {
return basename;
}
auto result = split_filename_extension(basename);
return fmt::format("{}.{}{}", result.first, index, result.second);
}
/// This class provides basic file operations and disables itself when it
/// encounters an error.
class file
{
std::string path;
std::FILE* handle = nullptr;
public:
~file() { close(); }
explicit operator bool() const { return handle; }
/// Returns the handle of the underlying file.
std::FILE* get_handle() { return handle; }
/// Returns the path of the file.
const std::string& get_path() const { return path; }
/// Creates a new file in the specified path by previously closing any opened
/// file.
detail::error_string create(const std::string& new_path)
{
close();
if ((handle = std::fopen(new_path.c_str(), "wb"))) {
path = new_path;
return {};
}
return format_error(fmt::format("Unable to create log file \"{}\"", path),
errno);
}
/// Writes the provided memory buffer into an open file, otherwise does
/// nothing.
detail::error_string write(detail::memory_buffer buffer)
{
if (handle &&
std::fwrite(buffer.data(), sizeof(char), buffer.size(), handle) !=
buffer.size()) {
close();
return format_error(fmt::format("Unable to write log file \"{}\"", path),
errno);
}
return {};
}
/// Flushes the contents of an open file, otherwise does nothing.
detail::error_string flush()
{
if (handle && ::fflush(handle) == EOF) {
close();
return format_error(
fmt::format("Error encountered while flushing log file \"{}\"", path),
errno);
}
return {};
}
/// Closes an open file, otherwise does nothing.
void close()
{
if (handle) {
::fclose(handle);
handle = nullptr;
path.clear();
}
}
};
} // namespace file_utils
} // namespace srslog
#endif // SRSLOG_FILE_UTILS_H

View File

@ -0,0 +1,64 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_STREAM_SINK_H
#define SRSLOG_STREAM_SINK_H
#include "srslte/srslog/sink.h"
#include <cassert>
#include <cstdio>
namespace srslog {
/// Standard stream types.
enum class sink_stream_type { stdout, stderr };
/// This sink implementation writes to either stdout or stderr streams.
class stream_sink : public sink
{
public:
explicit stream_sink(sink_stream_type s) :
handle((s == sink_stream_type::stdout) ? stdout : stderr)
{}
stream_sink(const stream_sink& other) = delete;
stream_sink& operator=(const stream_sink& other) = delete;
detail::error_string write(detail::memory_buffer buffer) override
{
assert(handle && "Invalid stream handle");
std::fwrite(buffer.data(), sizeof(char), buffer.size(), handle);
return {};
}
detail::error_string flush() override
{
std::fflush(handle);
return {};
}
private:
std::FILE* handle;
};
} // namespace srslog
#endif // SRSLOG_STREAM_SINK_H

97
lib/src/srslog/srslog.cpp Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/srslog/srslog.h"
#include "sinks/file_sink.h"
#include "sinks/stream_sink.h"
#include "srslog_instance.h"
using namespace srslog;
log_channel* srslog::create_log_channel(const std::string& id, sink& s)
{
srslog_instance& instance = srslog_instance::get();
auto channel = std::unique_ptr<log_channel>(
new log_channel(id, s, instance.get_backend()));
auto p = instance.get_channel_repo().insert(id, std::move(channel));
return (p) ? p->get() : nullptr;
}
log_channel* srslog::find_log_channel(const std::string& id)
{
auto p = srslog_instance::get().get_channel_repo().find(id);
return (p) ? p->get() : nullptr;
}
sink* srslog::find_sink(const std::string& id)
{
auto p = srslog_instance::get().get_sink_repo().find(id);
return (p) ? p->get() : nullptr;
}
/// Helper for creating and registering sinks.
template <typename T, typename... Args>
static sink* create_sink(const std::string& id, Args&&... args)
{
auto new_sink = std::unique_ptr<sink>(new T(std::forward<Args>(args)...));
auto p =
srslog_instance::get().get_sink_repo().insert(id, std::move(new_sink));
return (p) ? p->get() : nullptr;
}
sink* srslog::create_stdout_sink(const std::string& name)
{
return create_sink<stream_sink>(name, sink_stream_type::stdout);
}
sink* srslog::create_stderr_sink(const std::string& name)
{
return create_sink<stream_sink>(name, sink_stream_type::stderr);
}
sink* srslog::create_file_sink(const std::string& path, size_t max_size)
{
return create_sink<file_sink>(path, path, max_size);
}
void srslog::init()
{
srslog_instance::get().get_backend().start();
}
detail::any* srslog::detail::create_logger(const std::string& id,
detail::any&& logger)
{
return srslog_instance::get().get_logger_repo().insert(id, std::move(logger));
}
detail::any* srslog::detail::find_logger(const std::string& id)
{
return srslog_instance::get().get_logger_repo().find(id);
}
void srslog::set_error_handler(error_handler handler)
{
srslog_instance::get().set_error_handler(std::move(handler));
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_SRSLOG_INSTANCE_H
#define SRSLOG_SRSLOG_INSTANCE_H
#include "log_backend_impl.h"
#include "object_repository.h"
#include "srslte/srslog/detail/support/any.h"
#include "srslte/srslog/log_channel.h"
#include "srslte/srslog/sink.h"
namespace srslog {
/// Singleton of the framework containing all the required classes.
class srslog_instance
{
srslog_instance() = default;
public:
srslog_instance(const srslog_instance& other) = delete;
srslog_instance& operator=(const srslog_instance& other) = delete;
/// Access function to the singleton instance.
static srslog_instance& get()
{
static srslog_instance instance;
return instance;
}
/// Logger repository accessor.
using logger_repo_type = object_repository<std::string, detail::any>;
logger_repo_type& get_logger_repo() { return logger_repo; }
const logger_repo_type& get_logger_repo() const { return logger_repo; }
/// Log channel repository accessor.
using channel_repo_type =
object_repository<std::string, std::unique_ptr<log_channel>>;
channel_repo_type& get_channel_repo() { return channel_repo; }
const channel_repo_type& get_channel_repo() const { return channel_repo; }
/// Sink repository accessor.
using sink_repo_type = object_repository<std::string, std::unique_ptr<sink>>;
sink_repo_type& get_sink_repo() { return sink_repo; }
const sink_repo_type& get_sink_repo() const { return sink_repo; }
/// Backend accessor.
detail::log_backend& get_backend() { return backend; }
const detail::log_backend& get_backend() const { return backend; }
/// Installs the specified error handler into the backend.
void set_error_handler(error_handler callback)
{
backend.set_error_handler(std::move(callback));
}
private:
/// NOTE: The order of declaration of each member is important here for proper
/// destruction.
sink_repo_type sink_repo;
log_backend_impl backend;
channel_repo_type channel_repo;
logger_repo_type logger_repo;
};
} // namespace srslog
#endif // SRSLOG_SRSLOG_INSTANCE_H

View File

@ -22,4 +22,5 @@ add_subdirectory(asn1)
add_subdirectory(common)
add_subdirectory(mac)
add_subdirectory(phy)
add_subdirectory(srslog)
add_subdirectory(upper)

View File

@ -23,9 +23,9 @@ find_package(SCTP REQUIRED)
#######################################################################
# COMMON TESTS
#######################################################################
add_executable(logger_test logger_test.cc)
target_link_libraries(logger_test srslte_phy srslte_common srslte_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
add_test(logger_test logger_test)
add_executable(logger_file_test logger_file_test.cc)
target_link_libraries(logger_file_test srslte_phy srslte_common srslte_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})
add_test(logger_file_test logger_file_test)
add_executable(byte_buffer_queue_test byte_buffer_queue_test.cc)
target_link_libraries(byte_buffer_queue_test srslte_phy srslte_common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES})

View File

@ -0,0 +1,50 @@
#
# Copyright 2013-2020 Software Radio Systems Limited
#
# This file is part of srsLTE
#
# srsLTE is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# srsLTE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# A copy of the GNU Affero General Public License can be found in
# the LICENSE file in the top-level directory of this distribution
# and at http://www.gnu.org/licenses/.
#
add_executable(srslog_test srslog_test.cpp)
target_link_libraries(srslog_test srslog)
add_test(srslog_test srslog_test)
add_executable(log_channel_test log_channel_test.cpp)
target_link_libraries(log_channel_test srslog)
add_test(log_channel_test log_channel_test)
add_executable(log_backend_test log_backend_test.cpp)
target_include_directories(log_backend_test PUBLIC ../../)
target_link_libraries(log_backend_test srslog)
add_test(log_backend_test log_backend_test)
add_executable(logger_test logger_test.cpp)
target_link_libraries(logger_test srslog)
add_test(logger_test logger_test)
add_executable(any_test any_test.cpp)
target_link_libraries(any_test srslog)
add_test(any_test any_test)
add_executable(file_sink_test file_sink_test.cpp)
target_include_directories(file_sink_test PUBLIC ../../)
target_link_libraries(file_sink_test srslog)
add_test(file_sink_test file_sink_test)
add_executable(file_utils_test file_utils_test.cpp)
target_include_directories(file_utils_test PUBLIC ../../)
target_link_libraries(file_utils_test srslog)
add_test(file_utils_test file_utils_test)

View File

@ -0,0 +1,202 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/srslog/detail/support/any.h"
#include "testing_helpers.h"
using namespace srslog;
static bool when_default_constructed_then_any_is_empty()
{
detail::any a;
ASSERT_EQ(a.has_value(), false);
ASSERT_EQ(detail::any_cast<int>(&a), nullptr);
const detail::any b;
ASSERT_EQ(b.has_value(), false);
ASSERT_EQ(detail::any_cast<int>(&b), nullptr);
return true;
}
namespace {
struct copyable_object {
explicit copyable_object(int x) : x(x) {}
copyable_object(const copyable_object&) = default;
int x;
};
struct movable_object {
explicit movable_object(int x) : x(x) {}
movable_object(movable_object&&) = default;
int x;
};
} // namespace
static bool when_constructed_with_lvalue_then_any_has_value()
{
copyable_object value(5);
detail::any a(value);
ASSERT_EQ(a.has_value(), true);
const copyable_object* p = detail::any_cast<copyable_object>(&a);
ASSERT_NE(p, nullptr);
ASSERT_EQ(p->x, 5);
ASSERT_EQ(detail::any_cast<int>(&a), nullptr);
return true;
}
static bool when_constructed_with_rvalue_then_any_has_value()
{
movable_object value(5);
detail::any a(std::move(value));
ASSERT_EQ(a.has_value(), true);
const movable_object* p = detail::any_cast<movable_object>(&a);
ASSERT_NE(p, nullptr);
ASSERT_EQ(p->x, 5);
ASSERT_EQ(detail::any_cast<int>(&a), nullptr);
return true;
}
static bool when_constructed_with_make_any_then_any_has_int()
{
int value = 5;
auto a = detail::make_any<int>(value);
ASSERT_EQ(a.has_value(), true);
const int* p = detail::any_cast<int>(&a);
ASSERT_NE(p, nullptr);
ASSERT_EQ(*p, value);
ASSERT_EQ(detail::any_cast<char>(&a), nullptr);
return true;
}
static bool when_move_constructing_from_any_then_contents_are_transferred()
{
int value = 5;
detail::any a(value);
detail::any b(std::move(a));
ASSERT_EQ(a.has_value(), false);
ASSERT_EQ(b.has_value(), true);
const int* p = detail::any_cast<int>(&b);
ASSERT_NE(p, nullptr);
ASSERT_EQ(*p, value);
ASSERT_EQ(detail::any_cast<char>(&b), nullptr);
return true;
}
static bool when_move_assigning_from_any_then_contents_are_transferred()
{
int value = 5;
detail::any a(value);
detail::any b(3.0);
b = std::move(a);
ASSERT_EQ(a.has_value(), false);
ASSERT_EQ(b.has_value(), true);
const int* p = detail::any_cast<int>(&b);
ASSERT_NE(p, nullptr);
ASSERT_EQ(*p, value);
ASSERT_EQ(detail::any_cast<char>(&b), nullptr);
return true;
}
static bool when_any_is_reset_then_value_is_lost()
{
detail::any a(1);
a.reset();
ASSERT_EQ(a.has_value(), false);
return true;
}
static bool when_swapping_any_then_values_are_exchanged()
{
int i = 5;
detail::any a(i);
double d = 3.14;
detail::any b(d);
swap(a, b);
ASSERT_EQ(*detail::any_cast<double>(&a), d);
ASSERT_EQ(*detail::any_cast<int>(&b), i);
return true;
}
static bool when_null_is_passed_to_any_cast_then_null_is_returned()
{
detail::any* p = nullptr;
const detail::any* cp = nullptr;
ASSERT_EQ(detail::any_cast<int>(p), nullptr);
ASSERT_EQ(detail::any_cast<int>(cp), nullptr);
return true;
}
static bool when_empty_any_is_passed_to_any_cast_then_null_is_returned()
{
detail::any a;
const detail::any b;
ASSERT_EQ(detail::any_cast<int>(&a), nullptr);
ASSERT_EQ(detail::any_cast<int>(&b), nullptr);
return true;
}
int main()
{
TEST_FUNCTION(when_default_constructed_then_any_is_empty);
TEST_FUNCTION(when_constructed_with_lvalue_then_any_has_value);
TEST_FUNCTION(when_constructed_with_rvalue_then_any_has_value);
TEST_FUNCTION(when_constructed_with_make_any_then_any_has_int);
TEST_FUNCTION(when_move_constructing_from_any_then_contents_are_transferred);
TEST_FUNCTION(when_move_assigning_from_any_then_contents_are_transferred);
TEST_FUNCTION(when_any_is_reset_then_value_is_lost);
TEST_FUNCTION(when_swapping_any_then_values_are_exchanged);
TEST_FUNCTION(when_null_is_passed_to_any_cast_then_null_is_returned);
TEST_FUNCTION(when_empty_any_is_passed_to_any_cast_then_null_is_returned);
return 0;
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "src/srslog/sinks/file_sink.h"
#include "file_test_utils.h"
#include "testing_helpers.h"
using namespace srslog;
static const char* const log_filename = "testfile.log";
static bool when_data_is_written_to_file_then_contents_are_valid()
{
file_test_utils::scoped_file_deleter deleter(log_filename);
file_sink file(log_filename, 0);
std::vector<std::string> entries;
for (unsigned i = 0; i != 10; ++i) {
std::string entry = "Test log entry - " + std::to_string(i) + '\n';
file.write(detail::memory_buffer(entry));
entries.push_back(entry);
}
file.flush();
ASSERT_EQ(file_test_utils::file_exists(log_filename), true);
ASSERT_EQ(file_test_utils::compare_file_contents(log_filename, entries),
true);
return true;
}
static bool when_data_written_exceeds_size_threshold_then_new_file_is_created()
{
std::string filename0 =
file_utils::build_filename_with_index(log_filename, 0);
std::string filename1 =
file_utils::build_filename_with_index(log_filename, 1);
std::string filename2 =
file_utils::build_filename_with_index(log_filename, 2);
file_test_utils::scoped_file_deleter deleter = {
filename0, filename1, filename2};
file_sink file(log_filename, 5001);
// Build a 1000 byte entry.
std::string entry(1000, 'a');
// Fill in the file with 5000 bytes, one byte less than the threshold.
for (unsigned i = 0; i != 5; ++i) {
file.write(detail::memory_buffer(entry));
}
file.flush();
// Only one file should exist.
ASSERT_EQ(file_test_utils::file_exists(filename0), true);
ASSERT_EQ(file_test_utils::file_exists(filename1), false);
// Trigger a file rotation.
file.write(detail::memory_buffer(entry));
file.flush();
// A second file should be created.
ASSERT_EQ(file_test_utils::file_exists(filename0), true);
ASSERT_EQ(file_test_utils::file_exists(filename1), true);
// Fill in the second file with 4000 bytes, one byte less than the threshold.
for (unsigned i = 0; i != 4; ++i) {
file.write(detail::memory_buffer(entry));
}
file.flush();
// Two file should exist, third should not be created yet.
ASSERT_EQ(file_test_utils::file_exists(filename0), true);
ASSERT_EQ(file_test_utils::file_exists(filename1), true);
ASSERT_EQ(file_test_utils::file_exists(filename2), false);
// Trigger a file rotation.
file.write(detail::memory_buffer(entry));
file.flush();
// Three files should exist.
ASSERT_EQ(file_test_utils::file_exists(filename0), true);
ASSERT_EQ(file_test_utils::file_exists(filename1), true);
ASSERT_EQ(file_test_utils::file_exists(filename2), true);
return true;
}
int main()
{
TEST_FUNCTION(when_data_is_written_to_file_then_contents_are_valid);
TEST_FUNCTION(
when_data_written_exceeds_size_threshold_then_new_file_is_created);
return 0;
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLOG_FILE_TEST_UTILS_H
#define SRSLOG_FILE_TEST_UTILS_H
#include <cstdio>
#include <fstream>
#include <string>
#include <unistd.h>
#include <vector>
namespace file_test_utils {
/// Removes a list of files from the file system on object destruction.
class scoped_file_deleter
{
std::vector<std::string> paths;
public:
explicit scoped_file_deleter(const std::string& path) : paths({path}) {}
scoped_file_deleter(std::initializer_list<std::string> init) : paths(init) {}
~scoped_file_deleter()
{
for (const auto& path : paths) {
::remove(path.c_str());
}
}
};
/// Returns true if a file exists in the specified path, otherwise returns
/// false.
inline bool file_exists(const std::string& path)
{
return ::access(path.c_str(), F_OK) == 0;
}
/// Reads the contents of the file specified in path and returns true if they
/// match with the data in entries, otherwise returns false.
inline bool compare_file_contents(const std::string& path,
const std::vector<std::string>& entries)
{
std::ifstream file(path, std::ios::binary);
if (!file.is_open()) {
return false;
}
std::vector<std::string> data;
for (std::string line; std::getline(file, line);) {
data.push_back(line + '\n');
}
return (data == entries);
}
} // namespace file_test_utils
#endif // SRSLOG_FILE_TEST_UTILS_H

View File

@ -0,0 +1,186 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "src/srslog/sinks/file_utils.h"
#include "file_test_utils.h"
#include "testing_helpers.h"
using namespace srslog;
static const char* const log_filename = "testfile.log";
static const char* const log_filename2 = "testfile2.log";
static bool filename_extension_split_test()
{
auto checker = [](const std::string& base,
const std::string& file,
const std::string& ext) {
auto result = file_utils::split_filename_extension(base);
return (result.first == file && result.second == ext);
};
ASSERT_EQ(checker("filename", "filename", ""), true);
ASSERT_EQ(checker(".filename", ".filename", ""), true);
ASSERT_EQ(checker("filename.", "filename.", ""), true);
ASSERT_EQ(checker(".filename.", ".filename.", ""), true);
ASSERT_EQ(checker("filename.txt", "filename", ".txt"), true);
ASSERT_EQ(checker("/a/b/filename", "/a/b/filename", ""), true);
ASSERT_EQ(checker("/a/b/.filename", "/a/b/.filename", ""), true);
ASSERT_EQ(checker("/a/b/filename.", "/a/b/filename.", ""), true);
ASSERT_EQ(checker("/a/b/.filename.", "/a/b/.filename.", ""), true);
ASSERT_EQ(checker("/a/b/filename.txt", "/a/b/filename", ".txt"), true);
return true;
}
static bool filename_with_index_build_test()
{
ASSERT_EQ(file_utils::build_filename_with_index("filename", 0), "filename");
ASSERT_EQ(file_utils::build_filename_with_index("filename", 1), "filename.1");
ASSERT_EQ(file_utils::build_filename_with_index("filename.txt", 0),
"filename.txt");
ASSERT_EQ(file_utils::build_filename_with_index("filename.txt", 1),
"filename.1.txt");
return true;
}
static bool when_default_constructing_file_then_no_file_is_created()
{
file_utils::file f;
ASSERT_EQ(f.get_handle(), nullptr);
ASSERT_EQ(f.get_path().empty(), true);
return true;
}
static bool when_created_method_is_called_then_file_is_created()
{
file_test_utils::scoped_file_deleter deleter(log_filename);
file_utils::file f;
auto err_str = f.create(log_filename);
ASSERT_EQ(err_str.get_error().empty(), true);
ASSERT_NE(f.get_handle(), nullptr);
ASSERT_EQ(f.get_path(), log_filename);
ASSERT_EQ(file_test_utils::file_exists(log_filename), true);
return true;
}
static bool
when_created_method_is_called_twice_then_file_is_closed_and_created()
{
file_test_utils::scoped_file_deleter deleter = {log_filename, log_filename2};
file_utils::file f;
auto err_str = f.create(log_filename);
ASSERT_EQ(err_str.get_error().empty(), true);
ASSERT_EQ(file_test_utils::file_exists(log_filename), true);
err_str = f.create(log_filename2);
ASSERT_EQ(err_str.get_error().empty(), true);
ASSERT_EQ(file_test_utils::file_exists(log_filename2), true);
ASSERT_NE(f.get_handle(), nullptr);
ASSERT_EQ(f.get_path(), log_filename2);
return true;
}
static bool when_write_method_is_called_with_no_file_then_nothing_happens()
{
file_utils::file f;
auto err_str = f.write(detail::memory_buffer("test"));
ASSERT_EQ(err_str.get_error().empty(), true);
return true;
}
static bool when_flush_method_is_called_with_no_file_then_nothing_happens()
{
file_utils::file f;
auto err_str = f.flush();
ASSERT_EQ(err_str.get_error().empty(), true);
return true;
}
static bool when_data_is_written_to_file_then_contents_are_valid()
{
file_test_utils::scoped_file_deleter deleter(log_filename);
file_utils::file f;
auto err_str = f.create(log_filename);
ASSERT_EQ(err_str.get_error().empty(), true);
ASSERT_EQ(file_test_utils::file_exists(log_filename), true);
err_str = f.write(detail::memory_buffer("test\n"));
ASSERT_EQ(err_str.get_error().empty(), true);
err_str = f.flush();
ASSERT_EQ(err_str.get_error().empty(), true);
ASSERT_EQ(file_test_utils::compare_file_contents(log_filename, {"test\n"}),
true);
return true;
}
static bool when_file_is_opened_and_closed_then_members_are_reset()
{
file_test_utils::scoped_file_deleter deleter(log_filename);
file_utils::file f;
auto err_str = f.create(log_filename);
ASSERT_EQ(err_str.get_error().empty(), true);
ASSERT_EQ(file_test_utils::file_exists(log_filename), true);
f.close();
ASSERT_EQ(f.get_handle(), nullptr);
ASSERT_EQ(f.get_path().empty(), true);
return true;
}
int main()
{
TEST_FUNCTION(filename_extension_split_test);
TEST_FUNCTION(filename_with_index_build_test);
TEST_FUNCTION(when_default_constructing_file_then_no_file_is_created);
TEST_FUNCTION(when_created_method_is_called_then_file_is_created);
TEST_FUNCTION(
when_created_method_is_called_twice_then_file_is_closed_and_created);
TEST_FUNCTION(when_write_method_is_called_with_no_file_then_nothing_happens);
TEST_FUNCTION(when_flush_method_is_called_with_no_file_then_nothing_happens);
TEST_FUNCTION(when_data_is_written_to_file_then_contents_are_valid);
TEST_FUNCTION(when_file_is_opened_and_closed_then_members_are_reset);
return 0;
}

View File

@ -0,0 +1,231 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "src/srslog/log_backend_impl.h"
#include "srslte/srslog/sink.h"
#include "testing_helpers.h"
using namespace srslog;
static bool when_backend_is_started_then_is_started_returns_true()
{
log_backend_impl backend;
ASSERT_EQ(backend.is_started(), false);
backend.start();
ASSERT_EQ(backend.is_started(), true);
return true;
}
static bool when_backend_is_started_and_stopped_then_is_started_returns_false()
{
log_backend_impl backend;
backend.start();
backend.stop();
ASSERT_EQ(backend.is_started(), false);
return true;
}
namespace {
/// A Spy implementation of a log sink. Tests can query the last received memory
/// buffer by the sink and the number of invocations to the write method.
class sink_spy : public sink
{
public:
detail::error_string write(detail::memory_buffer buffer) override
{
++count;
str.insert(0, buffer.data(), buffer.size());
return {};
}
detail::error_string flush() override { return {}; }
unsigned write_invocation_count() const { return count; }
const std::string& received_buffer() const { return str; }
private:
unsigned count = 0;
std::string str;
};
} // namespace
static bool when_backend_is_not_started_then_pushed_log_entries_are_ignored()
{
sink_spy spy;
log_backend_impl backend;
detail::log_entry entry = {&spy, "Test"};
backend.push(std::move(entry));
ASSERT_EQ(spy.write_invocation_count(), 0);
return true;
}
/// Helper to build a log entry.
static detail::log_entry build_log_entry(sink* s)
{
// Populate store with some arguments.
fmt::dynamic_format_arg_store<fmt::printf_context> store;
store.push_back(3);
store.push_back("Hello");
store.push_back(3.14);
// Send the log entry to the backend.
detail::log_entry entry = {s, "Arg1:%u Arg2:%s Arg3:%.2f", std::move(store)};
return entry;
}
static bool when_backend_is_started_then_pushed_log_entries_are_sent_to_sink()
{
sink_spy spy;
log_backend_impl backend;
backend.start();
backend.push(build_log_entry(&spy));
// Stop the backend to ensure the entry has been processed.
backend.stop();
ASSERT_EQ(spy.write_invocation_count(), 1);
ASSERT_EQ(spy.received_buffer(), "Arg1:3 Arg2:Hello Arg3:3.14");
return true;
}
namespace {
/// A Configurable Stub implementation of an object to be invoked by the
/// backend. Upon invocation, instances of this class return an error that can
/// be configured dynamically.
class sink_error_stub : public sink
{
public:
explicit sink_error_stub(std::string err) : err(std::move(err)) {}
detail::error_string write(detail::memory_buffer buffer) override
{
return err;
}
detail::error_string flush() override { return err; }
private:
std::string err;
};
} // namespace
static bool when_sink_write_fails_then_error_handler_is_invoked()
{
std::string error_str("error");
sink_error_stub s(error_str);
bool valid_err = false;
unsigned count = 0;
// valid_err checks that the input error matches the expected string.
// The count variable counts the number of calls.
auto handler = [&count, &valid_err, error_str](const std::string& error) {
valid_err = (error == error_str);
++count;
};
log_backend_impl backend;
backend.set_error_handler(handler);
backend.start();
backend.push(build_log_entry(&s));
// Stop the backend to ensure the entry has been processed.
backend.stop();
ASSERT_EQ(valid_err, true);
ASSERT_EQ(count, 1);
return true;
}
static bool when_handler_is_set_after_start_then_handler_is_not_used()
{
sink_error_stub s("test");
unsigned count = 0;
// The count variable counts the number of calls.
auto handler = [&count](const std::string& error) { ++count; };
log_backend_impl backend;
// We want to remove output to stderr by the default handler.
backend.set_error_handler([](const std::string&) {});
backend.start();
backend.set_error_handler(handler);
backend.push(build_log_entry(&s));
// Stop the backend to ensure the entry has been processed.
backend.stop();
ASSERT_EQ(count, 0);
return true;
}
static bool when_empty_handler_is_used_then_backend_does_not_crash()
{
sink_error_stub s("test");
log_backend_impl backend;
// We want to remove output to stderr by the default handler.
backend.set_error_handler({});
backend.start();
backend.push(build_log_entry(&s));
// Stop the backend to ensure the entry has been processed.
backend.stop();
return true;
}
int main()
{
TEST_FUNCTION(when_backend_is_started_then_is_started_returns_true);
TEST_FUNCTION(
when_backend_is_started_and_stopped_then_is_started_returns_false);
TEST_FUNCTION(
when_backend_is_not_started_then_pushed_log_entries_are_ignored);
TEST_FUNCTION(
when_backend_is_started_then_pushed_log_entries_are_sent_to_sink);
TEST_FUNCTION(when_sink_write_fails_then_error_handler_is_invoked);
TEST_FUNCTION(when_handler_is_set_after_start_then_handler_is_not_used);
TEST_FUNCTION(when_empty_handler_is_used_then_backend_does_not_crash);
return 0;
}

View File

@ -0,0 +1,158 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/srslog/log_channel.h"
#include "srslte/srslog/sink.h"
#include "testing_helpers.h"
using namespace srslog;
namespace {
/// A Dummy implementation of a sink.
class sink_dummy : public sink
{
public:
detail::error_string write(detail::memory_buffer buffer) override
{
return {};
}
detail::error_string flush() override { return {}; }
};
/// A Dummy implementation of the log backend.
class backend_dummy : public detail::log_backend
{
public:
void start() override {}
void push(detail::log_entry&& entry) override {}
};
} // namespace
static bool when_log_channel_is_created_then_id_matches_expected_value()
{
backend_dummy backend;
sink_dummy s;
log_channel log("id", s, backend);
ASSERT_EQ(log.id(), "id");
return true;
}
static bool when_log_channel_is_disabled_then_enabled_returns_false()
{
backend_dummy backend;
sink_dummy s;
log_channel log("id", s, backend);
log.set_enabled(false);
ASSERT_EQ(log.enabled(), false);
return true;
}
static bool when_log_channel_is_enabled_then_enabled_returns_true()
{
backend_dummy backend;
sink_dummy s;
log_channel log("id", s, backend);
log.set_enabled(true);
ASSERT_EQ(log.enabled(), true);
return true;
}
namespace {
/// A Spy implementation of a log backend. Tests can query if the push method
/// has been invoked and the last received log entry.
class backend_spy : public detail::log_backend
{
public:
void start() override {}
void push(detail::log_entry&& entry) override
{
e = std::move(entry);
++count;
}
unsigned push_invocation_count() const { return count; }
const detail::log_entry& last_entry() const { return e; }
private:
unsigned count = 0;
detail::log_entry e;
};
} // namespace
static bool
when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend()
{
backend_spy backend;
sink_dummy s;
log_channel log("id", s, backend);
std::string fmtstring = "Arg1: %u Arg2: %s";
log(fmtstring, 42, "Hello");
ASSERT_EQ(backend.push_invocation_count(), 1);
ASSERT_NE(backend.last_entry().s, nullptr);
ASSERT_EQ(backend.last_entry().fmtstring, fmtstring);
return true;
}
static bool when_logging_in_disabled_log_channel_then_log_entry_is_ignored()
{
backend_spy backend;
sink_dummy s;
log_channel log("id", s, backend);
log.set_enabled(false);
std::string fmtstring = "Arg1: %u Arg2: %s";
log(fmtstring, 42, "Hello");
ASSERT_EQ(backend.push_invocation_count(), 0);
return true;
}
int main()
{
TEST_FUNCTION(when_log_channel_is_created_then_id_matches_expected_value);
TEST_FUNCTION(when_log_channel_is_disabled_then_enabled_returns_false);
TEST_FUNCTION(when_log_channel_is_enabled_then_enabled_returns_true);
TEST_FUNCTION(
when_logging_in_log_channel_then_log_entry_is_pushed_into_the_backend);
TEST_FUNCTION(when_logging_in_disabled_log_channel_then_log_entry_is_ignored);
return 0;
}

View File

@ -0,0 +1,141 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/srslog/logger.h"
#include "srslte/srslog/sink.h"
#include "testing_helpers.h"
using namespace srslog;
static const char* const logger_id = "TestLogger";
namespace {
/// A Dummy implementation of a sink.
class sink_dummy : public sink
{
public:
detail::error_string write(detail::memory_buffer buffer) override
{
return {};
}
detail::error_string flush() override { return {}; }
};
/// A Dummy implementation of the log backend.
class backend_dummy : public detail::log_backend
{
public:
void start() override {}
void push(detail::log_entry&& entry) override {}
};
/// Definition of a three level logger
enum class test_logger_levels { error, warning, info, LAST };
struct test_logger_channels {
log_channel& error;
log_channel& warning;
log_channel& info;
};
using test_logger = build_logger_type<test_logger_channels, test_logger_levels>;
} // namespace
static bool when_logger_is_created_then_id_matches_expected_value()
{
backend_dummy backend;
sink_dummy s;
log_channel error("err", s, backend);
log_channel warning("warning", s, backend);
log_channel info("info", s, backend);
test_logger logger(logger_id, error, warning, info);
ASSERT_EQ(logger.id(), logger_id);
return true;
}
static bool when_level_is_set_to_error_then_info_and_warning_is_disabled()
{
backend_dummy backend;
sink_dummy s;
log_channel error("err", s, backend);
log_channel warning("warning", s, backend);
log_channel info("info", s, backend);
test_logger logger(logger_id, error, warning, info);
logger.set_level(test_logger_levels::error);
ASSERT_EQ(logger.error.enabled(), true);
ASSERT_EQ(logger.warning.enabled(), false);
ASSERT_EQ(logger.info.enabled(), false);
return true;
}
static bool when_level_is_set_to_warning_then_info_is_disabled()
{
backend_dummy backend;
sink_dummy s;
log_channel error("err", s, backend);
log_channel warning("warning", s, backend);
log_channel info("info", s, backend);
test_logger logger(logger_id, error, warning, info);
logger.set_level(test_logger_levels::warning);
ASSERT_EQ(logger.error.enabled(), true);
ASSERT_EQ(logger.warning.enabled(), true);
ASSERT_EQ(logger.info.enabled(), false);
return true;
}
static bool when_level_is_set_to_info_then_all_are_enabled()
{
backend_dummy backend;
sink_dummy s;
log_channel error("err", s, backend);
log_channel warning("warning", s, backend);
log_channel info("info", s, backend);
test_logger logger(logger_id, error, warning, info);
logger.set_level(test_logger_levels::info);
ASSERT_EQ(logger.error.enabled(), true);
ASSERT_EQ(logger.warning.enabled(), true);
ASSERT_EQ(logger.info.enabled(), true);
return true;
}
int main()
{
TEST_FUNCTION(when_logger_is_created_then_id_matches_expected_value);
TEST_FUNCTION(when_level_is_set_to_error_then_info_and_warning_is_disabled);
TEST_FUNCTION(when_level_is_set_to_warning_then_info_is_disabled);
TEST_FUNCTION(when_level_is_set_to_info_then_all_are_enabled);
return 0;
}

View File

@ -0,0 +1,213 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srslte/srslog/sink.h"
#include "srslte/srslog/srslog.h"
#include "testing_helpers.h"
using namespace srslog;
static const char* const test_id = "Test";
//:FIXME: older compilers may not have defined this C++11 trait.
#if (defined(__clang__) && (__clang_major__ >= 5)) || \
(defined(__GNUG__) && (__GNUC__ >= 5))
static_assert(std::is_trivially_copyable<detail::memory_buffer>::value,
"Expected to be trivially copyable");
#endif
namespace {
/// A Dummy implementation of a sink.
class sink_dummy : public sink
{
public:
detail::error_string write(detail::memory_buffer buffer) override
{
return {};
}
detail::error_string flush() override { return {}; }
};
} // namespace
static bool when_no_channel_exists_then_channel_is_created()
{
sink_dummy s;
log_channel* channel = create_log_channel(test_id, s);
ASSERT_NE(channel, nullptr);
ASSERT_EQ(channel->id(), test_id);
return true;
}
static bool when_channel_already_exists_then_nullptr_is_returned()
{
sink_dummy s;
log_channel* channel = create_log_channel(test_id, s);
ASSERT_EQ(channel, nullptr);
return true;
}
static bool when_valid_id_is_passed_then_channel_is_found()
{
log_channel* c = find_log_channel(test_id);
ASSERT_NE(c, nullptr);
ASSERT_EQ(c->id(), test_id);
return true;
}
static bool when_invalid_id_is_passed_then_nothing_is_found()
{
ASSERT_EQ(find_log_channel("invalid"), nullptr);
return true;
}
static const char* const logger_id = "TestLogger";
static bool when_no_logger_exists_then_logger_is_created()
{
sink_dummy s;
log_channel& error = *create_log_channel("logger#error", s);
log_channel& warning = *create_log_channel("logger#warning", s);
log_channel& info = *create_log_channel("logger#info", s);
basic_logger* logger =
create_logger<basic_logger>(logger_id, error, warning, info);
ASSERT_NE(logger, nullptr);
ASSERT_EQ(logger->id(), logger_id);
return true;
}
static bool when_logger_already_exists_then_nullptr_is_returned()
{
log_channel& error = *find_log_channel("logger#error");
log_channel& warning = *find_log_channel("logger#warning");
log_channel& info = *find_log_channel("logger#info");
basic_logger* logger =
create_logger<basic_logger>(logger_id, error, warning, info);
ASSERT_EQ(logger, nullptr);
return true;
}
static bool when_valid_id_and_type_is_passed_then_logger_is_found()
{
basic_logger* l = find_logger<basic_logger>(logger_id);
ASSERT_NE(l, nullptr);
ASSERT_EQ(l->id(), logger_id);
return true;
}
static bool when_invalid_id_with_valid_type_is_passed_then_no_logger_is_found()
{
ASSERT_EQ(find_logger<basic_logger>("invalid"), nullptr);
return true;
}
/// Dummy logger type
enum class dummy_levels { error, LAST };
struct dummy_logger_channels {
log_channel& error;
};
using dummy_logger = build_logger_type<dummy_logger_channels, dummy_levels>;
static bool when_invalid_id_and_type_is_passed_then_no_logger_is_found()
{
ASSERT_EQ(find_logger<dummy_logger>("invalid"), nullptr);
return true;
}
static bool when_valid_id_with_invalid_type_is_passed_then_no_logger_is_found()
{
ASSERT_EQ(find_logger<dummy_logger>(logger_id), nullptr);
return true;
}
static bool when_no_sink_exists_then_sink_is_created()
{
sink* s = create_stdout_sink();
ASSERT_NE(s, nullptr);
return true;
}
static bool when_sink_already_exists_then_nullptr_is_returned()
{
sink* s = create_stdout_sink();
ASSERT_EQ(s, nullptr);
return true;
}
static bool when_valid_id_is_passed_then_sink_is_found()
{
sink* s = find_sink("stdout");
ASSERT_NE(s, nullptr);
return true;
}
static bool when_invalid_id_is_passed_then_no_sink_is_found()
{
ASSERT_EQ(find_sink("invalid"), nullptr);
return true;
}
int main()
{
TEST_FUNCTION(when_no_channel_exists_then_channel_is_created);
TEST_FUNCTION(when_channel_already_exists_then_nullptr_is_returned);
TEST_FUNCTION(when_valid_id_is_passed_then_channel_is_found);
TEST_FUNCTION(when_invalid_id_is_passed_then_nothing_is_found);
TEST_FUNCTION(when_no_logger_exists_then_logger_is_created);
TEST_FUNCTION(when_logger_already_exists_then_nullptr_is_returned);
TEST_FUNCTION(when_valid_id_and_type_is_passed_then_logger_is_found);
TEST_FUNCTION(
when_invalid_id_with_valid_type_is_passed_then_no_logger_is_found);
TEST_FUNCTION(when_invalid_id_and_type_is_passed_then_no_logger_is_found);
TEST_FUNCTION(
when_valid_id_with_invalid_type_is_passed_then_no_logger_is_found);
TEST_FUNCTION(when_no_sink_exists_then_sink_is_created);
TEST_FUNCTION(when_sink_already_exists_then_nullptr_is_returned);
TEST_FUNCTION(when_valid_id_is_passed_then_sink_is_found);
TEST_FUNCTION(when_invalid_id_is_passed_then_no_sink_is_found);
return 0;
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef TESTING_HELPERS_H
#define TESTING_HELPERS_H
#include <cstdio>
/// Invokes the given test function and printing test results to stdout.
#define TEST_FUNCTION(func) \
do { \
if (!func()) { \
std::printf("Test \"%s\" FAILED! - %s:%u\n", #func, __FILE__, __LINE__); \
return -1; \
} else { \
std::printf("Test \"%s\" PASSED!\n", #func); \
} \
} while (0)
/// Asserts for equality between a and b. The == operator must exist for the
/// input types.
#define ASSERT_EQ(a, b) \
do { \
if ((a) == (b)) { \
; \
} else { \
std::printf("EQ assertion failed in Test \"%s\" - %s:%u\n Condition " \
"\"%s\" not met\n", \
__FUNCTION__, \
__FILE__, \
__LINE__, \
#a " == " #b); \
return false; \
} \
} while (0)
/// Asserts for non equality between a and b. The != operator must exist for the
/// input types.
#define ASSERT_NE(a, b) \
do { \
if ((a) != (b)) { \
; \
} else { \
std::printf("NE assertion failed in Test \"%s\" - %s:%u\n Condition " \
"\"%s\" not met\n", \
__FUNCTION__, \
__FILE__, \
__LINE__, \
#a " != " #b); \
return false; \
} \
} while (0)
#endif // TESTING_HELPERS_H

View File

@ -32,8 +32,8 @@ endif (RPATH)
add_executable(srsue main.cc ue.cc metrics_stdout.cc metrics_csv.cc)
set(SRSUE_SOURCES srsue_phy srsue_stack srsue_upper srsue_mac srsue_rrc)
set(SRSLTE_SOURCES srslte_common srslte_mac srslte_phy srslte_radio srslte_upper rrc_asn1)
set(SRSUE_SOURCES srsue_phy srsue_stack srsue_upper srsue_mac srsue_rrc srslog)
set(SRSLTE_SOURCES srslte_common srslte_mac srslte_phy srslte_radio srslte_upper rrc_asn1 srslog)
if(ENABLE_5GNR)
set(SRSUE_SOURCES ${SRSUE_SOURCES} srsue_nr_stack srsue_rrc_nr srsue_mac_nr)

View File

@ -22,9 +22,11 @@
#include "srslte/common/common_helper.h"
#include "srslte/common/config_file.h"
#include "srslte/common/crash_handler.h"
#include "srslte/common/logger_srslog_wrapper.h"
#include "srslte/common/logmap.h"
#include "srslte/common/metrics_hub.h"
#include "srslte/common/signal_handler.h"
#include "srslte/srslog/srslog.h"
#include "srslte/srslte.h"
#include "srslte/version.h"
#include "srsue/hdr/metrics_csv.h"
@ -600,31 +602,44 @@ static void* input_loop(void*)
return nullptr;
}
/// Adjusts the input value in args from kbytes to bytes.
static size_t fixup_log_file_maxsize(int x)
{
return (x < 0) ? 0 : size_t(x) * 1024u;
}
int main(int argc, char* argv[])
{
srslte_register_signal_handler();
srslte_debug_handle_crash(argc, argv);
all_args_t args = {};
if (parse_args(&args, argc, argv) != SRSLTE_SUCCESS) {
return SRSLTE_ERROR;
};
// Setup logging
srslte::logger_stdout logger_stdout;
srslte::logger* logger = nullptr;
if (args.log.filename == "stdout") {
logger = &logger_stdout;
} else {
logger_file.init(args.log.filename, args.log.file_max_size);
logger = &logger_file;
if (int err = parse_args(&args, argc, argv)) {
return err;
}
srslte::logmap::set_default_logger(logger);
// Setup logging.
log_sink = (args.log.filename == "stdout")
? srslog::create_stdout_sink()
: srslog::create_file_sink(args.log.filename, fixup_log_file_maxsize(args.log.file_max_size));
if (!log_sink) {
return SRSLTE_ERROR;
}
srslog::log_channel* chan = srslog::create_log_channel("main_channel", *log_sink);
if (!chan) {
return SRSLTE_ERROR;
}
srslte::srslog_wrapper log_wrapper(*chan);
// Start the log backend.
srslog::init();
srslte::logmap::set_default_logger(&log_wrapper);
log_args(argc, argv, "UE");
// Create UE instance
srsue::ue ue;
if (ue.init(args, logger)) {
if (ue.init(args, &log_wrapper)) {
ue.stop();
return SRSLTE_SUCCESS;
}