mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 08:24:32 -08:00
[6s] Add 6s shell, make channels full-duplex
This commit adds the 6s shell, and a bunch of supporting work for it. Major changes include: - New shell.yaml manifest to give 6s control of the TTY instead of srv.logger - Changes to mailbox syscalls to add max handles array size separate from input size. Also reversed the meaning of the similar data size argument in those syscalls. (Using the second arg as the max array size and the first as the current valid size allows for the auto verify code to verify handles properly, and simplifies user-side code.) - New util::unique_ptr smart pointer class similar to std::unique_ptr - New ipc::message format that uses util::unique_ptr to manage ownership and lifetimes and avoid extra copying. - The service locator protocol now supports multiple handles per entry - Channels got a major overhaul. They are now split into two VMAs, each containing a mutex, a condition, and a util::bip_buffer. The order of the VMAs determines which end of the pipe you're on. (ie, the creator swaps them before handing them to the other thread.) Their API also changed to be similar to that of util::bip_buffer, to avoid extra copies. - util::bip_buffer now keeps its state and its buffer together, so that there are no pointers. This allows multiple processes to share them in shared memory, like in channels. - The UART driver changed from keeping buffers for the serial ports to just keeping a channel, and the serial port objects read/write directly from/to the channel. Known issues: - The shell doesn't actually do anything yet. It echos its input back to the serial line and injects a prompt on new lines. - The shell is one character behind in printing back to the serial line.
This commit is contained in:
@@ -12,138 +12,177 @@
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/syslog.hh>
|
||||
#include <util/spinlock.h>
|
||||
#include <util/bip_buffer.h>
|
||||
#include <util/new.h>
|
||||
|
||||
namespace j6 {
|
||||
|
||||
static uintptr_t channel_addr = 0x6000'0000;
|
||||
static util::spinlock addr_spinlock;
|
||||
|
||||
struct channel::header
|
||||
struct channel_memory_area
|
||||
{
|
||||
size_t size;
|
||||
size_t read_index;
|
||||
size_t write_index;
|
||||
|
||||
mutex mutex;
|
||||
condition read_waiting;
|
||||
condition write_waiting;
|
||||
|
||||
uint8_t data[0];
|
||||
|
||||
inline const void* read_at() const { return &data[read_index & (size - 1)]; }
|
||||
inline void* write_at() { return &data[write_index & (size - 1)]; }
|
||||
|
||||
inline size_t read_avail() const { return write_index - read_index; }
|
||||
inline size_t write_avail() const { return size - read_avail(); }
|
||||
|
||||
inline void consume(size_t n) { read_index += n; }
|
||||
inline void commit(size_t n) { write_index += n; }
|
||||
j6::mutex mutex;
|
||||
j6::condition waiting;
|
||||
util::bip_buffer buf;
|
||||
};
|
||||
|
||||
channel *
|
||||
channel::create(size_t size)
|
||||
|
||||
static bool
|
||||
check_channel_size(size_t s)
|
||||
{
|
||||
j6_status_t result;
|
||||
j6_handle_t vma = j6_handle_invalid;
|
||||
|
||||
if (size < arch::frame_size || (size & (size - 1)) != 0) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Bad channel size: %lx", size);
|
||||
return nullptr;
|
||||
if (s < arch::frame_size || (s & (s - 1)) != 0) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Bad channel size: %lx", s);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
util::scoped_lock lock {addr_spinlock};
|
||||
uintptr_t addr = channel_addr;
|
||||
channel_addr += size * 2; // account for ring buffer virtual space doubling
|
||||
lock.release();
|
||||
|
||||
result = j6_vma_create_map(&vma, size, &addr, j6_vm_flag_write|j6_vm_flag_ring);
|
||||
static uintptr_t
|
||||
create_channel_vma(j6_handle_t &vma, size_t size)
|
||||
{
|
||||
uintptr_t addr = 0;
|
||||
j6_status_t result = j6_vma_create_map(&vma, size, &addr, j6_vm_flag_write);
|
||||
if (result != j6_status_ok) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Failed to create channel VMA. Error: %lx", result);
|
||||
return 0;
|
||||
}
|
||||
syslog(j6::logs::ipc, j6::log_level::verbose, "Created channel VMA at 0x%lx size 0x%lx", addr, size);
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
init_channel_memory_area(channel_memory_area *area, size_t size)
|
||||
{
|
||||
new (&area->mutex) j6::mutex;
|
||||
new (&area->waiting) j6::condition;
|
||||
new (&area->buf) util::bip_buffer {size - sizeof(*area)};
|
||||
}
|
||||
|
||||
|
||||
channel *
|
||||
channel::create(size_t tx_size, size_t rx_size)
|
||||
{
|
||||
if (!rx_size)
|
||||
rx_size = tx_size;
|
||||
|
||||
if (!check_channel_size(tx_size) || !check_channel_size(rx_size))
|
||||
return nullptr;
|
||||
|
||||
j6_status_t result;
|
||||
j6_handle_t tx_vma = j6_handle_invalid;
|
||||
j6_handle_t rx_vma = j6_handle_invalid;
|
||||
|
||||
uintptr_t tx_addr = create_channel_vma(tx_vma, tx_size);
|
||||
if (!tx_addr)
|
||||
return nullptr;
|
||||
|
||||
uintptr_t rx_addr = create_channel_vma(rx_vma, rx_size);
|
||||
if (!rx_addr) {
|
||||
j6_vma_unmap(tx_vma, 0);
|
||||
// TODO: Close TX handle
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
header *h = reinterpret_cast<header*>(addr);
|
||||
memset(h, 0, sizeof(*h));
|
||||
h->size = size;
|
||||
channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
|
||||
channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
|
||||
init_channel_memory_area(tx, tx_size);
|
||||
init_channel_memory_area(rx, rx_size);
|
||||
|
||||
return new channel {vma, h};
|
||||
j6::syslog(logs::ipc, log_level::info, "Created new channel with handles {%x, %x}", tx_vma, rx_vma);
|
||||
|
||||
return new channel {{tx_vma, rx_vma}, *tx, *rx};
|
||||
}
|
||||
|
||||
channel *
|
||||
channel::open(j6_handle_t vma)
|
||||
channel::open(const channel_def &def)
|
||||
{
|
||||
j6_status_t result;
|
||||
|
||||
util::scoped_lock lock {addr_spinlock};
|
||||
uintptr_t addr = channel_addr;
|
||||
|
||||
result = j6_vma_map(vma, 0, &addr, 0);
|
||||
uintptr_t tx_addr = 0;
|
||||
result = j6_vma_map(def.tx, 0, &tx_addr, 0);
|
||||
if (result != j6_status_ok) {
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
header *h = reinterpret_cast<header*>(addr);
|
||||
channel_addr += h->size;
|
||||
lock.release();
|
||||
uintptr_t rx_addr = 0;
|
||||
result = j6_vma_map(def.rx, 0, &rx_addr, 0);
|
||||
if (result != j6_status_ok) {
|
||||
j6_vma_unmap(def.tx, 0);
|
||||
// TODO: Close TX handle
|
||||
syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new channel {vma, h};
|
||||
channel_memory_area *tx = reinterpret_cast<channel_memory_area*>(tx_addr);
|
||||
channel_memory_area *rx = reinterpret_cast<channel_memory_area*>(rx_addr);
|
||||
|
||||
j6::syslog(logs::ipc, log_level::info, "Opening existing channel with handles {%x:0x%lx, %x:0x%lx}", def.tx, tx, def.rx, rx);
|
||||
return new channel { def, *tx, *rx };
|
||||
}
|
||||
|
||||
channel::channel(j6_handle_t vma, header *h) :
|
||||
m_vma {vma},
|
||||
m_size {h->size},
|
||||
m_header {h}
|
||||
channel::channel(
|
||||
const channel_def &def,
|
||||
channel_memory_area &tx,
|
||||
channel_memory_area &rx) :
|
||||
m_def {def},
|
||||
m_tx {tx},
|
||||
m_rx {rx}
|
||||
{
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel::send(const void *buffer, size_t len, bool block)
|
||||
|
||||
size_t
|
||||
channel::reserve(size_t size, uint8_t **area, bool block)
|
||||
{
|
||||
if (len > m_header->size)
|
||||
if (size > m_tx.buf.buffer_size())
|
||||
return j6_err_insufficient;
|
||||
|
||||
j6::scoped_lock lock {m_header->mutex};
|
||||
while (m_header->write_avail() < len) {
|
||||
if (!block)
|
||||
return j6_status_would_block;
|
||||
|
||||
j6::scoped_lock lock {m_tx.mutex};
|
||||
while (m_tx.buf.write_available() < size) {
|
||||
if (!block) return 0;
|
||||
lock.release();
|
||||
m_header->write_waiting.wait();
|
||||
m_tx.waiting.wait();
|
||||
lock.acquire();
|
||||
}
|
||||
|
||||
memcpy(m_header->write_at(), buffer, len);
|
||||
m_header->commit(len);
|
||||
m_header->read_waiting.wake();
|
||||
|
||||
return j6_status_ok;
|
||||
return m_tx.buf.reserve(size, area);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel::receive(void *buffer, size_t *size, bool block)
|
||||
void
|
||||
channel::commit(size_t size)
|
||||
{
|
||||
j6::scoped_lock lock {m_header->mutex};
|
||||
while (!m_header->read_avail()) {
|
||||
if (!block) {
|
||||
*size = 0;
|
||||
return j6_status_would_block;
|
||||
}
|
||||
j6::syslog(logs::ipc, log_level::spam,
|
||||
"Sending %d bytes to channel on {%x}", size, m_def.tx);
|
||||
j6::scoped_lock lock {m_tx.mutex};
|
||||
m_tx.buf.commit(size);
|
||||
if (size)
|
||||
m_tx.waiting.wake();
|
||||
}
|
||||
|
||||
size_t
|
||||
channel::get_block(uint8_t const **area, bool block) const
|
||||
{
|
||||
j6::scoped_lock lock {m_rx.mutex};
|
||||
while (!m_rx.buf.size()) {
|
||||
if (!block) return 0;
|
||||
|
||||
lock.release();
|
||||
m_header->read_waiting.wait();
|
||||
m_rx.waiting.wait();
|
||||
lock.acquire();
|
||||
}
|
||||
|
||||
size_t avail = m_header->read_avail();
|
||||
size_t read = *size > avail ? avail : *size;
|
||||
return m_rx.buf.get_block(area);
|
||||
}
|
||||
|
||||
memcpy(buffer, m_header->read_at(), read);
|
||||
m_header->consume(read);
|
||||
m_header->write_waiting.wake();
|
||||
|
||||
*size = read;
|
||||
return j6_status_ok;
|
||||
void
|
||||
channel::consume(size_t size)
|
||||
{
|
||||
j6::syslog(logs::ipc, log_level::spam,
|
||||
"Read %d bytes from channel on {%x}", size, m_def.rx);
|
||||
m_rx.buf.consume(size);
|
||||
if (size)
|
||||
m_rx.waiting.wake();
|
||||
}
|
||||
|
||||
} // namespace j6
|
||||
|
||||
@@ -10,41 +10,66 @@
|
||||
#include <j6/types.h>
|
||||
#include <util/api.h>
|
||||
|
||||
namespace util {
|
||||
class bip_buffer;
|
||||
}
|
||||
|
||||
namespace j6 {
|
||||
|
||||
/// Descriptor of VMAs for a channel.
|
||||
struct channel_def
|
||||
{
|
||||
j6_handle_t tx;
|
||||
j6_handle_t rx;
|
||||
};
|
||||
|
||||
struct channel_memory_area;
|
||||
|
||||
class API channel
|
||||
{
|
||||
public:
|
||||
/// Create a new channel of the given size.
|
||||
static channel * create(size_t size);
|
||||
/// Create a new channel.
|
||||
/// \arg tx_size Size of the transmit buffer (sending from this thread)
|
||||
/// \arg rx_size Size of the receive buffer (sending from remote thread),
|
||||
/// or 0 for equal sized buffers.
|
||||
static channel * create(size_t tx_size, size_t rx_size = 0);
|
||||
|
||||
/// Open an existing channel for which we have a VMA handle
|
||||
static channel * open(j6_handle_t vma);
|
||||
/// Open an existing channel for which we have VMA handles
|
||||
static channel * open(const channel_def &def);
|
||||
|
||||
/// Send data into the channel.
|
||||
/// \arg buffer The buffer from which to read data
|
||||
/// \arg len The number of bytes to read from `buffer`
|
||||
/// \arg block If true, block this thread if there aren't `len` bytes of space available
|
||||
j6_status_t send(const void *buffer, size_t len, bool block = true);
|
||||
/// Reserve an area of the output buffer for a write.
|
||||
/// \arg size Requested size, in bytes
|
||||
/// \arg area [out] Pointer to returned area
|
||||
/// \arg block If true, block this thread until there are size bytes free
|
||||
/// \returns Size of returned area, in bytes, or 0 on failure
|
||||
size_t reserve(size_t size, uint8_t **area, bool block = true);
|
||||
|
||||
/// Read data out of the channel.
|
||||
/// \arg buffer The buffer to receive channel data
|
||||
/// \arg size [in] The size of `buffer` [out] the amount read into `buffer`
|
||||
/// \arg block If true, block this thread if there is no data to read yet
|
||||
j6_status_t receive(void *buffer, size_t *size, bool block = true);
|
||||
/// Commit a pending write started by reserve()
|
||||
/// \arg size Amount of data used, in bytes
|
||||
void commit(size_t size);
|
||||
|
||||
/// Get the VMA handle for sharing with other processes
|
||||
j6_handle_t handle() const { return m_vma; }
|
||||
/// Get a pointer to a block of data in the buffer.
|
||||
/// \arg area [out] Pointer to the retuned area
|
||||
/// \arg block If true, block this thread until there is data available
|
||||
/// \returns Size of the returned area, in bytes
|
||||
size_t get_block(uint8_t const **area, bool block = true) const;
|
||||
|
||||
/// Mark a number of bytes as consumed, freeing buffer space
|
||||
/// \arg size Number of bytes to consume
|
||||
void consume(size_t size);
|
||||
|
||||
/// Get the channel_def for creating the remote end
|
||||
inline channel_def remote_def() const { return {m_def.rx, m_def.tx}; }
|
||||
|
||||
private:
|
||||
struct header;
|
||||
channel(
|
||||
const channel_def &def,
|
||||
channel_memory_area &tx,
|
||||
channel_memory_area &rx);
|
||||
|
||||
channel(j6_handle_t vma, header *h);
|
||||
|
||||
j6_handle_t m_vma;
|
||||
|
||||
size_t m_size;
|
||||
header *m_header;
|
||||
channel_def m_def;
|
||||
channel_memory_area &m_tx;
|
||||
channel_memory_area &m_rx;
|
||||
};
|
||||
|
||||
} // namespace j6
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <j6/protocols/service_locator.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/api.h>
|
||||
#include <util/counted.h>
|
||||
|
||||
namespace j6::proto::sl {
|
||||
|
||||
@@ -13,16 +14,16 @@ public:
|
||||
|
||||
/// Register a handle as a service with the locator.
|
||||
/// \arg proto_id The protocol this handle supports
|
||||
/// \arg handle The mailbox or channel handle
|
||||
j6_status_t register_service(uint64_t proto_id, j6_handle_t handle);
|
||||
/// \arg handles The mailbox or channel handles
|
||||
j6_status_t register_service(uint64_t proto_id, util::counted<j6_handle_t> handles);
|
||||
|
||||
/// Look up a handle with the locator service.
|
||||
/// \arg proto_id The protocol to look for
|
||||
/// \arg handle [out] The mailbox or channel handle
|
||||
j6_status_t lookup_service(uint64_t proto_id, j6_handle_t &handle);
|
||||
/// \arg handles [out] The mailbox or channel handles
|
||||
j6_status_t lookup_service(uint64_t proto_id, util::counted<j6_handle_t> &handles);
|
||||
|
||||
private:
|
||||
j6_handle_t m_service;
|
||||
};
|
||||
|
||||
} // namespace j6::proto::sl
|
||||
} // namespace j6::proto::sl
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
LOG(apic, info)
|
||||
LOG(boot, info)
|
||||
LOG(device, spam)
|
||||
LOG(device, info)
|
||||
LOG(irq, info)
|
||||
LOG(memory, verbose)
|
||||
LOG(objs, spam)
|
||||
LOG(memory, info)
|
||||
LOG(objs, info)
|
||||
LOG(paging, info)
|
||||
LOG(sched, verbose)
|
||||
LOG(syscall,verbose)
|
||||
LOG(sched, info)
|
||||
LOG(syscall,info)
|
||||
LOG(task, verbose)
|
||||
LOG(timer, info)
|
||||
LOG(ipc, spam)
|
||||
LOG(app, spam)
|
||||
LOG(proto, spam)
|
||||
LOG(srv, spam)
|
||||
LOG(proto, spam)
|
||||
LOG(srv, info)
|
||||
|
||||
@@ -13,16 +13,15 @@ client::client(j6_handle_t slp_mb) :
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
client::register_service(uint64_t proto_id, j6_handle_t handle)
|
||||
client::register_service(uint64_t proto_id, util::counted<j6_handle_t> handles)
|
||||
{
|
||||
uint64_t tag = j6_proto_sl_register;
|
||||
size_t handle_count = 1;
|
||||
size_t data = proto_id;
|
||||
size_t data_size = sizeof(proto_id);
|
||||
|
||||
j6_status_t s = j6_mailbox_call(m_service, &tag,
|
||||
&data, &data_size, data_size,
|
||||
&handle, &handle_count);
|
||||
handles.pointer, &handles.count, handles.count);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
@@ -34,31 +33,35 @@ client::register_service(uint64_t proto_id, j6_handle_t handle)
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
client::lookup_service(uint64_t proto_id, j6_handle_t &handle)
|
||||
client::lookup_service(uint64_t proto_id, util::counted<j6_handle_t> &handles)
|
||||
{
|
||||
uint64_t tag = j6_proto_sl_find;
|
||||
size_t handle_count = 1;
|
||||
size_t data = proto_id;
|
||||
size_t data_size = sizeof(proto_id);
|
||||
handle = j6_handle_invalid;
|
||||
size_t handles_size = handles.count;
|
||||
handles.count = 0;
|
||||
|
||||
j6::syslog(j6::logs::proto, j6::log_level::verbose, "Looking up service for %x", proto_id);
|
||||
j6_status_t s = j6_mailbox_call(m_service, &tag,
|
||||
&data, &data_size, data_size,
|
||||
&handle, &handle_count);
|
||||
handles.pointer, &handles.count, handles_size);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
if (s != j6_status_ok) {
|
||||
j6::syslog(j6::logs::proto, j6::log_level::error, "Received error %lx trying to call service lookup", s);
|
||||
return s;
|
||||
}
|
||||
|
||||
if (tag == j6_proto_sl_result)
|
||||
return j6_status_ok; // handle is already in `handle`
|
||||
return j6_status_ok; // handles are already in `handles`
|
||||
|
||||
else if (tag == j6_proto_base_status)
|
||||
else if (tag == j6_proto_base_status) {
|
||||
j6::syslog(j6::logs::proto, j6::log_level::warn, "Received status %lx from service lookup", data);
|
||||
return data; // contains a status
|
||||
}
|
||||
|
||||
return j6_err_unexpected;
|
||||
}
|
||||
|
||||
|
||||
} // namespace j6::proto::sl
|
||||
#endif // __j6kernel
|
||||
#endif // __j6kernel
|
||||
|
||||
@@ -21,7 +21,7 @@ client::load_file(char *path, j6_handle_t &vma, size_t &size)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
uint64_t tag = j6_proto_vfs_load;
|
||||
size_t handle_count = 1;
|
||||
size_t handle_count = 0;
|
||||
vma = j6_handle_invalid;
|
||||
|
||||
// Always need to send a big enough buffer for a status code
|
||||
@@ -38,12 +38,12 @@ client::load_file(char *path, j6_handle_t &vma, size_t &size)
|
||||
|
||||
j6_status_t s = j6_mailbox_call(m_service, &tag,
|
||||
data, &data_len, path_len,
|
||||
&vma, &handle_count);
|
||||
&vma, &handle_count, 1);
|
||||
|
||||
if (s != j6_status_ok)
|
||||
return s;
|
||||
|
||||
if (tag == j6_proto_vfs_file) {
|
||||
if (tag == j6_proto_vfs_file && handle_count == 1) {
|
||||
size = 0;
|
||||
|
||||
// Get the size into `size`
|
||||
|
||||
@@ -8,27 +8,37 @@
|
||||
|
||||
namespace util {
|
||||
|
||||
bip_buffer::bip_buffer() :
|
||||
m_start_a(0),
|
||||
m_start_b(0),
|
||||
m_size_a(0),
|
||||
m_size_b(0),
|
||||
m_size_r(0),
|
||||
m_buffer_size(0),
|
||||
m_buffer(nullptr)
|
||||
{}
|
||||
bip_buffer::bip_buffer(size_t size) :
|
||||
m_start_a {0},
|
||||
m_start_b {0},
|
||||
m_size_a {0},
|
||||
m_size_b {0},
|
||||
m_size_r {0},
|
||||
m_buffer_size {size - sizeof(bip_buffer)}
|
||||
{}
|
||||
|
||||
bip_buffer::bip_buffer(uint8_t *buffer, size_t size) :
|
||||
m_start_a(0),
|
||||
m_start_b(0),
|
||||
m_size_a(0),
|
||||
m_size_b(0),
|
||||
m_size_r(0),
|
||||
m_buffer_size(size),
|
||||
m_buffer(buffer)
|
||||
{}
|
||||
size_t
|
||||
bip_buffer::write_available() const
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
size_t bip_buffer::reserve(size_t size, void **area)
|
||||
if (m_size_r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_size_b) {
|
||||
// If B exists, we're appending there. Get space between
|
||||
// the end of B and start of A.
|
||||
return m_start_a - m_start_b - m_size_b;
|
||||
} else {
|
||||
// B doesn't exist, check the space both before and after A.
|
||||
size_t remaining = m_buffer_size - m_start_a - m_size_a;
|
||||
return (m_start_a > remaining) ? m_start_a : remaining;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
bip_buffer::reserve(size_t size, uint8_t **area)
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
@@ -84,7 +94,7 @@ void bip_buffer::commit(size_t size)
|
||||
m_start_r = m_size_r = 0;
|
||||
}
|
||||
|
||||
size_t bip_buffer::get_block(void **area) const
|
||||
size_t bip_buffer::get_block(uint8_t const **area) const
|
||||
{
|
||||
scoped_lock lock {m_lock};
|
||||
|
||||
|
||||
@@ -14,17 +14,14 @@ namespace util {
|
||||
class API bip_buffer
|
||||
{
|
||||
public:
|
||||
/// Default constructor. Creates a zero-size buffer.
|
||||
bip_buffer();
|
||||
|
||||
/// Constructor.
|
||||
bip_buffer(uint8_t *buffer, size_t size);
|
||||
bip_buffer(size_t size);
|
||||
|
||||
/// Reserve an area of buffer for a write.
|
||||
/// \arg size Requested size, in bytes
|
||||
/// \arg area [out] Pointer to returned area
|
||||
/// \returns Size of returned area, in bytes, or 0 on failure
|
||||
size_t reserve(size_t size, void **area);
|
||||
size_t reserve(size_t size, uint8_t **area);
|
||||
|
||||
/// Commit a pending write started by reserve()
|
||||
/// \arg size Amount of data used, in bytes
|
||||
@@ -33,7 +30,7 @@ public:
|
||||
/// Get a pointer to a block of data in the buffer.
|
||||
/// \arg area [out] Pointer to the retuned area
|
||||
/// \returns Size of the returned area, in bytes
|
||||
size_t get_block(void **area) const;
|
||||
size_t get_block(uint8_t const **area) const;
|
||||
|
||||
/// Mark a number of bytes as consumed, freeing buffer space
|
||||
/// \arg size Number of bytes to consume
|
||||
@@ -47,6 +44,13 @@ public:
|
||||
/// \returns Number of bytes of buffer that are free
|
||||
inline size_t free_space() const { return m_buffer_size - size(); }
|
||||
|
||||
/// Get total size of the buffer
|
||||
/// \returns Number of bytes in the buffer
|
||||
inline size_t buffer_size() const { return m_buffer_size; }
|
||||
|
||||
/// Get the current size available for a contiguous write
|
||||
size_t write_available() const;
|
||||
|
||||
private:
|
||||
size_t m_start_a;
|
||||
size_t m_start_b;
|
||||
@@ -56,9 +60,10 @@ private:
|
||||
size_t m_size_r;
|
||||
|
||||
mutable spinlock m_lock;
|
||||
|
||||
const size_t m_buffer_size;
|
||||
uint8_t * const m_buffer;
|
||||
uint8_t m_buffer[0];
|
||||
|
||||
bip_buffer() = delete;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <util/basic_types.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
@@ -92,4 +93,45 @@ private:
|
||||
size_t m_off;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class unique_ptr
|
||||
{
|
||||
public:
|
||||
unique_ptr() : m_value {nullptr} {}
|
||||
unique_ptr(T *p) : m_value {p} {}
|
||||
unique_ptr(unique_ptr &&o) { *this = util::move(o); }
|
||||
|
||||
~unique_ptr() { delete m_value; }
|
||||
|
||||
unique_ptr & operator=(unique_ptr &&o) {
|
||||
if (o.m_value == m_value) return *this;
|
||||
delete m_value;
|
||||
|
||||
m_value = o.m_value;
|
||||
o.m_value = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
unique_ptr(const unique_ptr &) = delete;
|
||||
unique_ptr & operator=(const unique_ptr &) = delete;
|
||||
|
||||
T* get() { return m_value; }
|
||||
const T* get() const { return m_value; }
|
||||
operator bool() const { return m_value; }
|
||||
bool empty() const { return m_value = nullptr; }
|
||||
|
||||
T* operator->() { return m_value; }
|
||||
const T operator->() const { return m_value; }
|
||||
|
||||
T operator*() { return *m_value; }
|
||||
const T operator*() const { return *m_value; }
|
||||
|
||||
operator T*() { return m_value; }
|
||||
operator const T*() const { return m_value; }
|
||||
|
||||
private:
|
||||
T *m_value = nullptr;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
Reference in New Issue
Block a user