Skip to content

Instantly share code, notes, and snippets.

@DrkWithT
Created February 24, 2025 01:54
Show Gist options
  • Save DrkWithT/111ef99ab8c9464126e50089a9b08ab1 to your computer and use it in GitHub Desktop.
Save DrkWithT/111ef99ab8c9464126e50089a9b08ab1 to your computer and use it in GitHub Desktop.
#include <utility>
#include <concepts>
#include <algorithm>
#include <array>
#include <string>
template <template <typename> typename Buffer, typename ItemT>
concept BufferKind = requires(Buffer<ItemT>&& arg, std::size_t count) {
{arg.getPtr()} -> std::same_as<ItemT*>;
{arg.getSize()} -> std::same_as<std::size_t>;
{arg.getLength()} -> std::same_as<std::size_t>;
{arg.markLength(count)};
};
template <typename T, template <typename> typename B, typename ItemT>
concept IOProviderKind = requires(T&& arg, B<ItemT>&& buffer, int Fd, std::size_t N) {
{arg.readOne(Fd)} -> std::same_as<char>;
{arg.readBlob(Fd, N, buffer)} -> std::same_as<bool>;
{arg.writeBlob(Fd, buffer)} -> std::same_as<bool>;
} and BufferKind<B, ItemT>;
template <typename ItemT>
class FixedBuffer {
private:
static constexpr auto limit = 1024UL;
std::array<ItemT, limit> m_data;
std::size_t m_length;
public:
FixedBuffer() noexcept
: m_data {}, m_length {0UL} {
std::fill(m_data.begin(), m_data.end(), ItemT {});
}
[[nodiscard]] ItemT* getPtr() noexcept {
return m_data.data();
}
[[nodiscard]] std::size_t getSize() const noexcept {
return limit;
}
[[nodiscard]] std::size_t getLength() const noexcept {
return m_length;
}
void markLength(std::size_t count) noexcept {
m_length = count;
}
};
/// @note Decouple I/O logic from the socket for easier mocking of the socket interface's functionality.
class MockIO {
private:
std::string m_input;
int m_pos;
[[nodiscard]] bool isEmpty() const noexcept {
return m_pos >= m_input.size();
}
public:
MockIO(std::string temp) noexcept
: m_input {std::move(temp)}, m_pos {0} {}
char readOne(int fd) noexcept {
if (isEmpty()) {
return '\0';
}
return m_input[m_pos++];
}
template <template <typename> typename Buffer, typename ItemT> requires (BufferKind<Buffer, ItemT>)
[[nodiscard]] bool readBlob(int fd, std::size_t count, Buffer<ItemT>& target) noexcept {
return true; // dud behavior: pretend this read works...
}
template <template <typename> typename Buffer, typename ItemT> requires (BufferKind<Buffer, ItemT>)
[[nodiscard]] bool writeBlob(int fd, const Buffer<ItemT>& source) noexcept {
return true; // dud behavior: pretend writing works...
}
};
enum class SocketPolicy {
mock,
real
};
template <SocketPolicy P>
class Socket {};
template <>
class Socket <SocketPolicy::mock> {
private:
int m_fd {42};
bool m_closed {false};
public:
template <typename Provider, template <typename> typename Buffer, typename ItemT> requires (IOProviderKind<Provider, Buffer, ItemT>)
[[nodiscard]] bool readLine(Provider& service, Buffer<ItemT>& target, ItemT delim) noexcept {
auto space_n = target.getSize();
auto done_n = 0UL;
bool found_delim = false;
while (not m_closed and space_n > 0) {
auto temp = service.readOne(m_fd);
if (temp == ItemT {}) {
m_closed = true;
continue;
}
if (temp == delim) {
found_delim = true;
break;
}
target.getPtr()[done_n] = temp;
done_n++;
space_n--;
}
return not m_closed and found_delim;
}
};
template <>
class Socket <SocketPolicy::real> {
// pretend this uses open, close, recv, send functions from a real socket API...
};
int main() {
FixedBuffer<char> buffer {};
MockIO io {"hello world\n"};
Socket<SocketPolicy::mock> socket {};
if (not socket.readLine(io, buffer, '\n')) {
return 1;
}
std::string expected_line {"hello world"};
if (expected_line != buffer.getPtr()) {
return 1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment