Kernel-bypass I/O engine on io_uring. C99, zero dependencies, no liburing.
#include <echo.h>
struct le_ring ring; le_ring_init(&ring, 256, 0); le_read(&ring, fd, buf, 4096, 0); le_ring_destroy(&ring);
-- direct mmap of SQ/CQ rings (no liburing) -- static inline prep helpers (zero call overhead) -- async submission with callback dispatch -- registered buffer pool (zero-copy) -- linked operations (read->write chains without userspace) -- SQPOLL mode (kernel-side polling, no io_uring_enter) -- mixed epoll + io_uring event loop -- cancel, timeout, flush, batched submit
make cc -std=c99 myapp.c $(pkg-config --cflags --libs libecho)
find_package(echo REQUIRED) target_link_libraries(myapp echo)
make install PREFIX=/usr/local (or cmake --install build) make dist (release tarball)
Linux 5.6+, glibc or musl. SQPOLL needs 5.11+ and CAP_SYS_NICE or ulimit -l unlimited.
Sync read:
char buf[256]; ssize_t n = le_read(&ring, fd, buf, sizeof(buf), 0);
Batched -- N reads in one syscall:
for (int i = 0; i < N; i++) { struct io_uring_sqe *s = le_get_sqe(&ring); le_prep_read(s, fd, bufs[i], sz, (off_t)(i * sz)); } le_submit_and_wait(&ring, N); // collect CQEs
Async with callbacks:
le_ring_init_async(&ring, 256, 0, 256); le_submit_read_async(&ring, fd, buf, 4096, 0, on_read, user_data); while (running) le_process(&ring, 0);
Registered buffer pool:
struct le_bufpool pool; le_bufpool_init(&ring, &pool, 64, 4096); int idx = le_bufpool_get(&pool); struct io_uring_sqe *s = le_get_sqe(&ring); le_prep_read_fixed(s, fd, pool.bufs[idx], 4096, off, idx); le_submit(&ring);
Linked read -> write chain:
struct io_uring_sqe *r = le_get_sqe(&ring); struct io_uring_sqe *w = le_get_sqe(&ring); le_link_read_then_write(r, fi, buf, sz, oi, w, fo, oo); le_submit(&ring);
docs/tutorial.md -- // read btw docs/libecho.3 -- man page: man ./docs/libecho.3
A ring is not thread-safe. One ring per thread.
MIT.