Skip to content

Commit

Permalink
Merge pull request #16 from MichaelMiller-/memory-class
Browse files Browse the repository at this point in the history
Memory class
  • Loading branch information
MichaelMiller- authored Jan 12, 2025
2 parents 2a76645 + 62bb03b commit 1c04b79
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 21 deletions.
29 changes: 19 additions & 10 deletions include/sec21/literals/memory.h
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
#pragma once

#include <sec21/memory.h>

namespace sec21::literals
{
//! \brief Byte
constexpr auto operator""_B(unsigned long long n) noexcept -> memory { return {n}; }

// SI
//
//! \brief Kilobyte
constexpr auto operator""_kB(unsigned long long n) noexcept { return n * 1000; }
constexpr auto operator""_kB(unsigned long long n) noexcept -> memory { return {n * 1000}; }
//! \brief Megabyte
constexpr auto operator""_MB(unsigned long long n) noexcept { return n * 1000_kB; }
constexpr auto operator""_MB(unsigned long long n) noexcept -> memory { return memory{n} * 1000_kB; }
//! \brief Gigabyte
constexpr auto operator""_GB(unsigned long long n) noexcept { return n * 1000_MB; }
constexpr auto operator""_GB(unsigned long long n) noexcept -> memory { return memory{n} * 1000_MB; }
//! \brief Terabyte
constexpr auto operator""_TB(unsigned long long n) noexcept { return n * 1000_GB; }
constexpr auto operator""_TB(unsigned long long n) noexcept -> memory { return memory{n} * 1000_GB; }
//! \brief Petabyte
constexpr auto operator""_PB(unsigned long long n) noexcept { return n * 1000_TB; }
constexpr auto operator""_PB(unsigned long long n) noexcept -> memory { return memory{n} * 1000_TB; }

// IEC
//
//! \brief Kibibyte
constexpr auto operator""_kiB(unsigned long long n) noexcept { return n * 1024; }
constexpr auto operator""_kiB(unsigned long long n) noexcept -> memory { return {n * 1024}; }
//! \brief Mebibyte
constexpr auto operator""_MiB(unsigned long long n) noexcept { return n * 1024_kiB; }
constexpr auto operator""_MiB(unsigned long long n) noexcept -> memory { return memory{n} * 1024_kiB; }
//! \brief Gibibyte
constexpr auto operator""_GiB(unsigned long long n) noexcept { return n * 1024_MiB; }
constexpr auto operator""_GiB(unsigned long long n) noexcept -> memory { return memory{n} * 1024_MiB; }
//! \brief Tebibyte
constexpr auto operator""_TiB(unsigned long long n) noexcept { return n * 1024_GiB; }
constexpr auto operator""_TiB(unsigned long long n) noexcept -> memory { return memory{n} * 1024_GiB; }
//! \brief Pebibyte
constexpr auto operator""_PiB(unsigned long long n) noexcept { return n * 1024_TiB; }
constexpr auto operator""_PiB(unsigned long long n) noexcept -> memory { return memory{n} * 1024_TiB; }
} // namespace sec21::literals
99 changes: 99 additions & 0 deletions include/sec21/memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#pragma once

#include <cmath>
#include <format>
#include <utility>

namespace sec21
{
struct memory
{
using value_t = unsigned long long;
value_t bytes{};

friend auto operator<=>(memory const&, memory const&) = default;

friend auto operator<<(std::ostream& out, memory const& obj) -> auto& { return out << obj.bytes; }
};

[[nodiscard]] constexpr auto operator+(memory const& lhs, std::byte rhs) noexcept -> memory
{
return {lhs.bytes + std::to_underlying(rhs)};
}

[[nodiscard]] constexpr auto operator+(memory const& lhs, memory const& rhs) noexcept -> memory
{
return {lhs.bytes + rhs.bytes};
}

[[nodiscard]] constexpr auto operator-(memory const& lhs, std::byte rhs) noexcept -> memory
{
if (std::to_underlying(rhs) > lhs.bytes) {
throw std::out_of_range{"right-hand-side is greater than the left-hand-side"};
}
return {lhs.bytes - std::to_underlying(rhs)};
}

[[nodiscard]] constexpr auto operator-(memory const& lhs, memory const& rhs) -> memory
{
if (rhs.bytes > lhs.bytes) {
throw std::out_of_range{"right-hand-side is greater than the left-hand-side"};
}
return {lhs.bytes - rhs.bytes};
}

[[nodiscard]] constexpr auto operator*(memory const& lhs, std::byte rhs) noexcept -> memory
{
return {lhs.bytes * std::to_underlying(rhs)};
}

[[nodiscard]] constexpr auto operator*(memory const& lhs, memory const& rhs) noexcept -> memory
{
return {lhs.bytes * rhs.bytes};
}
} // namespace sec21

