libc: io: implement file io buffering and internal locking for concurrency
This commit is contained in:
@@ -15,10 +15,13 @@ int fclose(struct __opaque_file *stream)
|
||||
|
||||
close(stream->f_fd);
|
||||
|
||||
if (stream->f_buf) {
|
||||
free(stream->f_buf);
|
||||
if (stream->f_buf.buf_ptr) {
|
||||
free(stream->f_buf.buf_ptr);
|
||||
}
|
||||
|
||||
if (!(stream->f_flags & FILE_STATIC)) {
|
||||
free(stream);
|
||||
}
|
||||
|
||||
free(stream);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "file.h"
|
||||
|
||||
int ferr(struct __opaque_file *stream)
|
||||
int ferror(struct __opaque_file *stream)
|
||||
{
|
||||
return (stream->f_flags & FILE_ERR) != 0;
|
||||
}
|
||||
+12
-16
@@ -3,29 +3,25 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int fgetc(struct __opaque_file *stream)
|
||||
int __fgetc(struct __opaque_file *stream)
|
||||
{
|
||||
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
||||
return EOF;
|
||||
}
|
||||
|
||||
size_t available = __libc_file_available(stream);
|
||||
if (available == 0) {
|
||||
int err = __libc_file_refill(stream);
|
||||
if (err != SUCCESS) {
|
||||
__set_errno(err);
|
||||
stream->f_flags |= FILE_ERR;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
available = __libc_file_available(stream);
|
||||
}
|
||||
|
||||
if (available == 0) {
|
||||
stream->f_flags |= FILE_EOF;
|
||||
char c = 0;
|
||||
int ret = __libc_file_read(stream, &c, 1);
|
||||
if (ret < 1) {
|
||||
return EOF;
|
||||
}
|
||||
|
||||
char c = stream->f_buf[stream->f_buf_readptr++];
|
||||
return c;
|
||||
}
|
||||
|
||||
int fgetc(struct __opaque_file *stream)
|
||||
{
|
||||
__libc_file_lock(stream);
|
||||
int ret = __fgetc(stream);
|
||||
__libc_file_unlock(stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ char *fgets(
|
||||
int count,
|
||||
struct __opaque_file *restrict stream)
|
||||
{
|
||||
__libc_file_lock(stream);
|
||||
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
||||
__libc_file_unlock(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -28,13 +30,16 @@ char *fgets(
|
||||
}
|
||||
}
|
||||
|
||||
if (ferr(stream)) {
|
||||
if (ferror(stream)) {
|
||||
__libc_file_unlock(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (feof(stream) && i == 0) {
|
||||
__libc_file_unlock(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__libc_file_unlock(stream);
|
||||
return str;
|
||||
}
|
||||
|
||||
+299
-46
@@ -1,76 +1,329 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <mango/futex.h>
|
||||
#include <mango/log.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DEFAULT_BUFFER_SIZE 4096
|
||||
#define OP_READ 1
|
||||
#define OP_WRITE 2
|
||||
|
||||
#define FILE_UNUSABLE(f) (((f)->f_flags & (FILE_EOF | FILE_ERR)) != 0)
|
||||
#define FILE_UNUSABLE(f) (((f)->f_flags & (FILE_EOF | FILE_ERR)) != 0)
|
||||
|
||||
int __libc_file_refill(struct __opaque_file *f)
|
||||
static long clear_buf(struct __opaque_file *f)
|
||||
{
|
||||
f->f_buf_datalen = 0;
|
||||
f->f_buf_readptr = 0;
|
||||
__libc_ringbuf_clear(&f->f_buf);
|
||||
lseek(f->f_fd, f->f_seek, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (f->f_fd < 0) {
|
||||
return EBADF;
|
||||
static long refill_buf(struct __opaque_file *f)
|
||||
{
|
||||
kern_tracef("refill");
|
||||
char *buf = NULL;
|
||||
size_t available = 0;
|
||||
long r = 0;
|
||||
size_t nr_read = 0;
|
||||
|
||||
while (1) {
|
||||
__libc_ringbuf_get_write_buffer(&f->f_buf, &buf, &available);
|
||||
if (available == 0) {
|
||||
__libc_ringbuf_put_write_buffer(
|
||||
&f->f_buf,
|
||||
&buf,
|
||||
&available);
|
||||
break;
|
||||
}
|
||||
|
||||
kern_tracef("read(%d, %p, %zu)", f->f_fd, buf, available);
|
||||
r = read(f->f_fd, buf, available);
|
||||
if (r <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
available = r;
|
||||
nr_read += r;
|
||||
__libc_ringbuf_put_write_buffer(&f->f_buf, &buf, &available);
|
||||
}
|
||||
|
||||
if (f->f_flags & FILE_ERR) {
|
||||
return EIO;
|
||||
if (nr_read > 0) {
|
||||
return nr_read;
|
||||
}
|
||||
|
||||
if (f->f_flags & FILE_EOF) {
|
||||
return SUCCESS;
|
||||
if (r < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!f->f_buf) {
|
||||
f->f_buf_max = DEFAULT_BUFFER_SIZE;
|
||||
f->f_buf = malloc(f->f_buf_max);
|
||||
if (!f->f_buf) {
|
||||
f->f_buf_max = 0;
|
||||
return ENOMEM;
|
||||
return nr_read;
|
||||
}
|
||||
|
||||
static long flush_buf(struct __opaque_file *f)
|
||||
{
|
||||
const char *buf = NULL;
|
||||
size_t available = 0;
|
||||
long r = 0;
|
||||
size_t nr_written = 0;
|
||||
|
||||
while (1) {
|
||||
__libc_ringbuf_get_read_buffer(&f->f_buf, &buf, &available);
|
||||
if (available == 0) {
|
||||
__libc_ringbuf_put_read_buffer(
|
||||
&f->f_buf,
|
||||
&buf,
|
||||
&available);
|
||||
break;
|
||||
}
|
||||
|
||||
r = write(f->f_fd, buf, available);
|
||||
kern_tracef(
|
||||
"write(%d, %p, %zu) = %ld",
|
||||
f->f_fd,
|
||||
buf,
|
||||
available,
|
||||
r);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
available = r;
|
||||
nr_written += r;
|
||||
__libc_ringbuf_put_read_buffer(&f->f_buf, &buf, &available);
|
||||
}
|
||||
|
||||
if (nr_written > 0) {
|
||||
return nr_written;
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __libc_file_lock(struct __opaque_file *f)
|
||||
{
|
||||
kern_futex_t expected = 0;
|
||||
while (1) {
|
||||
kern_tracef("lock=%u (%p)", f->f_lock, f);
|
||||
expected = 0;
|
||||
if (__atomic_compare_exchange_n(
|
||||
&f->f_lock,
|
||||
&expected,
|
||||
1,
|
||||
0,
|
||||
__ATOMIC_ACQUIRE,
|
||||
__ATOMIC_ACQUIRE)) {
|
||||
kern_tracef("locked=%u (%p)", f->f_lock, f);
|
||||
return;
|
||||
}
|
||||
|
||||
kern_tracef("wait=%u (%p)", expected, f);
|
||||
futex_wait(&f->f_lock, 1, FUTEX_PRIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
void __libc_file_unlock(struct __opaque_file *f)
|
||||
{
|
||||
f->f_lock = 0;
|
||||
futex_wake(&f->f_lock, 1, FUTEX_PRIVATE);
|
||||
kern_tracef("unlocked=%u (%p)", f->f_lock, f);
|
||||
}
|
||||
|
||||
static long read_buf(struct __opaque_file *f, void *out, size_t count)
|
||||
{
|
||||
kern_tracef("read_buf(%p, %zu)", out, count);
|
||||
char *dest = out;
|
||||
size_t nr_read = 0;
|
||||
long r = 0;
|
||||
|
||||
while (nr_read < count) {
|
||||
r = __libc_ringbuf_read(
|
||||
&f->f_buf,
|
||||
dest + nr_read,
|
||||
count - nr_read);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
nr_read += r;
|
||||
|
||||
if (r == 0) {
|
||||
r = refill_buf(f);
|
||||
if (r <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long r = read(f->f_fd, f->f_buf, f->f_buf_max);
|
||||
if (r < 0) {
|
||||
return -r;
|
||||
return r;
|
||||
}
|
||||
|
||||
f->f_buf_datalen = r;
|
||||
return SUCCESS;
|
||||
return nr_read;
|
||||
}
|
||||
|
||||
int __libc_file_read(struct __opaque_file *f, void *out, size_t count)
|
||||
static long read_nobuf(struct __opaque_file *f, void *out, size_t count)
|
||||
{
|
||||
if (f->f_fd < 0) {
|
||||
return EBADF;
|
||||
}
|
||||
|
||||
if (f->f_flags & FILE_ERR) {
|
||||
return EIO;
|
||||
}
|
||||
|
||||
if (f->f_flags & FILE_EOF) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
size_t available = f->f_buf_datalen - f->f_buf_readptr;
|
||||
if (count > available) {
|
||||
count = available;
|
||||
}
|
||||
|
||||
memcpy(out, f->f_buf + f->f_buf_readptr, count);
|
||||
|
||||
f->f_buf_readptr += count;
|
||||
|
||||
return count;
|
||||
kern_tracef("read_nobuf");
|
||||
return read(f->f_fd, out, count);
|
||||
}
|
||||
|
||||
size_t __libc_file_available(struct __opaque_file *f)
|
||||
long __libc_file_read(struct __opaque_file *f, void *out, size_t count)
|
||||
{
|
||||
return f->f_buf_datalen - f->f_buf_readptr;
|
||||
long ret = 0;
|
||||
if (f->f_prev != OP_READ) {
|
||||
ret = flush_buf(f);
|
||||
clear_buf(f);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
return __set_errno(ret);
|
||||
}
|
||||
|
||||
switch (f->f_buffer_mode) {
|
||||
case _IOFBF:
|
||||
case _IOLBF:
|
||||
ret = read_buf(f, out, count);
|
||||
break;
|
||||
case _IONBF:
|
||||
ret = read_nobuf(f, out, count);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
f->f_prev = OP_READ;
|
||||
|
||||
if (ret < 0) {
|
||||
f->f_flags |= FILE_ERR;
|
||||
return __set_errno(-ret);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
f->f_flags |= FILE_EOF;
|
||||
}
|
||||
|
||||
f->f_seek += ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long write_fullbuf(struct __opaque_file *f, const void *p, size_t count)
|
||||
{
|
||||
const char *src = p;
|
||||
size_t nr_written = 0;
|
||||
long r = 0;
|
||||
|
||||
while (nr_written < count) {
|
||||
r = __libc_ringbuf_write(
|
||||
&f->f_buf,
|
||||
src + nr_written,
|
||||
count - nr_written);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
nr_written += r;
|
||||
|
||||
if (r == 0) {
|
||||
r = flush_buf(f);
|
||||
if (r <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return nr_written;
|
||||
}
|
||||
|
||||
static long write_linebuf(
|
||||
struct __opaque_file *f,
|
||||
const void *out,
|
||||
size_t count)
|
||||
{
|
||||
const char *s = out;
|
||||
size_t nr_written = 0;
|
||||
long r = 0;
|
||||
|
||||
while (nr_written < count) {
|
||||
char c = s[nr_written];
|
||||
|
||||
r = __libc_ringbuf_write(&f->f_buf, &c, 1);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
nr_written += r;
|
||||
|
||||
if (r == 0 || c == '\n') {
|
||||
r = flush_buf(f);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return nr_written;
|
||||
}
|
||||
|
||||
static long write_nobuf(struct __opaque_file *f, const void *out, size_t count)
|
||||
{
|
||||
return write(f->f_fd, out, count);
|
||||
}
|
||||
|
||||
long __libc_file_write(struct __opaque_file *f, const void *buf, size_t count)
|
||||
{
|
||||
if (f->f_lock != 1) {
|
||||
kern_tracef("file is not locked!!!");
|
||||
}
|
||||
long ret = 0;
|
||||
if (f->f_prev != OP_WRITE) {
|
||||
ret = clear_buf(f);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
return __set_errno(ret);
|
||||
}
|
||||
|
||||
switch (f->f_buffer_mode) {
|
||||
case _IOFBF:
|
||||
ret = write_fullbuf(f, buf, count);
|
||||
break;
|
||||
case _IOLBF:
|
||||
ret = write_linebuf(f, buf, count);
|
||||
break;
|
||||
case _IONBF:
|
||||
ret = write_nobuf(f, buf, count);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
f->f_prev = OP_WRITE;
|
||||
|
||||
if (ret < 0) {
|
||||
f->f_flags |= FILE_ERR;
|
||||
return __set_errno(-ret);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
f->f_flags |= FILE_EOF;
|
||||
}
|
||||
|
||||
f->f_seek += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
#ifndef _LIBC_IO_FILE_H_
|
||||
#define _LIBC_IO_FILE_H_
|
||||
|
||||
#include "ringbuf.h"
|
||||
|
||||
#include <mango/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
enum file_flags {
|
||||
FILE_EOF = 0x01u,
|
||||
FILE_ERR = 0x02u,
|
||||
FILE_BIN = 0x04u,
|
||||
FILE_STATIC = 0x08u,
|
||||
};
|
||||
|
||||
struct __opaque_file {
|
||||
enum file_flags f_flags;
|
||||
kern_futex_t f_lock;
|
||||
int f_prev;
|
||||
int f_buffer_mode;
|
||||
int f_fd;
|
||||
char f_unget;
|
||||
char *f_buf;
|
||||
size_t f_buf_datalen, f_buf_max;
|
||||
size_t f_buf_readptr;
|
||||
/* the current seek position of the buffered data (i.e. the offset of
|
||||
* the data that would be returned by reading f_buf). */
|
||||
size_t f_seek;
|
||||
struct ringbuf f_buf;
|
||||
};
|
||||
|
||||
extern int __libc_file_refill(struct __opaque_file *f);
|
||||
extern int __libc_file_read(struct __opaque_file *f, void *out, size_t count);
|
||||
extern size_t __libc_file_available(struct __opaque_file *f);
|
||||
extern void __libc_file_lock(struct __opaque_file *f);
|
||||
extern void __libc_file_unlock(struct __opaque_file *f);
|
||||
|
||||
extern long __libc_file_read(struct __opaque_file *f, void *p, size_t count);
|
||||
extern long __libc_file_write(
|
||||
struct __opaque_file *f,
|
||||
const void *p,
|
||||
size_t count);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -143,6 +143,12 @@ struct __opaque_file *fopen(const char *path, const char *mode)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fflags & FILE_BIN) {
|
||||
out->f_buffer_mode = _IOFBF;
|
||||
} else {
|
||||
out->f_buffer_mode = _IOLBF;
|
||||
}
|
||||
|
||||
out->f_fd = fd;
|
||||
out->f_flags = fflags;
|
||||
__set_errno(SUCCESS);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "file.h"
|
||||
|
||||
int fprintf(struct __opaque_file *stream, const char *format, ...)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int __fputc(int c, struct __opaque_file *stream)
|
||||
{
|
||||
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
||||
return EOF;
|
||||
}
|
||||
|
||||
char cv = c;
|
||||
int ret = __libc_file_write(stream, &cv, 1);
|
||||
if (ret < 1) {
|
||||
return EOF;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int fputc(int c, struct __opaque_file *stream)
|
||||
{
|
||||
__libc_file_lock(stream);
|
||||
int ret = __fputc(c, stream);
|
||||
__libc_file_unlock(stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern int __fputc(int c, struct __opaque_file *stream);
|
||||
|
||||
int fputs(const char *restrict str, struct __opaque_file *restrict stream)
|
||||
{
|
||||
__libc_file_lock(stream);
|
||||
if (stream->f_flags & (FILE_EOF | FILE_ERR)) {
|
||||
__libc_file_unlock(stream);
|
||||
return EOF;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
int err = 0;
|
||||
for (i = 0; str[i]; i++) {
|
||||
err = __fputc(str[i], stream);
|
||||
if (err < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__libc_file_unlock(stream);
|
||||
|
||||
if (i > 0) {
|
||||
return i;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
size_t fread(void *buf, size_t size, size_t count, struct __opaque_file *stream)
|
||||
{
|
||||
__libc_file_lock(stream);
|
||||
size_t total = size * count;
|
||||
if (total == 0) {
|
||||
__libc_file_unlock(stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
long ret = __libc_file_read(stream, buf, total);
|
||||
if (ret == 0) {
|
||||
stream->f_flags |= FILE_EOF;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
stream->f_flags |= FILE_ERR;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
__libc_file_unlock(stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
int fseek(struct __opaque_file *stream, long offset, int origin)
|
||||
{
|
||||
return lseek(stream->f_fd, offset, origin);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
long ftell(struct __opaque_file *stream)
|
||||
{
|
||||
return lseek(stream->f_fd, 0, SEEK_CUR);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
size_t fwrite(
|
||||
const void *buf,
|
||||
size_t size,
|
||||
size_t count,
|
||||
struct __opaque_file *stream)
|
||||
{
|
||||
__libc_file_lock(stream);
|
||||
size_t total = size * count;
|
||||
if (total == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long ret = __libc_file_write(stream, buf, total);
|
||||
if (ret == 0) {
|
||||
stream->f_flags |= FILE_EOF;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
stream->f_flags |= FILE_ERR;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
__libc_file_unlock(stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int printf(const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
int ret = vprintf(format, arg);
|
||||
va_end(arg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <mango/log.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DEFAULT_BUFFER_SIZE 4096
|
||||
|
||||
static int ringbuf_init(struct ringbuf *buf)
|
||||
{
|
||||
if (buf->buf_ptr) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (!buf->buf_max) {
|
||||
buf->buf_max = DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
buf->buf_ptr = malloc(buf->buf_max);
|
||||
if (!buf->buf_ptr) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int __libc_ringbuf_get_read_buffer(
|
||||
struct ringbuf *buf,
|
||||
const char **out_bufp,
|
||||
size_t *out_len)
|
||||
{
|
||||
int r = ringbuf_init(buf);
|
||||
if (r != SUCCESS) {
|
||||
return r;
|
||||
}
|
||||
|
||||
size_t contiguous_capacity = 0;
|
||||
if (buf->buf_readp > buf->buf_writep) {
|
||||
contiguous_capacity = buf->buf_max - buf->buf_readp;
|
||||
} else {
|
||||
contiguous_capacity = buf->buf_writep - buf->buf_readp;
|
||||
}
|
||||
|
||||
kern_tracef(
|
||||
"OPEN READ: readp=%zu, writep=%zu, max=%zu -> %p cap=%zu",
|
||||
buf->buf_readp,
|
||||
buf->buf_writep,
|
||||
buf->buf_max,
|
||||
buf->buf_ptr + buf->buf_readp,
|
||||
contiguous_capacity);
|
||||
*out_len = contiguous_capacity;
|
||||
|
||||
if (contiguous_capacity == 0) {
|
||||
*out_bufp = NULL;
|
||||
} else {
|
||||
*out_bufp = buf->buf_ptr + buf->buf_readp;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
void __libc_ringbuf_put_read_buffer(
|
||||
struct ringbuf *buf,
|
||||
const char **bufp,
|
||||
size_t *len)
|
||||
{
|
||||
buf->buf_readp += *len;
|
||||
|
||||
if (buf->buf_readp == buf->buf_writep) {
|
||||
buf->buf_readp = buf->buf_writep = 0;
|
||||
}
|
||||
|
||||
kern_tracef("CLOSE READ: %p r=%zu", *bufp, *len);
|
||||
|
||||
*bufp = NULL;
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
int __libc_ringbuf_get_write_buffer(
|
||||
struct ringbuf *buf,
|
||||
char **out_bufp,
|
||||
size_t *out_len)
|
||||
{
|
||||
int r = ringbuf_init(buf);
|
||||
if (r != SUCCESS) {
|
||||
return r;
|
||||
}
|
||||
|
||||
size_t contiguous_capacity = 0;
|
||||
if (buf->buf_writep >= buf->buf_readp) {
|
||||
contiguous_capacity = buf->buf_max - buf->buf_writep - 1;
|
||||
|
||||
if (buf->buf_readp > 0) {
|
||||
contiguous_capacity++;
|
||||
}
|
||||
} else {
|
||||
contiguous_capacity = buf->buf_readp - buf->buf_writep - 1;
|
||||
}
|
||||
|
||||
kern_tracef(
|
||||
"OPEN WRITE: readp=%zu, writep=%zu, max=%zu -> %p cap=%zu",
|
||||
buf->buf_readp,
|
||||
buf->buf_writep,
|
||||
buf->buf_max,
|
||||
buf->buf_ptr + buf->buf_readp,
|
||||
contiguous_capacity);
|
||||
|
||||
*out_len = contiguous_capacity;
|
||||
|
||||
if (contiguous_capacity == 0) {
|
||||
*out_bufp = NULL;
|
||||
} else {
|
||||
*out_bufp = buf->buf_ptr + buf->buf_writep;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
void __libc_ringbuf_put_write_buffer(
|
||||
struct ringbuf *buf,
|
||||
char **bufp,
|
||||
size_t *len)
|
||||
{
|
||||
buf->buf_writep += *len;
|
||||
|
||||
if (buf->buf_readp == buf->buf_writep) {
|
||||
buf->buf_readp = buf->buf_writep = 0;
|
||||
}
|
||||
|
||||
kern_tracef(
|
||||
"CLOSE WRITE: %p r=%zu (%c)",
|
||||
*bufp,
|
||||
*len,
|
||||
*(char *)(*bufp));
|
||||
|
||||
*bufp = NULL;
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
long __libc_ringbuf_read(struct ringbuf *buf, void *p, size_t count)
|
||||
{
|
||||
size_t nr_read = 0;
|
||||
long r = 0;
|
||||
|
||||
while (nr_read < count) {
|
||||
const char *src = NULL;
|
||||
size_t available = 0;
|
||||
r = __libc_ringbuf_get_read_buffer(buf, &src, &available);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
char *dest = p + nr_read;
|
||||
size_t to_copy = count - nr_read;
|
||||
|
||||
if (available == 0) {
|
||||
__libc_ringbuf_put_read_buffer(buf, &src, &available);
|
||||
break;
|
||||
}
|
||||
|
||||
if (to_copy > available) {
|
||||
to_copy = available;
|
||||
}
|
||||
|
||||
memcpy(dest, src, to_copy);
|
||||
|
||||
nr_read += available;
|
||||
__libc_ringbuf_put_read_buffer(buf, &src, &to_copy);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return nr_read;
|
||||
}
|
||||
|
||||
long __libc_ringbuf_write(struct ringbuf *buf, const void *p, size_t count)
|
||||
{
|
||||
size_t nr_written = 0;
|
||||
long r = 0;
|
||||
|
||||
while (nr_written < count) {
|
||||
char *dest = NULL;
|
||||
size_t available = 0;
|
||||
r = __libc_ringbuf_get_write_buffer(buf, &dest, &available);
|
||||
if (r < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const char *src = p + nr_written;
|
||||
size_t to_copy = count - nr_written;
|
||||
|
||||
if (available == 0) {
|
||||
__libc_ringbuf_put_write_buffer(buf, &dest, &available);
|
||||
break;
|
||||
}
|
||||
|
||||
if (to_copy > available) {
|
||||
to_copy = available;
|
||||
}
|
||||
|
||||
memcpy(dest, src, to_copy);
|
||||
|
||||
nr_written += available;
|
||||
__libc_ringbuf_put_write_buffer(buf, &dest, &to_copy);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return nr_written;
|
||||
}
|
||||
|
||||
int __libc_ringbuf_clear(struct ringbuf *buf)
|
||||
{
|
||||
buf->buf_readp = buf->buf_writep = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __libc_ringbuf_resize(struct ringbuf *buf, size_t max)
|
||||
{
|
||||
if (!(buf->buf_flags & RINGBUF_STATIC)) {
|
||||
free(buf->buf_ptr);
|
||||
buf->buf_ptr = NULL;
|
||||
}
|
||||
|
||||
buf->buf_readp = buf->buf_writep = 0;
|
||||
buf->buf_flags &= ~RINGBUF_STATIC;
|
||||
buf->buf_max = max;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __libc_ringbuf_resize_static(struct ringbuf *buf, void *p, size_t max)
|
||||
{
|
||||
if (!(buf->buf_flags & RINGBUF_STATIC)) {
|
||||
free(buf->buf_ptr);
|
||||
buf->buf_ptr = NULL;
|
||||
}
|
||||
|
||||
buf->buf_readp = buf->buf_writep = 0;
|
||||
buf->buf_flags |= RINGBUF_STATIC;
|
||||
buf->buf_ptr = p;
|
||||
buf->buf_max = max;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t __libc_ringbuf_available(const struct ringbuf *buf)
|
||||
{
|
||||
if (buf->buf_readp < buf->buf_writep) {
|
||||
return buf->buf_writep - buf->buf_readp;
|
||||
} else if (buf->buf_readp > buf->buf_writep) {
|
||||
return buf->buf_max - buf->buf_readp + buf->buf_writep;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t __libc_ringbuf_capacity(const struct ringbuf *buf)
|
||||
{
|
||||
if (buf->buf_readp > buf->buf_writep) {
|
||||
return buf->buf_readp - buf->buf_writep - 1;
|
||||
} else {
|
||||
return buf->buf_max - buf->buf_writep + buf->buf_readp - 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef _LIBC_IO_RINGBUF_H_
|
||||
#define _LIBC_IO_RINGBUF_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
enum ringbuf_flags {
|
||||
RINGBUF_NONE = 0,
|
||||
RINGBUF_STATIC,
|
||||
};
|
||||
|
||||
struct ringbuf {
|
||||
enum ringbuf_flags buf_flags;
|
||||
char *buf_ptr;
|
||||
size_t buf_max;
|
||||
|
||||
size_t buf_readp, buf_writep;
|
||||
};
|
||||
|
||||
extern int __libc_ringbuf_get_read_buffer(
|
||||
struct ringbuf *buf,
|
||||
const char **out_bufp,
|
||||
size_t *out_len);
|
||||
extern void __libc_ringbuf_put_read_buffer(
|
||||
struct ringbuf *buf,
|
||||
const char **bufp,
|
||||
size_t *len);
|
||||
|
||||
extern int __libc_ringbuf_get_write_buffer(
|
||||
struct ringbuf *buf,
|
||||
char **out_bufp,
|
||||
size_t *out_len);
|
||||
extern void __libc_ringbuf_put_write_buffer(
|
||||
struct ringbuf *buf,
|
||||
char **bufp,
|
||||
size_t *len);
|
||||
|
||||
extern long __libc_ringbuf_read(struct ringbuf *buf, void *p, size_t count);
|
||||
extern long __libc_ringbuf_write(
|
||||
struct ringbuf *buf,
|
||||
const void *p,
|
||||
size_t count);
|
||||
|
||||
extern int __libc_ringbuf_clear(struct ringbuf *buf);
|
||||
extern int __libc_ringbuf_resize(struct ringbuf *buf, size_t max);
|
||||
extern int __libc_ringbuf_resize_static(
|
||||
struct ringbuf *buf,
|
||||
void *p,
|
||||
size_t max);
|
||||
|
||||
extern size_t __libc_ringbuf_available(const struct ringbuf *buf);
|
||||
extern size_t __libc_ringbuf_capacity(const struct ringbuf *buf);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern int setvbuf(
|
||||
struct __opaque_file *restrict stream,
|
||||
char *restrict buf,
|
||||
int mode,
|
||||
size_t size)
|
||||
{
|
||||
switch (mode) {
|
||||
case _IOFBF:
|
||||
case _IOLBF:
|
||||
case _IONBF:
|
||||
break;
|
||||
default:
|
||||
return __set_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (stream->f_prev != 0) {
|
||||
return __set_errno(EINVAL);
|
||||
}
|
||||
|
||||
int err = SUCCESS;
|
||||
if (buf) {
|
||||
err = __libc_ringbuf_resize_static(&stream->f_buf, buf, size);
|
||||
} else {
|
||||
err = __libc_ringbuf_resize(&stream->f_buf, size);
|
||||
}
|
||||
|
||||
if (err != SUCCESS) {
|
||||
return __set_errno(err);
|
||||
}
|
||||
|
||||
stream->f_buffer_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static struct __opaque_file __stderr = {
|
||||
.f_fd = 2,
|
||||
.f_flags = FILE_STATIC,
|
||||
.f_buffer_mode = _IONBF,
|
||||
};
|
||||
|
||||
extern struct __opaque_file *__libc_file_stderr(void)
|
||||
{
|
||||
return &__stderr;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static struct __opaque_file __stdin = {
|
||||
.f_fd = 0,
|
||||
.f_flags = FILE_STATIC,
|
||||
.f_buffer_mode = _IOLBF,
|
||||
};
|
||||
|
||||
extern struct __opaque_file *__libc_file_stdin(void)
|
||||
{
|
||||
return &__stdin;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static struct __opaque_file __stdout = {
|
||||
.f_fd = 1,
|
||||
.f_flags = FILE_STATIC,
|
||||
.f_buffer_mode = _IOLBF,
|
||||
};
|
||||
|
||||
extern struct __opaque_file *__libc_file_stdout(void)
|
||||
{
|
||||
return &__stdout;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "file.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern int __libc_fctprintf(
|
||||
void (*out)(char character, void *arg),
|
||||
void *arg,
|
||||
const char *format,
|
||||
va_list va);
|
||||
extern int __fputc(int c, struct __opaque_file *stream);
|
||||
|
||||
static inline void _out_file(char character, void *arg)
|
||||
{
|
||||
struct __opaque_file *fp = arg;
|
||||
__fputc(character, fp);
|
||||
}
|
||||
|
||||
int vprintf(const char *format, va_list arg)
|
||||
{
|
||||
__libc_file_lock(stdout);
|
||||
|
||||
int ret = __libc_fctprintf(_out_file, stdout, format, arg);
|
||||
|
||||
__libc_file_unlock(stdout);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <errno.h>
|
||||
#include <mango/handle.h>
|
||||
#include <mango/msg.h>
|
||||
#include <rosetta/fs.h>
|
||||
#include <sys/remote.h>
|
||||
|
||||
int write(int fd, const void *buf, size_t count)
|
||||
{
|
||||
int err;
|
||||
size_t nr_written;
|
||||
kern_status_t status = fs_write(fd, buf, count, &err, &nr_written);
|
||||
if (status != KERN_OK) {
|
||||
return __set_errno(__errno_from_kern_status(status));
|
||||
}
|
||||
|
||||
if (err != SUCCESS) {
|
||||
return __set_errno(err);
|
||||
}
|
||||
|
||||
return nr_written;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user