lib: fs: implement memory-mapped file i/o
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "btree.h"
|
||||
#include "file.h"
|
||||
#include "interface.h"
|
||||
#include "mapping.h"
|
||||
|
||||
#include <fs/allocator.h>
|
||||
#include <fs/context.h>
|
||||
@@ -8,9 +9,16 @@
|
||||
#include <fs/inode.h>
|
||||
#include <fs/status.h>
|
||||
#include <fs/superblock.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/object.h>
|
||||
#include <mango/signal.h>
|
||||
#include <mango/task.h>
|
||||
#include <mango/vm.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TEMP_OBJECT_SIZE 0x10000
|
||||
|
||||
BTREE_DEFINE_SIMPLE_GET(struct fs_file, unsigned long, f_node, f_id, get_file);
|
||||
BTREE_DEFINE_SIMPLE_INSERT(struct fs_file, f_node, f_id, put_file);
|
||||
|
||||
@@ -18,12 +26,21 @@ struct fs_context {
|
||||
struct fs_superblock *ctx_sb;
|
||||
struct fs_allocator *ctx_alloc;
|
||||
struct btree ctx_filelist;
|
||||
kern_handle_t ctx_vm_controller;
|
||||
kern_handle_t ctx_channel;
|
||||
kern_handle_t ctx_temp_object;
|
||||
void *ctx_temp_object_buf;
|
||||
|
||||
struct fs_vtable ctx_vtable;
|
||||
};
|
||||
|
||||
struct fs_context *fs_context_create(struct fs_allocator *alloc)
|
||||
{
|
||||
kern_handle_t self, address_space;
|
||||
task_self(&self);
|
||||
task_get_address_space(self, &address_space);
|
||||
kern_handle_close(self);
|
||||
|
||||
struct fs_context *ctx = fs_alloc(alloc, sizeof *ctx);
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
@@ -31,10 +48,48 @@ struct fs_context *fs_context_create(struct fs_allocator *alloc)
|
||||
|
||||
memset(ctx, 0x0, sizeof *ctx);
|
||||
|
||||
kern_status_t status = vm_controller_create(&ctx->ctx_vm_controller);
|
||||
if (status != KERN_OK) {
|
||||
fs_free(alloc, ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = vm_object_create(
|
||||
NULL,
|
||||
0,
|
||||
TEMP_OBJECT_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&ctx->ctx_temp_object);
|
||||
if (status != KERN_OK) {
|
||||
kern_handle_close(ctx->ctx_vm_controller);
|
||||
fs_free(alloc, ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virt_addr_t temp_buffer;
|
||||
status = address_space_map(
|
||||
address_space,
|
||||
MAP_ADDRESS_ANY,
|
||||
ctx->ctx_temp_object,
|
||||
0,
|
||||
TEMP_OBJECT_SIZE,
|
||||
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER,
|
||||
&temp_buffer);
|
||||
if (status != KERN_OK) {
|
||||
kern_handle_close(ctx->ctx_temp_object);
|
||||
kern_handle_close(ctx->ctx_vm_controller);
|
||||
fs_free(alloc, ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->ctx_temp_object_buf = (void *)temp_buffer;
|
||||
ctx->ctx_alloc = alloc;
|
||||
|
||||
ctx->ctx_vtable.open = fs_msg_open;
|
||||
ctx->ctx_vtable.close = fs_msg_close;
|
||||
ctx->ctx_vtable.read = fs_msg_read;
|
||||
ctx->ctx_vtable.write = fs_msg_write;
|
||||
ctx->ctx_vtable.map = fs_msg_map;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
@@ -73,6 +128,112 @@ enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx)
|
||||
return FS_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void fs_context_set_channel(struct fs_context *ctx, kern_handle_t channel)
|
||||
{
|
||||
ctx->ctx_channel = channel;
|
||||
}
|
||||
|
||||
kern_handle_t fs_context_get_vm_controller(const struct fs_context *ctx)
|
||||
{
|
||||
return ctx->ctx_vm_controller;
|
||||
}
|
||||
|
||||
static enum fs_status handle_msg(struct fs_context *ctx)
|
||||
{
|
||||
xpc_msg_t msg;
|
||||
kern_status_t status = xpc_msg_recv_nowait(ctx->ctx_channel, &msg);
|
||||
if (status == KERN_NO_ENTRY) {
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
if (status != KERN_OK) {
|
||||
kern_logf("message recv error %d", status);
|
||||
return FS_ERR_INTERNAL_FAILURE;
|
||||
}
|
||||
|
||||
switch (msg.msg_header.hdr_interface) {
|
||||
case INTERFACE_FS:
|
||||
status = fs_context_dispatch_msg(ctx, &msg);
|
||||
break;
|
||||
default:
|
||||
kern_logf(
|
||||
"unknown message protocol %u",
|
||||
msg.msg_header.hdr_interface);
|
||||
xpc_msg_reply_error(&msg, KERN_UNSUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
static enum fs_status handle_page_request(struct fs_context *ctx)
|
||||
{
|
||||
equeue_packet_page_request_t packet;
|
||||
vm_controller_recv(ctx->ctx_vm_controller, &packet);
|
||||
struct file_mapping *mapping = (struct file_mapping *)packet.req_vmo;
|
||||
kern_logf(
|
||||
"received page request [%zx-%zx] for file %s",
|
||||
packet.req_offset,
|
||||
packet.req_offset + packet.req_length,
|
||||
mapping->m_file->f_dent->d_name);
|
||||
|
||||
size_t length = packet.req_length;
|
||||
if (length > TEMP_OBJECT_SIZE) {
|
||||
length = TEMP_OBJECT_SIZE;
|
||||
}
|
||||
|
||||
char *dst = ctx->ctx_temp_object_buf;
|
||||
xpc_buffer_t buf = XPC_LOCAL_BUFFER_OUT(dst, TEMP_OBJECT_SIZE);
|
||||
enum fs_status status = fs_file_read_at(
|
||||
mapping->m_file,
|
||||
&buf,
|
||||
mapping->m_file_offset + packet.req_offset,
|
||||
length);
|
||||
if (status != FS_SUCCESS) {
|
||||
kern_logf("map-read failed with code %d", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
vm_controller_supply_pages(
|
||||
ctx->ctx_vm_controller,
|
||||
mapping->m_vmo,
|
||||
packet.req_offset,
|
||||
ctx->ctx_temp_object,
|
||||
0,
|
||||
packet.req_length);
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
enum fs_status fs_context_handle_request(struct fs_context *ctx)
|
||||
{
|
||||
kern_wait_item_t waiters[] = {
|
||||
{
|
||||
.w_handle = ctx->ctx_channel,
|
||||
.w_waitfor = CHANNEL_SIGNAL_MSG_RECEIVED,
|
||||
},
|
||||
{
|
||||
.w_handle = ctx->ctx_vm_controller,
|
||||
.w_waitfor = VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED,
|
||||
},
|
||||
};
|
||||
const size_t nr_waiters = sizeof waiters / sizeof waiters[0];
|
||||
|
||||
kern_status_t kstatus = kern_object_wait(waiters, nr_waiters);
|
||||
if (kstatus != KERN_OK) {
|
||||
return FS_ERR_INTERNAL_FAILURE;
|
||||
}
|
||||
|
||||
if (waiters[0].w_observed & CHANNEL_SIGNAL_MSG_RECEIVED) {
|
||||
return handle_msg(ctx);
|
||||
}
|
||||
|
||||
if (waiters[1].w_observed & VM_CONTROLLER_SIGNAL_REQUEST_RECEIVED) {
|
||||
return handle_page_request(ctx);
|
||||
}
|
||||
|
||||
return FS_SUCCESS;
|
||||
}
|
||||
|
||||
struct fs_file *fs_context_open_file(struct fs_context *ctx, unsigned long id)
|
||||
{
|
||||
struct fs_file *f = get_file(&ctx->ctx_filelist, id);
|
||||
|
||||
@@ -25,9 +25,22 @@ enum fs_status fs_file_read(
|
||||
return status;
|
||||
}
|
||||
|
||||
enum fs_status fs_file_write(
|
||||
enum fs_status fs_file_read_at(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
off_t offset,
|
||||
size_t count)
|
||||
{
|
||||
if (!f->f_ops || !f->f_ops->f_read) {
|
||||
return FS_ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return f->f_ops->f_read(f, buf, count, &offset);
|
||||
}
|
||||
|
||||
enum fs_status fs_file_write(
|
||||
struct fs_file *f,
|
||||
const struct xpc_buffer *buf,
|
||||
size_t count)
|
||||
{
|
||||
if (!f->f_ops || !f->f_ops->f_write) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _FS_FILE_H_
|
||||
|
||||
#include "btree.h"
|
||||
#include "queue.h"
|
||||
|
||||
#include <fs/dentry.h>
|
||||
#include <fs/file.h>
|
||||
@@ -18,6 +19,8 @@ struct fs_file {
|
||||
off_t f_seek;
|
||||
struct fs_inode *f_inode;
|
||||
struct fs_dentry *f_dent;
|
||||
|
||||
struct queue f_mappings;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,12 @@ extern enum fs_status fs_context_mount_filesystem(
|
||||
enum fs_mount_flags flags);
|
||||
extern enum fs_status fs_context_unmount_filesystem(struct fs_context *ctx);
|
||||
|
||||
extern void fs_context_set_channel(
|
||||
struct fs_context *ctx,
|
||||
kern_handle_t channel);
|
||||
extern kern_handle_t fs_context_get_vm_controller(const struct fs_context *ctx);
|
||||
extern enum fs_status fs_context_handle_request(struct fs_context *ctx);
|
||||
|
||||
extern struct fs_file *fs_context_open_file(
|
||||
struct fs_context *ctx,
|
||||
unsigned long id);
|
||||
|
||||
@@ -27,9 +27,14 @@ extern enum fs_status fs_file_read(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
size_t count);
|
||||
extern enum fs_status fs_file_write(
|
||||
extern enum fs_status fs_file_read_at(
|
||||
struct fs_file *f,
|
||||
struct xpc_buffer *buf,
|
||||
off_t offset,
|
||||
size_t count);
|
||||
extern enum fs_status fs_file_write(
|
||||
struct fs_file *f,
|
||||
const struct xpc_buffer *buf,
|
||||
size_t count);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,9 +17,8 @@ extern kern_status_t fs_msg_open(
|
||||
int *out_err,
|
||||
void *arg);
|
||||
extern kern_status_t fs_msg_close(
|
||||
const struct msg_endpoint *sender,
|
||||
const char *path,
|
||||
int flags,
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
int *out_err,
|
||||
void *arg);
|
||||
|
||||
@@ -31,5 +30,21 @@ extern kern_status_t fs_msg_read(
|
||||
size_t *out_nr_read,
|
||||
xpc_buffer_t *out_data,
|
||||
void *arg);
|
||||
extern kern_status_t fs_msg_write(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
const xpc_buffer_t *data,
|
||||
int *out_err,
|
||||
size_t *out_nr_written,
|
||||
void *arg);
|
||||
|
||||
extern kern_status_t fs_msg_map(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
int prot,
|
||||
int flags,
|
||||
int *out_err,
|
||||
kern_handle_t *out_vmo,
|
||||
void *arg);
|
||||
|
||||
#endif
|
||||
|
||||
14
lib/libfs/interface/close.c
Normal file
14
lib/libfs/interface/close.c
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "../interface.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <mango/status.h>
|
||||
|
||||
extern kern_status_t fs_msg_close(
|
||||
xpc_context_t *ctx,
|
||||
const xpc_endpoint_t *sender,
|
||||
int *out_err,
|
||||
void *arg)
|
||||
{
|
||||
*out_err = ENOSYS;
|
||||
return KERN_OK;
|
||||
}
|
||||
74
lib/libfs/interface/map.c
Normal file
74
lib/libfs/interface/map.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "../file.h"
|
||||
#include "../mapping.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fs/context.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/status.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/log.h>
|
||||
#include <mango/vm.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
extern kern_status_t fs_msg_map(
|
||||
xpc_context_t *xpc,
|
||||
const xpc_endpoint_t *sender,
|
||||
int prot,
|
||||
int flags,
|
||||
int *out_err,
|
||||
kern_handle_t *out_vmo,
|
||||
void *arg)
|
||||
{
|
||||
struct fs_context *ctx = arg;
|
||||
struct fs_file *f = fs_context_get_file(ctx, sender->e_port);
|
||||
if (!f) {
|
||||
*out_err = EBADF;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
struct file_mapping *mapping = fs_context_alloc(ctx, sizeof *mapping);
|
||||
if (!mapping) {
|
||||
*out_err = ENOMEM;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
kern_logf("mapping file %s", f->f_dent->d_name);
|
||||
vm_prot_t vm_prot = VM_PROT_USER;
|
||||
if (prot & PROT_READ) {
|
||||
vm_prot |= VM_PROT_READ;
|
||||
}
|
||||
|
||||
if (prot & PROT_WRITE) {
|
||||
vm_prot |= VM_PROT_WRITE;
|
||||
}
|
||||
|
||||
if (prot & PROT_EXEC) {
|
||||
vm_prot |= VM_PROT_EXEC;
|
||||
}
|
||||
|
||||
kern_handle_t vmo = KERN_HANDLE_INVALID;
|
||||
kern_status_t status = vm_controller_create_object(
|
||||
fs_context_get_vm_controller(ctx),
|
||||
f->f_dent->d_name,
|
||||
strlen(f->f_dent->d_name),
|
||||
(equeue_key_t)mapping,
|
||||
f->f_inode->i_size,
|
||||
vm_prot,
|
||||
&vmo);
|
||||
|
||||
if (status != KERN_OK) {
|
||||
fs_context_free(ctx, mapping);
|
||||
*out_err = __errno_from_kern_status(status);
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
mapping->m_file = f;
|
||||
mapping->m_file_offset = 0;
|
||||
kern_handle_duplicate(vmo, &mapping->m_vmo);
|
||||
queue_push_back(&f->f_mappings, &mapping->m_entry);
|
||||
|
||||
*out_err = SUCCESS;
|
||||
*out_vmo = vmo;
|
||||
return KERN_OK;
|
||||
}
|
||||
29
lib/libfs/interface/write.c
Normal file
29
lib/libfs/interface/write.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <errno.h>
|
||||
#include <fs/context.h>
|
||||
#include <fs/file.h>
|
||||
#include <fs/status.h>
|
||||
|
||||
extern kern_status_t fs_msg_write(
|
||||
xpc_context_t *xpc,
|
||||
const xpc_endpoint_t *sender,
|
||||
const xpc_buffer_t *data,
|
||||
int *out_err,
|
||||
size_t *out_nr_written,
|
||||
void *arg)
|
||||
{
|
||||
struct fs_context *ctx = arg;
|
||||
struct fs_file *f = fs_context_get_file(ctx, sender->e_port);
|
||||
if (!f) {
|
||||
*out_err = EBADF;
|
||||
return KERN_OK;
|
||||
}
|
||||
|
||||
size_t start = fs_file_get_cursor(f);
|
||||
enum fs_status status = fs_file_write(f, data, data->buf_len);
|
||||
size_t end = fs_file_get_cursor(f);
|
||||
|
||||
*out_err = fs_status_to_errno(status);
|
||||
*out_nr_written = end - start;
|
||||
|
||||
return KERN_OK;
|
||||
}
|
||||
17
lib/libfs/mapping.h
Normal file
17
lib/libfs/mapping.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef MAPPING_H_
|
||||
#define MAPPING_H_
|
||||
|
||||
#include "queue.h"
|
||||
|
||||
#include <mango/types.h>
|
||||
|
||||
struct fs_file;
|
||||
|
||||
struct file_mapping {
|
||||
struct fs_file *m_file;
|
||||
kern_handle_t m_vmo;
|
||||
off_t m_file_offset;
|
||||
struct queue_entry m_entry;
|
||||
};
|
||||
|
||||
#endif
|
||||
138
lib/libfs/queue.c
Normal file
138
lib/libfs/queue.c
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "queue.h"
|
||||
|
||||
size_t queue_length(struct queue *q)
|
||||
{
|
||||
size_t i = 0;
|
||||
struct queue_entry *x = q->q_first;
|
||||
while (x) {
|
||||
i++;
|
||||
x = x->qe_next;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void queue_insert_before(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *before)
|
||||
{
|
||||
struct queue_entry *x = before->qe_prev;
|
||||
if (x) {
|
||||
x->qe_next = entry;
|
||||
} else {
|
||||
q->q_first = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = x;
|
||||
|
||||
before->qe_prev = entry;
|
||||
entry->qe_next = before;
|
||||
}
|
||||
|
||||
void queue_insert_after(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *after)
|
||||
{
|
||||
struct queue_entry *x = after->qe_next;
|
||||
if (x) {
|
||||
x->qe_prev = entry;
|
||||
} else {
|
||||
q->q_last = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = x;
|
||||
|
||||
after->qe_next = entry;
|
||||
entry->qe_prev = after;
|
||||
}
|
||||
|
||||
void queue_push_front(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (q->q_first) {
|
||||
q->q_first->qe_prev = entry;
|
||||
}
|
||||
|
||||
entry->qe_next = q->q_first;
|
||||
entry->qe_prev = NULL;
|
||||
|
||||
q->q_first = entry;
|
||||
|
||||
if (!q->q_last) {
|
||||
q->q_last = entry;
|
||||
}
|
||||
}
|
||||
|
||||
void queue_push_back(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (q->q_last) {
|
||||
q->q_last->qe_next = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = q->q_last;
|
||||
entry->qe_next = NULL;
|
||||
|
||||
q->q_last = entry;
|
||||
|
||||
if (!q->q_first) {
|
||||
q->q_first = entry;
|
||||
}
|
||||
}
|
||||
|
||||
struct queue_entry *queue_pop_front(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_first;
|
||||
if (x) {
|
||||
queue_delete(q, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
struct queue_entry *queue_pop_back(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_last;
|
||||
if (x) {
|
||||
queue_delete(q, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void queue_delete(struct queue *q, struct queue_entry *entry)
|
||||
{
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry == q->q_first) {
|
||||
q->q_first = q->q_first->qe_next;
|
||||
}
|
||||
|
||||
if (entry == q->q_last) {
|
||||
q->q_last = q->q_last->qe_prev;
|
||||
}
|
||||
|
||||
if (entry->qe_next) {
|
||||
entry->qe_next->qe_prev = entry->qe_prev;
|
||||
}
|
||||
|
||||
if (entry->qe_prev) {
|
||||
entry->qe_prev->qe_next = entry->qe_next;
|
||||
}
|
||||
|
||||
entry->qe_next = entry->qe_prev = NULL;
|
||||
}
|
||||
|
||||
void queue_delete_all(struct queue *q)
|
||||
{
|
||||
struct queue_entry *x = q->q_first;
|
||||
while (x) {
|
||||
struct queue_entry *next = x->qe_next;
|
||||
x->qe_next = x->qe_prev = NULL;
|
||||
x = next;
|
||||
}
|
||||
|
||||
q->q_first = q->q_last = NULL;
|
||||
}
|
||||
100
lib/libfs/queue.h
Normal file
100
lib/libfs/queue.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef QUEUE_H_
|
||||
#define QUEUE_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define QUEUE_CONTAINER(t, m, v) \
|
||||
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
|
||||
|
||||
#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL})
|
||||
#define QUEUE_ENTRY_INIT \
|
||||
((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL})
|
||||
|
||||
#define queue_foreach(iter_type, iter_name, queue_name, node_member) \
|
||||
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_first(queue_name)); \
|
||||
iter_name; \
|
||||
iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_next(&((iter_name)->node_member))))
|
||||
|
||||
#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \
|
||||
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_last(queue_name)); \
|
||||
iter_name; \
|
||||
iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||
iter_type, \
|
||||
node_member, \
|
||||
queue_prev(&((iter_name)->node_member))))
|
||||
|
||||
struct queue_entry {
|
||||
struct queue_entry *qe_next;
|
||||
struct queue_entry *qe_prev;
|
||||
};
|
||||
|
||||
struct queue {
|
||||
struct queue_entry *q_first;
|
||||
struct queue_entry *q_last;
|
||||
};
|
||||
|
||||
static inline void queue_init(struct queue *q)
|
||||
{
|
||||
memset(q, 0x00, sizeof *q);
|
||||
}
|
||||
static inline bool queue_empty(struct queue *q)
|
||||
{
|
||||
return q->q_first == NULL;
|
||||
}
|
||||
|
||||
static inline struct queue_entry *queue_first(struct queue *q)
|
||||
{
|
||||
return q->q_first;
|
||||
}
|
||||
static inline struct queue_entry *queue_last(struct queue *q)
|
||||
{
|
||||
return q->q_last;
|
||||
}
|
||||
static inline struct queue_entry *queue_next(struct queue_entry *entry)
|
||||
{
|
||||
return entry->qe_next;
|
||||
}
|
||||
static inline struct queue_entry *queue_prev(struct queue_entry *entry)
|
||||
{
|
||||
return entry->qe_prev;
|
||||
}
|
||||
|
||||
extern size_t queue_length(struct queue *q);
|
||||
|
||||
extern void queue_insert_before(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *before);
|
||||
extern void queue_insert_after(
|
||||
struct queue *q,
|
||||
struct queue_entry *entry,
|
||||
struct queue_entry *after);
|
||||
|
||||
extern void queue_push_front(struct queue *q, struct queue_entry *entry);
|
||||
extern void queue_push_back(struct queue *q, struct queue_entry *entry);
|
||||
|
||||
extern struct queue_entry *queue_pop_front(struct queue *q);
|
||||
extern struct queue_entry *queue_pop_back(struct queue *q);
|
||||
|
||||
extern void queue_delete(struct queue *q, struct queue_entry *entry);
|
||||
extern void queue_delete_all(struct queue *q);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user