Compare commits

...

2 Commits

8 changed files with 291 additions and 29 deletions

119
.clang-format Normal file
View File

@ -0,0 +1,119 @@
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 11.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentGotoLabels: false
IndentPPDirectives: None
IndentWidth: 8
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
PenaltyBreakAssignment: 10
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatementsExceptForEachMacros
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 8
UseTab: Always
...

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "test/catch2"]
path = test/catch2
url = https://git.shimatta.de/3rd-party/catch2.git

View File

@ -5,4 +5,5 @@ aux_source_directory("src" SOURCES)
add_library(${PROJECT_NAME} STATIC ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC include)
# Add tests. This is not included in the overall build
add_subdirectory(test)

View File

@ -2,16 +2,44 @@
#define _SIMPLE_PRINTF_H_
#include <stddef.h>
#include <stdbool.h>
#ifndef SIMPLE_PRINTF_ATTR_FORMAT
# if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)))
# define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) __attribute__((__format__(__printf__, fmt, args)))
# else
# define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args)
# endif
#if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)))
#define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) __attribute__((__format__(__printf__, fmt, args)))
#else
# define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args)
#define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args)
#endif
#else
#define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args)
#endif
enum format_specifier_type {
FMT_INVALID = 0,
FMT_INTEGER,
FMT_UNSIGNED,
FMT_STRING,
FMT_HEXUINT_CAPITALIZED,
FMT_HEXUINT,
FMT_CHAR,
FMT_SIZE,
FMT_PERCENT,
};
struct format_specifier {
enum format_specifier_type specifier;
size_t length; /**< @brief lenght of formatted output.*/
bool zero_pad;
size_t length_of_provided_specifier;
};
/**
* @brief Helper function to parse printf format specifiers
*
* @param start Start of parsing
* @param[out] result Result of format specifier parsing
*/
void parse_format_specifier(const char *start, struct format_specifier *result);
/**
* @brief Simple implementation of snprintf

View File

@ -2,25 +2,6 @@
#include <stddef.h>
#include <stdbool.h>
enum format_specifier_type {
FMT_INVALID = 0,
FMT_INTEGER,
FMT_UNSIGNED,
FMT_STRING,
FMT_HEXUINT_CAPITALIZED,
FMT_HEXUINT,
FMT_CHAR,
FMT_SIZE,
FMT_PERCENT,
};
struct format_specifier {
enum format_specifier_type specifier;
size_t length; /**< @brief lenght of formatted output.*/
bool zero_pad;
size_t length_of_provided_specifier;
};
enum format_parser_state {
PARSER_PERCENT = 0,
PARSER_SECOND,
@ -87,7 +68,7 @@ static enum format_specifier_type resolve_specifier(char c)
return ret;
}
static void parse_format_specifier(const char *start, struct format_specifier *result)
void parse_format_specifier(const char *start, struct format_specifier *result)
{
size_t fmt_len = 1ull;
bool keep_parsing = true;
@ -115,15 +96,19 @@ static void parse_format_specifier(const char *start, struct format_specifier *r
switch (state) {
case PARSER_PERCENT:
if (*ptr == '%') {
advance_ptr++;
advance_ptr = true;
next_state = PARSER_SECOND;
} else {
keep_parsing = false;
}
break;
case PARSER_SECOND:
if (*ptr == '0') {
result->zero_pad = true;
advance_ptr++;
advance_ptr = true;
next_state = PARSER_NUM;
current_token = ptr + 1;
token_length = 0;
} else if (*ptr > '9' || *ptr < '0') {
/* Specifier */
next_state = PARSER_SPECIFIER;

11
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,11 @@
project(simple-printf-test)
add_subdirectory(catch2 EXCLUDE_FROM_ALL)
add_custom_target(test "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}" "-r compact" "-s" DEPENDS ${PROJECT_NAME})
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/catch-framework")
aux_source_directory("src" TEST_SOURCES)
add_executable(${PROJECT_NAME} EXCLUDE_FROM_ALL ${TEST_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE simple-printf Catch2::Catch2WithMain)

1
test/catch2 Submodule

@ -0,0 +1 @@
Subproject commit 0631b607ee2bbc07c7c238f0b15b23ef21926960

114
test/src/tests.cpp Normal file
View File

@ -0,0 +1,114 @@
#include <array>
#include <catch2/catch_test_macros.hpp>
#include <cstdio>
#include <string>
#include <iostream>
extern "C" {
#include <simple-printf/simple-printf.h>
}
TEST_CASE("Parse Invalid Specifier")
{
const char *tst = "%m";
const char *tst2 = "%45m";
const char *tst3 = "%014m";
struct format_specifier result;
parse_format_specifier(tst, &result);
REQUIRE(result.specifier == FMT_INVALID);
parse_format_specifier(tst, &result);
REQUIRE(result.specifier == FMT_INVALID);
parse_format_specifier(tst, &result);
REQUIRE(result.specifier == FMT_INVALID);
}
TEST_CASE("Parse Format Specifier c", "Char")
{
const char *tst = "%c";
struct format_specifier result;
parse_format_specifier(tst, &result);
REQUIRE(result.length_of_provided_specifier == 2);
REQUIRE(result.specifier == FMT_CHAR);
REQUIRE(result.zero_pad == false);
}
TEST_CASE("Parse Format Specifier s", "String")
{
const char *tst = "%s";
struct format_specifier result;
parse_format_specifier(tst, &result);
REQUIRE(result.length_of_provided_specifier == 2);
REQUIRE(result.specifier == FMT_STRING);
REQUIRE(result.length == 0);
REQUIRE(result.zero_pad == false);
}
TEST_CASE("Parse Format Specifier 04s", "String")
{
const char *tst = "%04s";
struct format_specifier result;
parse_format_specifier(tst, &result);
REQUIRE(result.specifier == FMT_STRING);
REQUIRE(result.length_of_provided_specifier == 4);
REQUIRE(result.length == 4);
REQUIRE(result.zero_pad == true);
}
TEST_CASE("Parse Format Specifier 08x", "hex")
{
const char *tst = "%08x";
struct format_specifier result;
parse_format_specifier(tst, &result);
REQUIRE(result.specifier == FMT_HEXUINT);
REQUIRE(result.length_of_provided_specifier == 4);
REQUIRE(result.length == 8);
REQUIRE(result.zero_pad == true);
}
TEST_CASE("Parse Format Specifier 08x with stuff", "hex")
{
const std::array<std::string, 8> tst_arr {
std::move(std::string("%08xff458578")),
std::move(std::string("%08x45578")),
std::move(std::string("%08xx458578")),
std::move(std::string("%08x44458578")),
std::move(std::string("%08x45sdf458578")),
std::move(std::string("%08xsfsdf4558578")),
std::move(std::string("%08xX")),
std::move(std::string("%08xdsda"))
};
struct format_specifier result;
for (const auto &tst : tst_arr) {
parse_format_specifier(tst.c_str(), &result);
REQUIRE(result.specifier == FMT_HEXUINT);
REQUIRE(result.length_of_provided_specifier == 4);
REQUIRE(result.length == 8);
REQUIRE(result.zero_pad == true);
}
}
TEST_CASE("Parse Percent spefifier")
{
const char *tst = "%%";
struct format_specifier result;
parse_format_specifier(tst, &result);
REQUIRE(result.specifier == FMT_PERCENT);
REQUIRE(result.length_of_provided_specifier == 2);
REQUIRE(result.length == 0);
REQUIRE(result.zero_pad == false);
}