template <>
struct std::formatter<sec21::memory>
{
constexpr auto parse(std::format_parse_context& ctx)
{
auto pos = ctx.begin();
while (pos != ctx.end() and *pos != '}') {
if (*pos == 'h' or *pos == 'H') {
human_readable = true;
}
if (*pos == '.') {
pos = std::from_chars(++pos, ctx.end(), precision).ptr;
--pos;
}
++pos;
}
return pos;
}

auto format(sec21::memory const& obj, std::format_context& ctx) const
{
constexpr auto units = std::array{"B", "kB", "MB", "GB", "TB", "PB"};

if (human_readable) {
constexpr auto factor = 1000;
constexpr auto factorIEC = 1024; //! \todo

auto value = static_cast<double>(obj.bytes);
auto unit = units[0];

for (decltype(units.size()) i = 0; i < units.size(); ++i) {
if (obj.bytes >= std::pow(factor, i)) {
value = static_cast<double>(obj.bytes) / std::pow(factor, i);
unit = units[i];
}
}
return std::format_to(ctx.out(), "{:.{}f}{}", value, precision, unit);
}
return std::format_to(ctx.out(), "{}{}", obj.bytes, units[0]);
}

bool human_readable{false};
std::size_t precision{2};
};
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ test("for_each_adjacent")
test("for_each_chunk")
test("for_each_indexed")
test("limited_quantity")
test("memory")
test("literals.memory")
test("reflection.get_column")
test("scope_guard")
Expand Down
25 changes: 14 additions & 11 deletions tests/literals.memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

#include <sec21/literals/memory.h>

TEST_CASE("checks memory literals", "[sec21][type_traits]")
TEST_CASE("checks memory literals", "[sec21][literals]")
{
using namespace sec21;
using namespace sec21::literals;

STATIC_REQUIRE(1_kB == 1'000);
STATIC_REQUIRE(1_MB == 1'000'000);
STATIC_REQUIRE(1_GB == 1'000'000'000);
STATIC_REQUIRE(1_TB == 1'000'000'000'000);
STATIC_REQUIRE(1_PB == 1'000'000'000'000'000);
STATIC_REQUIRE(memory{1_B}.bytes == 1);

STATIC_REQUIRE(1_kiB == 1024);
STATIC_REQUIRE(1_MiB == 1048576);
STATIC_REQUIRE(1_GiB == 1073741824);
STATIC_REQUIRE(1_TiB == 1099511627776);
STATIC_REQUIRE(1_PiB == 1125899906842624);
STATIC_REQUIRE(memory{1_kB}.bytes == 1'000);
STATIC_REQUIRE(memory{1_MB}.bytes == 1'000'000);
STATIC_REQUIRE(memory{1_GB}.bytes == 1'000'000'000);
STATIC_REQUIRE(memory{1_TB}.bytes == 1'000'000'000'000);
STATIC_REQUIRE(memory{1_PB}.bytes == 1'000'000'000'000'000);

STATIC_REQUIRE(memory{1_kiB}.bytes == 1024);
STATIC_REQUIRE(memory{1_MiB}.bytes == 1048576);
STATIC_REQUIRE(memory{1_GiB}.bytes == 1073741824);
STATIC_REQUIRE(memory{1_TiB}.bytes == 1099511627776);
STATIC_REQUIRE(memory{1_PiB}.bytes == 1125899906842624);
}
51 changes: 51 additions & 0 deletions tests/memory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <catch.hpp>

#include <sec21/literals/memory.h>
#include <sec21/memory.h>

TEST_CASE("memory class", "[sec21]")
{
using namespace sec21;
using namespace sec21::literals;

SECTION("addition")
{
REQUIRE(memory{1} + memory{2} == memory{3});
REQUIRE(4_kiB + std::byte{4} == memory{4100});
}
SECTION("subtraction")
{
REQUIRE(memory{2} - memory{1} == memory{1});
REQUIRE(4_kiB - std::byte{4} == memory{4092});
}
SECTION("test safety check of subtraction operator")
{
REQUIRE_THROWS([] { auto result = memory{2} - memory{10}; }());
// REQUIRE_THROWS([] { auto result = memory{2} - std::byte{10}; }());
}
SECTION("multiplication")
{
REQUIRE(memory{2} * memory{1} == 2_B);
REQUIRE(4_kiB * std::byte{2} == memory{8192});
}
SECTION("test formatter")
{
SECTION("default") { REQUIRE(std::format("{}", memory{1024}) == "1024B"); }

SECTION("format output to a human readable format")
{
REQUIRE(std::format("{:h}", memory{1_kiB}) == "1.02kB");
REQUIRE(std::format("{:h}", memory{1_MiB}) == "1.05MB");
REQUIRE(std::format("{:h}", memory{1_GiB}) == "1.07GB");
REQUIRE(std::format("{:h}", memory{1_TiB}) == "1.10TB");
REQUIRE(std::format("{:h}", memory{1_PiB}) == "1.13PB");
}
SECTION("human readable format with precision")
{
REQUIRE(std::format("{:.0h}", memory{1024 * 1024}) == "1MB");
REQUIRE(std::format("{:.1h}", memory{1024 * 1024}) == "1.0MB");
REQUIRE(std::format("{:.4h}", memory{1024 * 1024}) == "1.0486MB");
REQUIRE(std::format("{:.h}", memory{1024 * 1024}) == "1.05MB");
}
}
}

0 comments on commit 1c04b79

Please sign in to comment.