General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

163 lines
4.0 KiB

#pragma once
#include "gp/algorithm/move.hpp"
#include "gp/allocator/aggregator.hpp"
#include "gp/array.hpp"
#include "gp/vfs/fs_types.hpp"
namespace gp {
template<typename error_function, typename pop_function>
class self_filling_channel{
error_function on_error;
pop_function filler;
gp::buffer<char>::associated_iterator push(gp::buffer<char> input) {
return filler(input);
}
gp::buffer<char>::associated_iterator pop(gp::buffer<char> dest) {
on_error();
return dest.begin();
}
};
template<typename error_function, typename push_function>
class input_channel{
error_function on_error;
push_function filler;
gp::buffer<char>::associated_iterator push(gp::buffer<char> input) {
on_error();
return input.begin();
}
gp::buffer<char>::associated_iterator pop(gp::buffer<char> dest) {
return filler(dest);
}
};
template<size_t size, size_t count = 1, bool whole_messages = true, bool zero_out = true>
class channel {
gp::array<size_t, count> message_sizes;
gp::array<
gp::array<char, size>,
count
> messages;
size_t r_offset = 0;
size_t w_offset = 0;
[[nodiscard]] size_t write_one(size_t loc, gp::buffer<char> message) {
auto cond = message.size();
size_t idx = 0;
for(; idx < cond; ++idx) {
messages[loc][idx] = message[idx];
}
message_sizes[loc] = message.size();
w_offset += 1;
if constexpr (count != 1) w_offset %= size;
if constexpr (zero_out) {
auto ret = idx;
for(; idx < messages[loc].size(); ++idx) {
messages[loc] = 0;
}
return ret;
}
return idx;
}
[[nodiscard]] size_t read_one(size_t loc, gp::buffer<char> message) {
size_t ret;
size_t idx = 0;
size_t trailer = 0;
if(message_sizes[loc] < message.size())
{
for(; idx < message_sizes[loc]; ++idx) {
message[idx] = messages[loc][idx];
}
r_offset += 1;
if constexpr (count != 1) w_offset %= size;
ret = idx;
} else {
for(; idx < message.size(); ++idx) {
message[idx] = messages[loc][idx];
}
ret = idx;
for(; idx < message_sizes[loc]; ++idx, ++trailer)
{
message[trailer] = message[idx];
}
message_sizes[loc] = trailer;
}
if constexpr (zero_out) {
if(trailer == 0) {
message_sizes[loc] = 0;
}
for(; trailer < messages[loc].size(); ++trailer)
{
messages[loc][trailer] = 0;
}
}
return ret;
}
public:
channel() {}
gp::buffer<char>::associated_iterator push(gp::buffer<char> input) {
if constexpr (whole_messages) {
if(input.size() > size) {
return input.begin();
}
}
if constexpr (count == 1) {
if(w_offset == r_offset) {
auto to_write = gp::min(input.size(), size);
write_one(
0,
gp::buffer<char>(input.begin().data, to_write)
);
return input.begin()+to_write;
} else {
return input.begin();
}
} else {
auto next_w = (w_offset+1)%count;
auto to_consume = input;
while(to_consume.size() && (next_w == r_offset)) {
auto to_write = gp::min(to_consume.size(), size);
to_write = write_one(
w_offset,
to_consume.trim_start(to_write)
);
to_consume = to_consume.trim_start(to_write);
next_w = (w_offset+1)%count;
}
return to_consume.begin();
}
}
gp::buffer<char>::associated_iterator pop(gp::buffer<char> dest) {
if(r_offset == w_offset) {
return dest.begin();
}
size_t idx;
if constexpr (count == 1) {
idx = 0;
} else {
idx = r_offset;
}
if constexpr (whole_messages) {
if(message_sizes[idx] > dest.size()) {
return dest.begin();
}
auto sz = gp::min(message_sizes[idx], dest.size());
read_one(idx, gp::buffer<char>(dest.begin(), sz));
return dest.begin()+sz;
}
auto to_fill = dest;
do{
auto to_read = gp::min(to_fill.size(), size);
to_read = read_one(
r_offset,
to_fill.trim_start(to_read)
);
to_fill = to_fill.trim_start(to_read);
}while(r_offset != w_offset && to_fill.size());
return to_fill.begin();
}
};
}