Compare commits

...

2 commits

Author SHA1 Message Date
9480520e4d [General] Add a more proper description 2025-05-18 01:09:56 +02:00
18dbfe22cd [Base64] Add validation 2025-05-18 01:02:55 +02:00
3 changed files with 144 additions and 1 deletions

View file

@ -138,6 +138,33 @@ namespace Garbage::Base64
return result;
}
[[nodiscard]] constexpr
bool Validate(std::string_view data)
{
// Technically superfluous - the decoder can deal with unpadded data.
if (data.size() % 4 != 0) {
return false;
}
std::size_t i = 0;
for (char ch : data) {
bool condition = (ch == '+') ||
(ch >= '/' && ch <= '9') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z');
if (!condition) {
// Allow '=' for the last two character
if (i >= data.size() - 2 && ch == '=') {
continue;
}
return false;
}
++i;
}
return true;
}
}
#endif // GARBAGE_BASE64_HPP

107
README.md
View file

@ -1,3 +1,108 @@
# Garbage
A collection of various single-header libraries of questionable quality.
A collection of various single-header libraries of questionable quality.
## Usage
To use these libraries (please don't), just add them into your project's include path.
Ideally, use CMake with FetchContent pinned to a specific commit hash so you don't encounter
nasty surprises when the libraries update.
```cmake
include(FetchContent)
FetchContent_Declare(
Garbage
GIT_REPOSITORY https://code.3011.io/TennesseeTrash/Garbage
GIT_TAG 72f02ff856ddfd1b817d4f95096ec1be0ed11a49
)
FetchContent_MakeAvailable(
Garbage
)
```
Once you've added the `Include` directory into your include path, you can use the headers you need,
e.g.
```cpp
#include <Garbage/Base64.hpp>
// Now you can use
Garbage::Base64::Decode(/* some base64 string */);
```
## Base64
A very simple library composed of 3 functions in the `Garbage::Base64` namespace.
- `Encode()` takes a `std::span<std::uint8_t>` (i.e. an array of bytes), and produces a
padded Base64 encoded string out of it.
- `Decode()` takes an encoded `std::string_view`, and converts it into an array of bytes
(specifically a `std::vector<std::uint8_t>`).
- `Validate()` performs validation of an untrusted `std::string_view`. This is necessary
because `Decode()` does not do any checking and running it on unverified data is
dangerous. This function is not completely robust, but it is good enough to make sure
the `Decode()` call is safe.
## SimpleConf
A small config file library. Provides a `Garbage::SimpleConf` class that reads files
with key-value pairs. This library makes use of exceptions, it will throw `Garbage::SimpleConfError`
when an error is encountered. These exceptions inerit from `std::exception`, so using those to catch
them works fine.
```cpp
///////////////////////////////////////////////////////////////////////////////
// INIT
// Read a file from disk
Garbage::SimpleConf config(std::filesystem::path(/*path to the config*/));
// Use an externally provided string
std::string rawConfig = /* whatever procedure to obtain a string */;
Garbage::SimpleConf config(std::move(rawConfig));
///////////////////////////////////////////////////////////////////////////////
// GETTING VALUES
// Optional values
// Get a std::optional
auto value = config.GetOptional<std::string>("SomeKey");
// Get a default value
int value = config.Get<int>("SomeKey", 0 /* Optional default value */);
// Required values
using Required = Garbage::SimpleConf::Required;
int value = config.Get<int, Required>("SomeKey");
```
The config files are structured such that a single line contains a key-value pair separated by a `=`
character.
Some specifics:
- Leading and trailing whitespace is removed from both keys and values.
- Whitespace is is allowed inside both keys and values.
- Values may be empty.
- The `#` character denotes the start of a comment, everything after (and including)
this character is ignored until the end of the line.
Example:
```conf
# Simplest scenario
SomeKey=SomeValue
# Also works
SomeKey = SomeValue
# Also supported, but note that the key is now "Some Key"
Some Key = Some Value
Comments = After # The pair are also fine
# Also fine
KeyOnly=
```
## SQLite
WIP

View file

@ -10,6 +10,8 @@ TEST_CASE("Base64 encoding/decoding tests")
std::string encoded = Garbage::Base64::Encode(data);
REQUIRE(encoded.size() == 0);
REQUIRE(Garbage::Base64::Validate(encoded));
std::vector<std::uint8_t> decoded = Garbage::Base64::Decode(encoded);
REQUIRE(encoded.size() == 0);
}
@ -21,12 +23,15 @@ TEST_CASE("Base64 encoding/decoding tests")
std::string encoded = Garbage::Base64::Encode(data);
REQUIRE(encoded == "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu");
REQUIRE(Garbage::Base64::Validate(encoded));
}
SECTION("Decoding short data")
{
std::string data("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu");
REQUIRE(Garbage::Base64::Validate(data));
std::vector<std::uint8_t> decoded = Garbage::Base64::Decode(data);
std::string destination(decoded.begin(), decoded.end());
REQUIRE(destination == "Many hands make light work.");
@ -39,12 +44,15 @@ TEST_CASE("Base64 encoding/decoding tests")
std::string encoded = Garbage::Base64::Encode(data);
REQUIRE(encoded == "V2l0aCBhIHBhZGRpbmc=");
REQUIRE(Garbage::Base64::Validate(encoded));
}
SECTION("Decoding with a padding char")
{
std::string data("V2l0aCBhIHBhZGRpbmc=");
REQUIRE(Garbage::Base64::Validate(data));
std::vector<std::uint8_t> decoded = Garbage::Base64::Decode(data);
std::string destination(decoded.begin(), decoded.end());
REQUIRE(destination == "With a padding");
@ -57,12 +65,15 @@ TEST_CASE("Base64 encoding/decoding tests")
std::string encoded = Garbage::Base64::Encode(data);
REQUIRE(encoded == "V2l0aCB0d28gcGFkZGluZw==");
REQUIRE(Garbage::Base64::Validate(encoded));
}
SECTION("Decoding with two padding chars")
{
std::string data("V2l0aCB0d28gcGFkZGluZw==");
REQUIRE(Garbage::Base64::Validate(data));
std::vector<std::uint8_t> decoded = Garbage::Base64::Decode(data);
std::string destination(decoded.begin(), decoded.end());
REQUIRE(destination == "With two padding");