mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-01-22 01:30:35 -05:00
3ds: Small changes to port sources.
This commit does the following changes: - Replace buffered io implementation with a simpler, lighter, slightly faster version. - Turn off both screens when backlight goes off. - Small change to enable plugins in the folling commit (s). Change-Id: I45df30be037c3a1686bd85c16c87bcd248db456f
This commit is contained in:
parent
fcf67a2ea9
commit
0b3e0d1432
11 changed files with 192 additions and 1320 deletions
|
|
@ -89,8 +89,9 @@ void backlight_hw_off(void)
|
|||
{
|
||||
lcd_mutex_lock();
|
||||
if (last_bl != 0) {
|
||||
/* only power off rockbox ui screen */
|
||||
/* power off both screens to save battery */
|
||||
GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_BOTTOM);
|
||||
GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_TOP);
|
||||
#ifdef HAVE_LCD_ENABLE
|
||||
lcd_enable(false);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,11 +27,17 @@
|
|||
#include "filesystem-ctru.h"
|
||||
#include "debug.h"
|
||||
|
||||
extern u32 __ctrl_code_allocator_pages;
|
||||
|
||||
void* programResolver(const char* sym, void *userData);
|
||||
void * lc_open(const char *filename, unsigned char *buf, size_t buf_size)
|
||||
{
|
||||
DEBUGF("dlopen(path=\"%s\")\n", filename);
|
||||
|
||||
/* We need to increase __ctrl_code_allocator_pages value to 8 MB
|
||||
to enable big plugins */
|
||||
__ctrl_code_allocator_pages = 2048;
|
||||
|
||||
/* note: the 3ds dlopen implementation needs a custom resolver
|
||||
for the unresolved symbols in shared objects */
|
||||
/* void *handle = dlopen(filename, RTLD_NOW | RTLD_LOCAL); */
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <math.h>
|
||||
|
||||
|
|
@ -19,11 +20,6 @@
|
|||
/* #define MALLOC_DEBUG
|
||||
#include "rmalloc/rmalloc.h" */
|
||||
|
||||
#include "cslice.h"
|
||||
#include "cmap.h"
|
||||
|
||||
#define nil NULL
|
||||
|
||||
/* in go functions can return two values */
|
||||
#define two_type_value(type1, type2, name1, name2, type_name) \
|
||||
typedef struct { \
|
||||
|
|
@ -68,32 +64,14 @@ two_type_value(int, const char*, n, err, int_error);
|
|||
two_type_value(struct stat, const char*, fi, err, stat_error);
|
||||
typedef const char* file_error_t;
|
||||
|
||||
typedef struct page {
|
||||
s64 num;
|
||||
struct page* prev;
|
||||
struct page* next;
|
||||
u8* data;
|
||||
} page;
|
||||
typedef struct {
|
||||
Handle file;
|
||||
s64 start;
|
||||
s64 end;
|
||||
s64 size;
|
||||
bool isLastPage;
|
||||
|
||||
/* the two map types used by this library */
|
||||
cmap_declare(page, s64, struct page*);
|
||||
cmap_declare(bool, s64, bool);
|
||||
|
||||
typedef struct shard {
|
||||
sync_Mutex mu;
|
||||
cmap(page) pages;
|
||||
cmap(bool) dirty;
|
||||
struct page* head;
|
||||
struct page* tail;
|
||||
} shard;
|
||||
|
||||
typedef struct Pager {
|
||||
Handle file;
|
||||
s64 pgsize;
|
||||
s64 pgmax;
|
||||
/* sync_RWMutex mu; */
|
||||
s64 size;
|
||||
cslice(shard) shards;
|
||||
} Pager;
|
||||
u8 *buffer;
|
||||
} PageReader;
|
||||
|
||||
#endif /* _BFILE_INTERNAL_H_ */
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
/*
|
||||
* This code is based on bfile.go by Josh Baker.
|
||||
* Converted to C code by Mauricio G.
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
/* IMPORTANT: this code only works for O_RDONLY and O_RDWR files. */
|
||||
// This is a very simple code that reads blocks of data (pages) from a
|
||||
// file and then simulates file reads, reading from a memory buffer.
|
||||
// This is significantly faster than reading small chunks of data
|
||||
// directly from a file.
|
||||
|
||||
#include "bfile.h"
|
||||
|
||||
/* note: for ease of reading and ease of comparing go code
|
||||
with c implementation, function names are similar to the
|
||||
go version */
|
||||
|
||||
/* note2: sync_RWMutex calls have been moved to rockbox sys_file
|
||||
implementation. To use as standalone code please uncomment those calls. */
|
||||
|
||||
// rw mutex implementation
|
||||
void sync_RWMutexInit(sync_RWMutex *m) {
|
||||
LightLock_Init(&m->shared);
|
||||
CondVar_Init(&m->reader_q);
|
||||
|
|
@ -67,405 +59,117 @@ void sync_RWMutexUnlock(sync_RWMutex *m) {
|
|||
LightLock_Unlock(lk);
|
||||
}
|
||||
|
||||
void s_init(shard* s);
|
||||
|
||||
void s_push(shard* s, page* p) {
|
||||
s->head->next->prev = p;
|
||||
p->next = s->head->next;
|
||||
p->prev = s->head;
|
||||
s->head->next = p;
|
||||
}
|
||||
|
||||
void s_pop(shard* s, page* p) {
|
||||
p->prev->next = p->next;
|
||||
p->next->prev = p->prev;
|
||||
}
|
||||
|
||||
void s_bump(shard* s, page* p) {
|
||||
s_pop(s, p);
|
||||
s_push(s, p);
|
||||
}
|
||||
|
||||
/* page_pair_t destructor */
|
||||
/* page_pair_t type is defined by cmap_declare(page, s64, struct page*) */
|
||||
/* struct {
|
||||
s64 key;
|
||||
struct page* value;
|
||||
} page_pair_t;
|
||||
*/
|
||||
void page_pair_free(void* pair_ptr) {
|
||||
if (pair_ptr) {
|
||||
page_pair_t *pair = (page_pair_t*) pair_ptr;
|
||||
struct page *p = pair->value;
|
||||
if (p != nil) {
|
||||
if (p->data != nil) {
|
||||
free(p->data);
|
||||
}
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* shard destructor */
|
||||
void s_free(void* s_ptr) {
|
||||
if (s_ptr) {
|
||||
shard *s = (shard*) s_ptr;
|
||||
if (s->pages != nil) {
|
||||
cmap_set_elem_destructor(s->pages, page_pair_free);
|
||||
cmap_clear(page, s->pages);
|
||||
cmap_clear(bool, s->dirty);
|
||||
free(s->head);
|
||||
free(s->tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pager* NewPager(Handle file) {
|
||||
return NewPagerSize(file, 0, 0);
|
||||
}
|
||||
|
||||
Pager* NewPagerSize(Handle file, int pageSize, int bufferSize) {
|
||||
if (pageSize <= 0) {
|
||||
pageSize = defaultPageSize;
|
||||
} else if ((pageSize & 4095) != 0) {
|
||||
// must be a power of two
|
||||
int x = 1;
|
||||
while (x < pageSize) {
|
||||
x *= 2;
|
||||
}
|
||||
pageSize = x;
|
||||
}
|
||||
|
||||
if (bufferSize <= 0) {
|
||||
bufferSize = defaultBufferSize;
|
||||
} else if (bufferSize < pageSize) {
|
||||
bufferSize = pageSize;
|
||||
}
|
||||
|
||||
Pager* f = (Pager*) malloc(sizeof(Pager));
|
||||
f->file = file;
|
||||
f->size = -1;
|
||||
f->pgsize = (s64) pageSize;
|
||||
|
||||
/* sync_RWMutexInit(&f->mu); */
|
||||
|
||||
// calculate the max number of pages across all shards
|
||||
s64 pgmax = (s64) bufferSize / f->pgsize;
|
||||
if (pgmax < minPages) {
|
||||
pgmax = minPages;
|
||||
}
|
||||
|
||||
// calculate how many shards are needed, power of 2
|
||||
s64 nshards = (s64) ceil((double) pgmax / (double) pagesPerShard);
|
||||
if (nshards > maxShards) {
|
||||
nshards = maxShards;
|
||||
}
|
||||
s64 x = 1;
|
||||
while (x < nshards) {
|
||||
x *= 2;
|
||||
}
|
||||
nshards = x;
|
||||
|
||||
// calculate the max number of pages per shard
|
||||
f->pgmax = (s64) floor((double) pgmax / (double) nshards);
|
||||
cslice_make(f->shards, nshards, (shard) { 0 });
|
||||
|
||||
// initialize sync mutex
|
||||
size_t i;
|
||||
for (i = 0; i < cslice_len(f->shards); i++) {
|
||||
sync_MutexInit(&f->shards[i].mu);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
static int_error_t read_at(Handle file, u8 *data, size_t data_len, off_t off)
|
||||
// page reader implementation
|
||||
file_error_t readPage(PageReader *p, u64 offset)
|
||||
{
|
||||
u32 read_bytes = 0;
|
||||
if (R_FAILED(FSFILE_Read(file, &read_bytes, (u64) off, data, (u32) data_len))) {
|
||||
return (int_error_t) { -1, "I/O error" };
|
||||
if (R_FAILED(FSFILE_Read(p->file,
|
||||
&read_bytes,
|
||||
offset,
|
||||
p->buffer,
|
||||
p->size))) {
|
||||
printf("readPage: FSFILE_Read failed (I/O Error)\n");
|
||||
return "I/O Error";
|
||||
}
|
||||
|
||||
/* io.EOF */
|
||||
if (read_bytes == 0) {
|
||||
return (int_error_t) { 0, "io.EOF" };
|
||||
// we are at the last page of the file
|
||||
if (read_bytes < p->size) {
|
||||
p->isLastPage = true;
|
||||
}
|
||||
|
||||
return (int_error_t) { (int) read_bytes, nil };
|
||||
p->size = read_bytes;
|
||||
p->start = offset;
|
||||
p->end = offset + p->size;
|
||||
|
||||
/* printf("page: 0x%llx, 0x%llx, %lld (last_page: %s)\n", p->start, p->end, p->size, p->isLastPage == true ? "true": "false"); */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int_error_t write_at(Handle file, u8 *data, size_t data_len, off_t off)
|
||||
PageReader* NewPageReader(Handle file, s64 pageSize)
|
||||
{
|
||||
u32 written = 0;
|
||||
if (R_FAILED(FSFILE_Write(file, &written, (u64) off, data, (u32) data_len, FS_WRITE_FLUSH))) {
|
||||
return (int_error_t) { -1, "I/O error" };
|
||||
PageReader *p = (PageReader *) malloc(sizeof(PageReader));
|
||||
if (p == NULL) {
|
||||
printf("NewPageReader: memory error\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* I/O error */
|
||||
if ((written == 0) || (written < (u32) data_len)) {
|
||||
return (int_error_t) { -1, "I/O error" };
|
||||
p->buffer = (u8 *) malloc(sizeof(u8) * pageSize);
|
||||
if (p->buffer == NULL) {
|
||||
printf("NewPagereader: memory error (buffer)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (int_error_t) { (int) written, nil };
|
||||
// clear buffer data
|
||||
memset(p->buffer, 0x0, sizeof(u8) + pageSize);
|
||||
|
||||
p->start = 0;
|
||||
p->end = 0;
|
||||
p->isLastPage = false;
|
||||
p->size = pageSize;
|
||||
p->file = file;
|
||||
|
||||
// read ahead first page buffer from file
|
||||
file_error_t err = readPage(p, 0);
|
||||
if (err != NULL) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static stat_error_t file_stat(Handle file)
|
||||
void PageReader_Free(PageReader *p)
|
||||
{
|
||||
u64 size = 0;
|
||||
struct stat fi = { 0 };
|
||||
if (R_FAILED(FSFILE_GetSize(file, &size))) {
|
||||
fi.st_size = 0;
|
||||
return (stat_error_t) { fi, "I/O error" };
|
||||
}
|
||||
|
||||
fi.st_size = (off_t) size;
|
||||
return (stat_error_t) { fi, nil };
|
||||
}
|
||||
|
||||
void s_init(shard* s)
|
||||
{
|
||||
if (s->pages == nil) {
|
||||
s->pages = cmap_make(/*s64*/page);
|
||||
s->dirty = cmap_make(/*s64*/bool);
|
||||
s->head = (page*) malloc(sizeof(page));
|
||||
s->tail = (page*) malloc(sizeof(page));
|
||||
s->head->next = s->tail;
|
||||
s->tail->prev = s->head;
|
||||
}
|
||||
}
|
||||
|
||||
file_error_t f_write(Pager* f, page* p) {
|
||||
s64 off = p->num * f->pgsize;
|
||||
s64 end = f->pgsize;
|
||||
if ((off + end) > f->size) {
|
||||
end = f->size - off;
|
||||
}
|
||||
int_error_t __err = write_at(f->file, p->data, end, off);
|
||||
if (__err.err != nil) {
|
||||
return __err.err;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
file_error_t f_read(Pager* f, page* p) {
|
||||
int_error_t __err = read_at(f->file, p->data, f->pgsize, p->num * f->pgsize);
|
||||
if ((__err.err != nil) && strcmp(__err.err, "io.EOF")) {
|
||||
return "I/O error";
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
const char* f_incrSize(Pager* f, s64 end, bool write)
|
||||
{
|
||||
#define defer(m) \
|
||||
sync_RWMutexUnlock(&f->mu); \
|
||||
sync_RWMutexRLock(&f->mu);
|
||||
|
||||
/* sync_RWMutexRUnlock(&f->mu);
|
||||
sync_RWMutexLock(&f->mu); */
|
||||
|
||||
if (f->size == -1) {
|
||||
stat_error_t fi_err = file_stat(f->file);
|
||||
if (fi_err.err != nil) {
|
||||
/* defer(&f->mu); */
|
||||
return nil;
|
||||
if (p != NULL) {
|
||||
if (p->buffer != NULL) {
|
||||
free(p->buffer);
|
||||
p->buffer = NULL;
|
||||
}
|
||||
f->size = fi_err.fi.st_size;
|
||||
}
|
||||
if (write && (end > f->size)) {
|
||||
f->size = end;
|
||||
}
|
||||
|
||||
/* defer(&f->mu); */
|
||||
return nil;
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
int_error_t f_pio(Pager *f, u8 *b, size_t len_b, s64 pnum, s64 pstart, s64 pend, bool write);
|
||||
int_error_t f_io(Pager *f, u8 *b, size_t len_b, s64 off, bool write) {
|
||||
if (f == nil) {
|
||||
return (int_error_t) { 0, "invalid argument" };
|
||||
}
|
||||
static inline void copyPage(PageReader *p, u8 *buffer, size_t size, off_t offset)
|
||||
{
|
||||
off_t off = offset - p->start;
|
||||
memcpy(buffer, p->buffer + off, size);
|
||||
}
|
||||
|
||||
int_error_t PageReader_ReadAt(PageReader *p, u8 *buffer, size_t size, off_t offset)
|
||||
{
|
||||
bool eof = false;
|
||||
s64 start = off, end = off + len_b;
|
||||
if (start < 0) {
|
||||
return (int_error_t) { 0, "negative offset" };
|
||||
|
||||
// higher probability, buffer is in cached page range
|
||||
if ((offset >= p->start) && ((offset + size) < p->end)) {
|
||||
copyPage(p, buffer, size, offset);
|
||||
return (int_error_t) { size, NULL };
|
||||
}
|
||||
|
||||
// Check the upper bounds of the input to the known file size.
|
||||
// Increase the file size if needed.
|
||||
/* sync_RWMutexRLock(&f->mu); */
|
||||
if (end > f->size) {
|
||||
file_error_t err = f_incrSize(f, end, write);
|
||||
if (err != nil) {
|
||||
/* sync_RWMutexRUnlock(&f->mu); */
|
||||
return (int_error_t) { 0, err };
|
||||
}
|
||||
if (!write && (end > f->size)) {
|
||||
end = f->size;
|
||||
if ((end - start) < 0) {
|
||||
end = start;
|
||||
}
|
||||
eof = true;
|
||||
len_b = end-start; /* b = b[:end-start] */
|
||||
}
|
||||
}
|
||||
/* sync_RWMutexRUnlock(&f->mu); */
|
||||
|
||||
// Perform the page I/O.
|
||||
int total = 0;
|
||||
while (len_b > 0) {
|
||||
s64 pnum = off / f->pgsize;
|
||||
s64 pstart = off & (f->pgsize - 1);
|
||||
s64 pend = pstart + (s64) len_b;
|
||||
if (pend > f->pgsize) {
|
||||
pend = f->pgsize;
|
||||
// less higher probability, read new page at offset
|
||||
if (p->isLastPage == false) {
|
||||
file_error_t err = readPage(p, offset);
|
||||
if (err != NULL) {
|
||||
return (int_error_t) { -1, "I/O Error" };
|
||||
}
|
||||
|
||||
int_error_t result = f_pio(f, b, pend - pstart, pnum, pstart, pend, write);
|
||||
if (result.err != nil) {
|
||||
return (int_error_t) { total, result.err };
|
||||
}
|
||||
|
||||
off += (s64) result.n;
|
||||
total += result.n;
|
||||
b = &b[result.n]; len_b -= result.n; /* b = b[n:] */
|
||||
}
|
||||
if (eof) {
|
||||
return (int_error_t) { total, "io.EOF" };
|
||||
// we could have reached last page here so continue to
|
||||
// last page handling
|
||||
}
|
||||
|
||||
return (int_error_t) { total, nil };
|
||||
// lower probability, we are at the last page
|
||||
|
||||
// trying to read past end of file
|
||||
if (offset >= p->end) {
|
||||
return (int_error_t) { 0, "io.EOF" };
|
||||
}
|
||||
|
||||
// we reached the end of file so adjust buffer size to remaining bytes number
|
||||
if ((p->end - offset) < size) {
|
||||
size = p->end - offset;
|
||||
eof = true;
|
||||
}
|
||||
|
||||
copyPage(p, buffer, size, offset);
|
||||
return (int_error_t) { size, eof == true ? "io.EOF" : NULL };
|
||||
}
|
||||
|
||||
int_error_t f_pio(Pager *f, u8 *b, size_t len_b, s64 pnum, s64 pstart, s64 pend, bool write) {
|
||||
/* printf("pio(%p, %d, %lld, %lld, %lld, %s)\n", b, len_b, pnum, pstart, pend, write == true ? "true" : "false"); */
|
||||
shard *s = &f->shards[pnum & (s64) (cslice_len(f->shards) - 1)];
|
||||
sync_MutexLock(&s->mu);
|
||||
s_init(s);
|
||||
page *p = cmap_get_ptr(page, s->pages, pnum);
|
||||
if (p == nil) {
|
||||
// Page does not exist in memory.
|
||||
// Acquire a new one.
|
||||
if (cmap_len(s->pages) == f->pgmax) {
|
||||
// The buffer is at capacity.
|
||||
// Evict lru page and hang on to it.
|
||||
p = s->tail->prev;
|
||||
s_pop(s, p);
|
||||
cmap_delete(page, s->pages, p->num);
|
||||
if (cmap_get(bool, s->dirty, p->num)) {
|
||||
// dirty page. flush it now
|
||||
file_error_t err = f_write(f, p);
|
||||
if (err != nil) {
|
||||
sync_MutexUnlock(&s->mu);
|
||||
return (int_error_t) { 0, err };
|
||||
}
|
||||
cmap_delete(bool, s->dirty, p->num);
|
||||
}
|
||||
// Clear the previous page memory for partial page writes for
|
||||
// pages that are being partially written to.
|
||||
if (write && ((pend - pstart) < f->pgsize)) {
|
||||
memset(p->data, 0, f->pgsize);
|
||||
}
|
||||
} else {
|
||||
// Allocate an entirely new page.
|
||||
p = (page *) malloc(sizeof(page));
|
||||
p->data = (u8 *) malloc(f->pgsize);
|
||||
}
|
||||
p->num = pnum;
|
||||
// Read contents of page from file for all read operations, and
|
||||
// partial write operations. Ignore for full page writes.
|
||||
if (!write || ((pend-pstart) < f->pgsize)) {
|
||||
file_error_t err = f_read(f, p);
|
||||
if (err != nil) {
|
||||
sync_MutexUnlock(&s->mu);
|
||||
return (int_error_t) { 0, err };
|
||||
}
|
||||
}
|
||||
// Add the newly acquired page to the list.
|
||||
cmap_set(page, s->pages, p->num, p);
|
||||
s_push(s, p);
|
||||
} else {
|
||||
// Bump the page to the front of the list.
|
||||
s_bump(s, p);
|
||||
}
|
||||
if (write) {
|
||||
memcpy(p->data + pstart, b, pend - pstart);
|
||||
cmap_set(bool, s->dirty, pnum, true);
|
||||
} else {
|
||||
memcpy(b, p->data + pstart, pend - pstart);
|
||||
}
|
||||
sync_MutexUnlock(&s->mu);
|
||||
return (int_error_t) { len_b, nil };
|
||||
}
|
||||
|
||||
// Flush writes any unwritten buffered data to the underlying file.
|
||||
file_error_t PagerFlush(Pager *f) {
|
||||
if (f == nil) {
|
||||
return "invalid argument";
|
||||
}
|
||||
|
||||
/* sync_RWMutexLock(&f->mu); */
|
||||
for (size_t i = 0; i < cslice_len(f->shards); i++) {
|
||||
cmap_iterator(bool) pnum;
|
||||
if (f->shards[i].dirty != nil) {
|
||||
for (pnum = cmap_begin(f->shards[i].dirty);
|
||||
pnum != cmap_end(f->shards[i].dirty); pnum++) {
|
||||
if (pnum->value == true) {
|
||||
page *p = cmap_get_ptr(page, f->shards[i].pages, pnum->key);
|
||||
if (p != nil) {
|
||||
file_error_t err = f_write(f, p);
|
||||
if (err != nil) {
|
||||
/* sync_RWMutexUnlock(&f->mu); */
|
||||
return err;
|
||||
}
|
||||
}
|
||||
cmap_set(bool, f->shards[i].dirty, pnum->key, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* sync_RWMutexUnlock(&f->mu); */
|
||||
return nil;
|
||||
}
|
||||
|
||||
// ReadAt reads len(b) bytes from the File starting at byte offset off.
|
||||
int_error_t PagerReadAt(Pager *f, u8 *b, size_t len_b, off_t off) {
|
||||
return f_io(f, b, len_b, off, false);
|
||||
}
|
||||
|
||||
// WriteAt writes len(b) bytes to the File starting at byte offset off.
|
||||
int_error_t PagerWriteAt(Pager *f, u8 *b, size_t len_b, off_t off) {
|
||||
return f_io(f, b, len_b, off, true);
|
||||
}
|
||||
|
||||
file_error_t PagerTruncate(Pager *f, off_t length) {
|
||||
if (f == nil) {
|
||||
return "invalid argument";
|
||||
}
|
||||
|
||||
/* flush unwritten changes to disk */
|
||||
PagerFlush(f);
|
||||
|
||||
/* sync_RWMutexRLock(&f->mu); */
|
||||
/* set new file size */
|
||||
Handle handle = f->file;
|
||||
Result res = FSFILE_SetSize(handle, (u64) length);
|
||||
if (R_FAILED(res)) {
|
||||
return "I/O error";
|
||||
}
|
||||
/* sync_RWMutexRUnlock(&f->mu); */
|
||||
|
||||
/* FIXME: truncate only required pages. Remove all for now */
|
||||
PagerClear(f);
|
||||
f = NewPager(handle);
|
||||
return nil;
|
||||
}
|
||||
|
||||
void PagerClear(Pager *f) {
|
||||
if (f) {
|
||||
cslice_set_elem_destructor(f->shards, s_free);
|
||||
cslice_clear(f->shards);
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,35 +3,15 @@
|
|||
|
||||
#include "bfile-internal.h"
|
||||
|
||||
static const int defaultPageSize = 4096; // all pages are this size
|
||||
static const int defaultBufferSize = 0x800000; // default buffer size, 8 MB
|
||||
static const int minPages = 4; // minimum total pages per file
|
||||
static const int pagesPerShard = 32; // ideal number of pages per shard
|
||||
static const int maxShards = 128; // maximum number of shards per file
|
||||
static const int defaultPageSize = 0x80000; // default page buffer size, 512 kB
|
||||
|
||||
// NewPager returns a new Pager that is backed by the provided file.
|
||||
Pager* NewPager(Handle file);
|
||||
// NewPageReader creates a new PageReader struct with pageSize
|
||||
PageReader *NewPageReader(Handle file, s64 pageSize);
|
||||
|
||||
// NewPagerSize returns a new Pager with a custom page size and buffer size.
|
||||
// The bufferSize is the maximum amount of memory dedicated to individual
|
||||
// pages. Setting pageSize and bufferSize to zero will use their defaults,
|
||||
// which are 4096 and 8 MB respectively. Custom values are rounded up to the
|
||||
// nearest power of 2.
|
||||
Pager* NewPagerSize(Handle file, int pageSize, int bufferSize);
|
||||
// Free all memory associated to the PageReader pointer
|
||||
void PageReader_Free(PageReader *p);
|
||||
|
||||
// ReadAt reads len(b) bytes from the File starting at byte offset off.
|
||||
int_error_t PagerReadAt(Pager *f, u8 *b, size_t len_b, off_t off);
|
||||
|
||||
// WriteAt writes len(b) bytes to the File starting at byte offset off.
|
||||
int_error_t PagerWriteAt(Pager *f, u8 *b, size_t len_b, off_t off);
|
||||
|
||||
// Flush writes any unwritten buffered data to the underlying file.
|
||||
file_error_t PagerFlush(Pager *f);
|
||||
|
||||
// Truncates pager to specified length
|
||||
file_error_t PagerTruncate(Pager *f, off_t length);
|
||||
|
||||
// Free all memory associated to a Pager file
|
||||
void PagerClear(Pager *f);
|
||||
// ReadAt reads size bytes from the PageReader starting at offset.
|
||||
int_error_t PageReader_ReadAt(PageReader *p, u8 *buffer, size_t size, off_t offset);
|
||||
|
||||
#endif /* _B_FILE_H_ */
|
||||
|
|
|
|||
|
|
@ -1,209 +0,0 @@
|
|||
#ifndef CMAP_H_
|
||||
#define CMAP_H_
|
||||
|
||||
#define CVECTOR_LINEAR_GROWTH
|
||||
#include "cvector.h"
|
||||
|
||||
/* note: for ease of porting go code to c, many functions (macros) names
|
||||
remain similar to the ones used by go */
|
||||
|
||||
/* note2: this is a very basic map implementation. It does not do any sorting, and only works for basic types (and pointers) that can be compared with the equality operator */
|
||||
|
||||
#define nil NULL
|
||||
|
||||
#define cmap_elem_destructor_t cvector_elem_destructor_t
|
||||
|
||||
/**
|
||||
* @brief cmap_declare - The map type used in this library
|
||||
* @param name - The name associated to a map type.
|
||||
* @param key_type - The map pair key type.
|
||||
* @param val_type - The map pair value type.
|
||||
* @param compare_func - The function used to compare for key_type. Should return value < 0 when a < b, 0 when a == b and value > 0 when a > b.
|
||||
*/
|
||||
#define cmap_declare(name, key_type, val_type) \
|
||||
typedef struct { \
|
||||
key_type key; \
|
||||
val_type value; \
|
||||
} name##_pair_t; \
|
||||
\
|
||||
typedef struct { \
|
||||
cvector(name##_pair_t) tree; \
|
||||
cmap_elem_destructor_t elem_destructor; \
|
||||
} name##_map_t; \
|
||||
\
|
||||
static inline val_type name##_get_( \
|
||||
name##_map_t *this, const key_type key) \
|
||||
{ \
|
||||
if (this) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < cvector_size(this->tree); i++) { \
|
||||
if (key == this->tree[i].key) { \
|
||||
return this->tree[i].value; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
static inline val_type name##_get_ptr_( \
|
||||
name##_map_t *this, const key_type key) \
|
||||
{ \
|
||||
if (this) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < cvector_size(this->tree); i++) { \
|
||||
if (key == this->tree[i].key) { \
|
||||
return this->tree[i].value; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
return nil; \
|
||||
} \
|
||||
\
|
||||
static inline void name##_set_( \
|
||||
name##_map_t *this, const key_type key, val_type value) \
|
||||
{ \
|
||||
if (this) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < cvector_size(this->tree); i++) { \
|
||||
if (key == this->tree[i].key) { \
|
||||
this->tree[i].value = value; \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
name##_pair_t new_pair = (name##_pair_t) { key, value }; \
|
||||
cvector_push_back(this->tree, new_pair); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void name##_delete_( \
|
||||
name##_map_t *this, const key_type key) \
|
||||
{ \
|
||||
if (this) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < cvector_size(this->tree); i++) { \
|
||||
if (key == this->tree[i].key) { \
|
||||
cvector_erase(this->tree, i); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline name##_map_t* name##_map_make_(void) \
|
||||
{ \
|
||||
name##_map_t *map = (name##_map_t*) malloc(sizeof(name##_map_t)); \
|
||||
if (map) { \
|
||||
map->tree = nil; \
|
||||
cvector_init(map->tree, 0, nil); \
|
||||
return map; \
|
||||
} \
|
||||
return nil; \
|
||||
} \
|
||||
\
|
||||
static inline void name##_clear_(name##_map_t *this) \
|
||||
{ \
|
||||
if (this) { \
|
||||
cvector_free(this->tree); \
|
||||
free(this); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
|
||||
|
||||
/**
|
||||
* @brief cmap - The map type used in this library
|
||||
* @param name - The name associated to a map type.
|
||||
*/
|
||||
#define cmap(name) name##_map_t *
|
||||
|
||||
/**
|
||||
* @brief cmap_make - creates a new map. Automatically initializes the map.
|
||||
* @param name - the name asociated to the map type
|
||||
* @return a pointer to a new map.
|
||||
*/
|
||||
#define cmap_make(name) name##_map_make_()
|
||||
|
||||
/**
|
||||
* @brief cmap_size - gets the current size of the map
|
||||
* @param map_ptr - the map pointer
|
||||
* @return the size as a size_t
|
||||
*/
|
||||
#define cmap_len(map_ptr) cvector_size(map_ptr->tree)
|
||||
|
||||
/**
|
||||
* @brief cmap_get - gets value associated to a key.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @param key - the key to search for
|
||||
* @return the value associated to a key
|
||||
*/
|
||||
#define cmap_get(name, map_ptr, key) name##_get_(map_ptr, key)
|
||||
|
||||
/**
|
||||
* @brief cmap_get-ptr - gets ptr_value associated to a key. Use it to avoid assigning a ptr to 0.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @param key - the key to search for
|
||||
* @return the value associated to a key
|
||||
*/
|
||||
#define cmap_get_ptr(name, map_ptr, key) name##_get_ptr_(map_ptr, key)
|
||||
|
||||
|
||||
/**
|
||||
* @brief cmap_set - sets value associated to a key.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @param key - the key to search for
|
||||
* @param value - the new value
|
||||
* @return void
|
||||
*/
|
||||
#define cmap_set(name, map_ptr, key, val) name##_set_(map_ptr, key, val)
|
||||
|
||||
/**
|
||||
* @brief cmap_delete - deletes map entry associated to a key.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @param key - the key to search for
|
||||
* @return void
|
||||
*/
|
||||
#define cmap_delete(name, map_ptr, key) name##_delete_(map_ptr, key)
|
||||
|
||||
/**
|
||||
* @brief cmap_set_elem_destructor - set the element destructor function
|
||||
* used to clean up removed elements. The map must NOT be NULL for this to do anything.
|
||||
* @param map_ptr - the map pointer
|
||||
* @param elem_destructor_fn - function pointer of type cvector_elem_destructor_t used to destroy elements
|
||||
* @return void
|
||||
*/
|
||||
#define cmap_set_elem_destructor(map_ptr, elem_destructor_fn) \
|
||||
cvector_set_elem_destructor(map_ptr->tree, elem_destructor_fn)
|
||||
|
||||
/**
|
||||
* @brief cmap_clear - deletes all map entries. And frees memory is an element destructor was set previously.
|
||||
* @param name - the name asociated to the map type
|
||||
* @param map_ptr - the map pointer
|
||||
* @return void
|
||||
*/
|
||||
#define cmap_clear(name, map_ptr) name##_clear_(map_ptr)
|
||||
|
||||
/**
|
||||
* @brief cmap_iterator - The iterator type used for cmap
|
||||
* @param type The type of iterator to act on.
|
||||
*/
|
||||
#define cmap_iterator(name) cvector_iterator(name##_pair_t)
|
||||
|
||||
/**
|
||||
* @brief cmap_begin - returns an iterator to first element of the vector
|
||||
* @param map_ptr - the map pointer
|
||||
* @return a pointer to the first element (or NULL)
|
||||
*/
|
||||
#define cmap_begin(map_ptr) ((map_ptr) ? cvector_begin(map_ptr->tree) : nil)
|
||||
|
||||
/**
|
||||
* @brief cmap_end - returns an iterator to one past the last element of the vector
|
||||
* @param map_ptrs - the map pointer
|
||||
* @return a pointer to one past the last element (or NULL)
|
||||
*/
|
||||
#define cmap_end(map_ptr) ((map_ptr) ? cvector_end(map_ptr->tree) : nil)
|
||||
|
||||
#endif /* CMAP_H_ */
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#ifndef CSLICE_H_
|
||||
#define CSLICE_H_
|
||||
|
||||
#define CVECTOR_LINEAR_GROWTH
|
||||
#include "cvector.h"
|
||||
|
||||
/* note: for ease of porting go code to c, many functions (macros) names
|
||||
remain similar to the ones used by go */
|
||||
|
||||
#define nil NULL
|
||||
|
||||
/**
|
||||
* @brief cslice - The slice type used in this library
|
||||
* @param type The type of slice to act on.
|
||||
*/
|
||||
#define cslice(type) cvector(type)
|
||||
|
||||
/**
|
||||
* @brief cslice_make - creates a new slice. Automatically initializes the slice.
|
||||
* @param slice - the slice
|
||||
* @param count - new size of the slice
|
||||
* @param value - the value to initialize new elements with
|
||||
* @return void
|
||||
*/
|
||||
#define cslice_make(slice, capacity, value) \
|
||||
do { \
|
||||
slice = nil; \
|
||||
cvector_init(slice, capacity, nil); \
|
||||
cvector_resize(slice, capacity, value); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief cslice_size - gets the current size of the slice
|
||||
* @param slice - the slice
|
||||
* @return the size as a size_t
|
||||
*/
|
||||
#define cslice_len(slice) cvector_size(slice)
|
||||
|
||||
/**
|
||||
* @brief cslice_capacity - gets the current capacity of the slice
|
||||
* @param slice - the slice
|
||||
* @return the capacity as a size_t
|
||||
*/
|
||||
#define cslice_cap(slice) cvector_capacity(slice)
|
||||
|
||||
/**
|
||||
* @brief cslice_set_elem_destructor - set the element destructor function
|
||||
* used to clean up removed elements. The vector must NOT be NULL for this to do anything.
|
||||
* @param slice - the slice
|
||||
* @param elem_destructor_fn - function pointer of type cslice_elem_destructor_t used to destroy elements
|
||||
* @return void
|
||||
*/
|
||||
#define cslice_set_elem_destructor(slice, elem_destructor_fn) \
|
||||
cvector_set_elem_destructor(slice, elem_destructor_fn)
|
||||
|
||||
/**
|
||||
* @brief cslice_free - frees all memory associated with the slice
|
||||
* @param slice - the slice
|
||||
* @return void
|
||||
*/
|
||||
#define cslice_clear(slice) cvector_free(slice)
|
||||
|
||||
#endif /* CSLICE_H_ */
|
||||
|
|
@ -1,549 +0,0 @@
|
|||
#ifndef CVECTOR_H_
|
||||
#define CVECTOR_H_
|
||||
/**
|
||||
* @copyright Copyright (c) 2015 Evan Teran,
|
||||
* License: The MIT License (MIT)
|
||||
* @brief cvector heap implemented using C library malloc()
|
||||
* @file cvector.h
|
||||
*/
|
||||
|
||||
/* in case C library malloc() needs extra protection,
|
||||
* allow these defines to be overridden.
|
||||
*/
|
||||
/* functions for allocation and deallocation need to correspond to each other, fall back to C library functions if not all are overridden */
|
||||
#if !defined(cvector_clib_free) || !defined(cvector_clib_malloc) || !defined(cvector_clib_calloc) || !defined(cvector_clib_realloc)
|
||||
#ifdef cvector_clib_free
|
||||
#undef cvector_clib_free
|
||||
#endif
|
||||
#ifdef cvector_clib_malloc
|
||||
#undef cvector_clib_malloc
|
||||
#endif
|
||||
#ifdef cvector_clib_calloc
|
||||
#undef cvector_clib_calloc
|
||||
#endif
|
||||
#ifdef cvector_clib_realloc
|
||||
#undef cvector_clib_realloc
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#define cvector_clib_free free
|
||||
#define cvector_clib_malloc malloc
|
||||
#define cvector_clib_calloc calloc
|
||||
#define cvector_clib_realloc realloc
|
||||
#endif
|
||||
/* functions independent of memory allocation */
|
||||
#ifndef cvector_clib_assert
|
||||
#include <assert.h> /* for assert */
|
||||
#define cvector_clib_assert assert
|
||||
#endif
|
||||
#ifndef cvector_clib_memcpy
|
||||
#include <string.h> /* for memcpy */
|
||||
#define cvector_clib_memcpy memcpy
|
||||
#endif
|
||||
#ifndef cvector_clib_memmove
|
||||
#include <string.h> /* for memmove */
|
||||
#define cvector_clib_memmove memmove
|
||||
#endif
|
||||
|
||||
/* NOTE: Similar to C's qsort and bsearch, you will receive a T*
|
||||
* for a vector of Ts. This means that you cannot use `free` directly
|
||||
* as a destructor. Instead if you have for example a cvector_vector_type(int *)
|
||||
* you will need to supply a function which casts `elem_ptr` to an `int**`
|
||||
* and then does a free on what that pointer points to:
|
||||
*
|
||||
* ex:
|
||||
*
|
||||
* void free_int(void *p) { free(*(int **)p); }
|
||||
*/
|
||||
typedef void (*cvector_elem_destructor_t)(void *elem_ptr);
|
||||
|
||||
typedef struct cvector_metadata_t {
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
cvector_elem_destructor_t elem_destructor;
|
||||
} cvector_metadata_t;
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_type - The vector type used in this library
|
||||
* @param type The type of vector to act on.
|
||||
*/
|
||||
#define cvector_vector_type(type) type *
|
||||
|
||||
/**
|
||||
* @brief cvector - Syntactic sugar to retrieve a vector type
|
||||
* @param type The type of vector to act on.
|
||||
*/
|
||||
#define cvector(type) cvector_vector_type(type)
|
||||
|
||||
/**
|
||||
* @brief cvector_iterator - The iterator type used for cvector
|
||||
* @param type The type of iterator to act on.
|
||||
*/
|
||||
#define cvector_iterator(type) cvector_vector_type(type)
|
||||
|
||||
/**
|
||||
* @note you can also safely pass a pointer to a cvector iterator to a function
|
||||
* but you have to update the pointer at the end to update the original
|
||||
* iterator.
|
||||
* example:
|
||||
* void function( cvector_vector_type( type ) * p_it )
|
||||
* {
|
||||
* cvector_vector_type( type ) it = *p_it;
|
||||
* it ++;
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* *p_it = it;
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_type_ptr - helper to make code more "readable"
|
||||
* @param type - the vector type pointer
|
||||
*/
|
||||
#define cvector_ptr_type(type) \
|
||||
cvector_vector_type(type) *
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_ptr_get_iterator/set - helpers to make code more "readable"
|
||||
* @param it - the vector iterator
|
||||
* @param ptr - the vector pointer
|
||||
*/
|
||||
#define cvector_ptr_get_iterator(ptr) \
|
||||
*(ptr)
|
||||
|
||||
#define cvector_ptr_set(ptr, it) \
|
||||
*(ptr) = it
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_container_declare - defined a vector container type
|
||||
*/
|
||||
#define cvector_vector_container_declare(name, type) \
|
||||
struct cvector_vector_container_##name { \
|
||||
cvector_vector_type(type) vector; \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief cvector_vector_container - used to pass a cvector wrapped inside a container as a function parameter
|
||||
*/
|
||||
#define cvector_vector_container(name) \
|
||||
struct cvector_vector_container_##name
|
||||
|
||||
|
||||
/**
|
||||
* @brief cvector_vec_to_base - For internal use, converts a vector pointer to a metadata pointer
|
||||
* @param vec - the vector
|
||||
* @return the metadata pointer of the vector
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_vec_to_base(vec) \
|
||||
(&((cvector_metadata_t *)(void *)(vec))[-1])
|
||||
|
||||
/**
|
||||
* @brief cvector_base_to_vec - For internal use, converts a metadata pointer to a vector pointer
|
||||
* @param ptr - pointer to the metadata
|
||||
* @return the vector
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_base_to_vec(ptr) \
|
||||
((void *)&((cvector_metadata_t *)(ptr))[1])
|
||||
|
||||
/**
|
||||
* @brief cvector_capacity - gets the current capacity of the vector
|
||||
* @param vec - the vector
|
||||
* @return the capacity as a size_t
|
||||
*/
|
||||
#define cvector_capacity(vec) \
|
||||
((vec) ? cvector_vec_to_base(vec)->capacity : (size_t)0)
|
||||
|
||||
/**
|
||||
* @brief cvector_size - gets the current size of the vector
|
||||
* @param vec - the vector
|
||||
* @return the size as a size_t
|
||||
*/
|
||||
#define cvector_size(vec) \
|
||||
((vec) ? cvector_vec_to_base(vec)->size : (size_t)0)
|
||||
|
||||
/**
|
||||
* @brief cvector_elem_destructor - get the element destructor function used
|
||||
* to clean up elements
|
||||
* @param vec - the vector
|
||||
* @return the function pointer as cvector_elem_destructor_t
|
||||
*/
|
||||
#define cvector_elem_destructor(vec) \
|
||||
((vec) ? cvector_vec_to_base(vec)->elem_destructor : NULL)
|
||||
|
||||
/**
|
||||
* @brief cvector_empty - returns non-zero if the vector is empty
|
||||
* @param vec - the vector
|
||||
* @return non-zero if empty, zero if non-empty
|
||||
*/
|
||||
#define cvector_empty(vec) \
|
||||
(cvector_size(vec) == 0)
|
||||
|
||||
/**
|
||||
* @brief cvector_reserve - Requests that the vector capacity be at least enough
|
||||
* to contain n elements. If n is greater than the current vector capacity, the
|
||||
* function causes the container to reallocate its storage increasing its
|
||||
* capacity to n (or greater).
|
||||
* @param vec - the vector
|
||||
* @param n - Minimum capacity for the vector.
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_reserve(vec, n) \
|
||||
do { \
|
||||
size_t cv_reserve_cap__ = cvector_capacity(vec); \
|
||||
if (cv_reserve_cap__ < (n)) { \
|
||||
cvector_grow((vec), (n)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_init - Initialize a vector. The vector must be NULL for this to do anything.
|
||||
* @param vec - the vector
|
||||
* @param capacity - vector capacity to reserve
|
||||
* @param elem_destructor_fn - element destructor function
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_init(vec, capacity, elem_destructor_fn) \
|
||||
do { \
|
||||
if (!(vec)) { \
|
||||
cvector_reserve((vec), capacity); \
|
||||
cvector_set_elem_destructor((vec), (elem_destructor_fn)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_init_default - Initialize a vector with default value. The vector must be NULL for this to do anything. Does NOT work for struct types.
|
||||
* @param vec - the vector
|
||||
* @param capacity - vector capacity to reserve
|
||||
* @param elem_destructor_fn - element destructor function
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_init_default(vec, capacity, default) \
|
||||
do { \
|
||||
if (!(vec)) { \
|
||||
cvector_reserve((vec), capacity); \
|
||||
cvector_set_elem_destructor((vec), (elem_destructor_fn)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_erase - removes the element at index i from the vector
|
||||
* @param vec - the vector
|
||||
* @param i - index of element to remove
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_erase(vec, i) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
const size_t cv_erase_sz__ = cvector_size(vec); \
|
||||
if ((i) < cv_erase_sz__) { \
|
||||
cvector_elem_destructor_t cv_erase_elem_dtor__ = cvector_elem_destructor(vec); \
|
||||
if (cv_erase_elem_dtor__) { \
|
||||
cv_erase_elem_dtor__(&(vec)[i]); \
|
||||
} \
|
||||
cvector_set_size((vec), cv_erase_sz__ - 1); \
|
||||
cvector_clib_memmove( \
|
||||
(vec) + (i), \
|
||||
(vec) + (i) + 1, \
|
||||
sizeof(*(vec)) * (cv_erase_sz__ - 1 - (i))); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_clear - erase all of the elements in the vector
|
||||
* @param vec - the vector
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_clear(vec) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
cvector_elem_destructor_t cv_clear_elem_dtor__ = cvector_elem_destructor(vec); \
|
||||
if (cv_clear_elem_dtor__) { \
|
||||
size_t cv_clear_i__; \
|
||||
for (cv_clear_i__ = 0; cv_clear_i__ < cvector_size(vec); ++cv_clear_i__) { \
|
||||
cv_clear_elem_dtor__(&(vec)[cv_clear_i__]); \
|
||||
} \
|
||||
} \
|
||||
cvector_set_size(vec, 0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_free - frees all memory associated with the vector
|
||||
* @param vec - the vector
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_free(vec) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
void *cv_free_p__ = cvector_vec_to_base(vec); \
|
||||
cvector_elem_destructor_t cv_free_elem_dtor__ = cvector_elem_destructor(vec); \
|
||||
if (cv_free_elem_dtor__) { \
|
||||
size_t cv_free_i__; \
|
||||
for (cv_free_i__ = 0; cv_free_i__ < cvector_size(vec); ++cv_free_i__) { \
|
||||
cv_free_elem_dtor__(&(vec)[cv_free_i__]); \
|
||||
} \
|
||||
} \
|
||||
cvector_clib_free(cv_free_p__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_begin - returns an iterator to first element of the vector
|
||||
* @param vec - the vector
|
||||
* @return a pointer to the first element (or NULL)
|
||||
*/
|
||||
#define cvector_begin(vec) \
|
||||
(vec)
|
||||
|
||||
/**
|
||||
* @brief cvector_end - returns an iterator to one past the last element of the vector
|
||||
* @param vec - the vector
|
||||
* @return a pointer to one past the last element (or NULL)
|
||||
*/
|
||||
#define cvector_end(vec) \
|
||||
((vec) ? &((vec)[cvector_size(vec)]) : NULL)
|
||||
|
||||
/* user request to use linear growth algorithm */
|
||||
#ifdef CVECTOR_LINEAR_GROWTH
|
||||
|
||||
/**
|
||||
* @brief cvector_compute_next_grow - returns an the computed size in next vector grow
|
||||
* size is increased by 1
|
||||
* @param size - current size
|
||||
* @return size after next vector grow
|
||||
*/
|
||||
#define cvector_compute_next_grow(size) \
|
||||
((size) + 1)
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* @brief cvector_compute_next_grow - returns an the computed size in next vector grow
|
||||
* size is increased by multiplication of 2
|
||||
* @param size - current size
|
||||
* @return size after next vector grow
|
||||
*/
|
||||
#define cvector_compute_next_grow(size) \
|
||||
((size) ? ((size) << 1) : 1)
|
||||
|
||||
#endif /* CVECTOR_LINEAR_GROWTH */
|
||||
|
||||
/**
|
||||
* @brief cvector_push_back - adds an element to the end of the vector
|
||||
* @param vec - the vector
|
||||
* @param value - the value to add
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_push_back(vec, value) \
|
||||
do { \
|
||||
size_t cv_push_back_cap__ = cvector_capacity(vec); \
|
||||
if (cv_push_back_cap__ <= cvector_size(vec)) { \
|
||||
cvector_grow((vec), cvector_compute_next_grow(cv_push_back_cap__)); \
|
||||
} \
|
||||
(vec)[cvector_size(vec)] = (value); \
|
||||
cvector_set_size((vec), cvector_size(vec) + 1); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_insert - insert element at position pos to the vector
|
||||
* @param vec - the vector
|
||||
* @param pos - position in the vector where the new elements are inserted.
|
||||
* @param val - value to be copied (or moved) to the inserted elements.
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_insert(vec, pos, val) \
|
||||
do { \
|
||||
size_t cv_insert_cap__ = cvector_capacity(vec); \
|
||||
if (cv_insert_cap__ <= cvector_size(vec)) { \
|
||||
cvector_grow((vec), cvector_compute_next_grow(cv_insert_cap__)); \
|
||||
} \
|
||||
if ((pos) < cvector_size(vec)) { \
|
||||
cvector_clib_memmove( \
|
||||
(vec) + (pos) + 1, \
|
||||
(vec) + (pos), \
|
||||
sizeof(*(vec)) * ((cvector_size(vec)) - (pos))); \
|
||||
} \
|
||||
(vec)[(pos)] = (val); \
|
||||
cvector_set_size((vec), cvector_size(vec) + 1); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_pop_back - removes the last element from the vector
|
||||
* @param vec - the vector
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_pop_back(vec) \
|
||||
do { \
|
||||
cvector_elem_destructor_t cv_pop_back_elem_dtor__ = cvector_elem_destructor(vec); \
|
||||
if (cv_pop_back_elem_dtor__) { \
|
||||
cv_pop_back_elem_dtor__(&(vec)[cvector_size(vec) - 1]); \
|
||||
} \
|
||||
cvector_set_size((vec), cvector_size(vec) - 1); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_copy - copy a vector
|
||||
* @param from - the original vector
|
||||
* @param to - destination to which the function copy to
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_copy(from, to) \
|
||||
do { \
|
||||
if ((from)) { \
|
||||
cvector_grow(to, cvector_size(from)); \
|
||||
cvector_set_size(to, cvector_size(from)); \
|
||||
cvector_clib_memcpy((to), (from), cvector_size(from) * sizeof(*(from))); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_swap - exchanges the content of the vector by the content of another vector of the same type
|
||||
* @param vec - the original vector
|
||||
* @param other - the other vector to swap content with
|
||||
* @param type - the type of both vectors
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_swap(vec, other, type) \
|
||||
do { \
|
||||
if (vec && other) { \
|
||||
cvector_vector_type(type) cv_swap__ = vec; \
|
||||
vec = other; \
|
||||
other = cv_swap__; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_set_capacity - For internal use, sets the capacity variable of the vector
|
||||
* @param vec - the vector
|
||||
* @param size - the new capacity to set
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_set_capacity(vec, size) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
cvector_vec_to_base(vec)->capacity = (size); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_set_size - For internal use, sets the size variable of the vector
|
||||
* @param vec - the vector
|
||||
* @param _size - the new capacity to set
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_set_size(vec, _size) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
cvector_vec_to_base(vec)->size = (_size); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_set_elem_destructor - set the element destructor function
|
||||
* used to clean up removed elements. The vector must NOT be NULL for this to do anything.
|
||||
* @param vec - the vector
|
||||
* @param elem_destructor_fn - function pointer of type cvector_elem_destructor_t used to destroy elements
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_set_elem_destructor(vec, elem_destructor_fn) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
cvector_vec_to_base(vec)->elem_destructor = (elem_destructor_fn); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_grow - For internal use, ensures that the vector is at least `count` elements big
|
||||
* @param vec - the vector
|
||||
* @param count - the new capacity to set
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
#define cvector_grow(vec, count) \
|
||||
do { \
|
||||
const size_t cv_grow_sz__ = (count) * sizeof(*(vec)) + sizeof(cvector_metadata_t); \
|
||||
if (vec) { \
|
||||
void *cv_grow_p1__ = cvector_vec_to_base(vec); \
|
||||
void *cv_grow_p2__ = cvector_clib_realloc(cv_grow_p1__, cv_grow_sz__); \
|
||||
cvector_clib_assert(cv_grow_p2__); \
|
||||
(vec) = cvector_base_to_vec(cv_grow_p2__); \
|
||||
} else { \
|
||||
void *cv_grow_p__ = cvector_clib_malloc(cv_grow_sz__); \
|
||||
cvector_clib_assert(cv_grow_p__); \
|
||||
(vec) = cvector_base_to_vec(cv_grow_p__); \
|
||||
cvector_set_size((vec), 0); \
|
||||
cvector_set_elem_destructor((vec), NULL); \
|
||||
} \
|
||||
cvector_set_capacity((vec), (count)); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_shrink_to_fit - requests the container to reduce its capacity to fit its size
|
||||
* @param vec - the vector
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_shrink_to_fit(vec) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
const size_t cv_shrink_to_fit_sz__ = cvector_size(vec); \
|
||||
cvector_grow(vec, cv_shrink_to_fit_sz__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief cvector_at - returns a reference to the element at position n in the vector.
|
||||
* @param vec - the vector
|
||||
* @param n - position of an element in the vector.
|
||||
* @return the element at the specified position in the vector.
|
||||
*/
|
||||
#define cvector_at(vec, n) \
|
||||
((vec) ? (((int)(n) < 0 || (size_t)(n) >= cvector_size(vec)) ? NULL : &(vec)[n]) : NULL)
|
||||
|
||||
/**
|
||||
* @brief cvector_front - returns a reference to the first element in the vector. Unlike member cvector_begin, which returns an iterator to this same element, this function returns a direct reference.
|
||||
* @param vec - the vector
|
||||
* @return a reference to the first element in the vector container.
|
||||
*/
|
||||
#define cvector_front(vec) \
|
||||
((vec) ? ((cvector_size(vec) > 0) ? cvector_at(vec, 0) : NULL) : NULL)
|
||||
|
||||
/**
|
||||
* @brief cvector_back - returns a reference to the last element in the vector.Unlike member cvector_end, which returns an iterator just past this element, this function returns a direct reference.
|
||||
* @param vec - the vector
|
||||
* @return a reference to the last element in the vector.
|
||||
*/
|
||||
#define cvector_back(vec) \
|
||||
((vec) ? ((cvector_size(vec) > 0) ? cvector_at(vec, cvector_size(vec) - 1) : NULL) : NULL)
|
||||
|
||||
/**
|
||||
* @brief cvector_resize - resizes the container to contain count elements.
|
||||
* @param vec - the vector
|
||||
* @param count - new size of the vector
|
||||
* @param value - the value to initialize new elements with
|
||||
* @return void
|
||||
*/
|
||||
#define cvector_resize(vec, count, value) \
|
||||
do { \
|
||||
if (vec) { \
|
||||
size_t cv_resize_count__ = (size_t)(count); \
|
||||
size_t cv_resize_sz__ = cvector_vec_to_base(vec)->size; \
|
||||
if (cv_resize_count__ > cv_resize_sz__) { \
|
||||
cvector_reserve((vec), cv_resize_count__); \
|
||||
cvector_set_size((vec), cv_resize_count__); \
|
||||
do { \
|
||||
(vec)[cv_resize_sz__++] = (value); \
|
||||
} while (cv_resize_sz__ < cv_resize_count__); \
|
||||
} else { \
|
||||
while (cv_resize_count__ < cv_resize_sz__--) { \
|
||||
cvector_pop_back(vec); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* CVECTOR_H_ */
|
||||
|
|
@ -120,7 +120,7 @@ u32 fs_error(void) {
|
|||
/* Initialize the base descriptor */
|
||||
static void filestr_base_init(struct filestr_base *stream)
|
||||
{
|
||||
stream->cache = nil;
|
||||
stream->cache = NULL;
|
||||
stream->handle = 0;
|
||||
stream->size = 0;
|
||||
LightLock_Init(&stream->mtx);
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ static struct filestr_desc
|
|||
u64 *sizep; /* shortcut to file size in fileobj */
|
||||
} open_streams[MAX_OPEN_FILES] =
|
||||
{
|
||||
[0 ... MAX_OPEN_FILES-1] = { .stream = { .cache = nil, .flags = 0 } }
|
||||
[0 ... MAX_OPEN_FILES-1] = { .stream = { .cache = NULL, .flags = 0 } }
|
||||
};
|
||||
|
||||
extern FS_Archive sdmcArchive;
|
||||
|
|
@ -65,11 +65,11 @@ static struct filestr_desc * get_filestr(int fildes)
|
|||
|
||||
if ((unsigned int)fildes >= MAX_OPEN_FILES)
|
||||
file = NULL;
|
||||
else if (file->stream.cache != nil)
|
||||
else if (file->stream.cache != NULL)
|
||||
return file;
|
||||
|
||||
logf("fildes %d: bad file number\n", fildes);
|
||||
errno = (file && (file->stream.cache == nil)) ? ENXIO : EBADF;
|
||||
errno = (file && (file->stream.cache == NULL)) ? ENXIO : EBADF;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ static int alloc_filestr(struct filestr_desc **filep)
|
|||
for (int fildes = 0; fildes < MAX_OPEN_FILES; fildes++)
|
||||
{
|
||||
struct filestr_desc *file = &open_streams[fildes];
|
||||
if (file->stream.cache == nil)
|
||||
if (file->stream.cache == NULL)
|
||||
{
|
||||
*filep = file;
|
||||
return fildes;
|
||||
|
|
@ -117,10 +117,10 @@ int test_stream_exists_internal(const char *path)
|
|||
|
||||
Handle handle;
|
||||
Result res = FSUSER_OpenFile(&handle,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path),
|
||||
FS_OPEN_READ,
|
||||
0);
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path),
|
||||
FS_OPEN_READ,
|
||||
0);
|
||||
if (R_FAILED(res)) {
|
||||
/* not a file, try to open a directory */
|
||||
res = FSUSER_OpenDirectory(&handle,
|
||||
|
|
@ -234,12 +234,50 @@ static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
|
|||
int rc = 0;
|
||||
int_error_t n_err;
|
||||
|
||||
if (write)
|
||||
n_err = PagerWriteAt(file->stream.cache, (u8 *) buf, nbyte, AtomicLoad(&file->offset));
|
||||
else
|
||||
n_err = PagerReadAt(file->stream.cache, (u8 *) buf, nbyte, AtomicLoad(&file->offset));
|
||||
if (write) {
|
||||
// we will use direct file access for writing
|
||||
u32 written = 0;
|
||||
Result res = FSFILE_Write(file->stream.handle,
|
||||
&written,
|
||||
AtomicLoad(&file->offset),
|
||||
buf,
|
||||
nbyte,
|
||||
FS_WRITE_FLUSH);
|
||||
if (R_FAILED(res)) {
|
||||
n_err.err = "I/O error";
|
||||
}
|
||||
|
||||
if ((n_err.err != nil) && strcmp(n_err.err, "io.EOF")) {
|
||||
n_err.n = written;
|
||||
}
|
||||
else {
|
||||
// only use page_reader if buffer size is smaller than page size
|
||||
if (nbyte < file->stream.cache->size) {
|
||||
n_err = PageReader_ReadAt(file->stream.cache,
|
||||
(u8 *) buf,
|
||||
nbyte,
|
||||
AtomicLoad(&file->offset));
|
||||
}
|
||||
else {
|
||||
u32 bytes_read = 0;
|
||||
Result res = FSFILE_Read(file->stream.handle,
|
||||
&bytes_read,
|
||||
AtomicLoad(&file->offset),
|
||||
buf,
|
||||
nbyte);
|
||||
if (R_FAILED(res)) {
|
||||
n_err.err = "I/O error";
|
||||
}
|
||||
|
||||
// io.EOF
|
||||
if (bytes_read == 0) {
|
||||
n_err.err = "io.EOF";
|
||||
}
|
||||
|
||||
n_err.n = bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
if ((n_err.err != NULL) && strcmp(n_err.err, "io.EOF")) {
|
||||
FILE_ERROR(ERRNO, -3);
|
||||
}
|
||||
|
||||
|
|
@ -271,7 +309,7 @@ file_error:;
|
|||
/* initialize the base descriptor */
|
||||
static void filestr_base_init(struct filestr_base *stream)
|
||||
{
|
||||
stream->cache = nil;
|
||||
stream->cache = NULL;
|
||||
stream->handle = 0;
|
||||
stream->size = 0;
|
||||
LightLock_Init(&stream->mtx);
|
||||
|
|
@ -281,10 +319,10 @@ int open_internal_inner2(Handle *handle, const char *path, u32 openFlags, u32 at
|
|||
{
|
||||
int rc;
|
||||
Result res = FSUSER_OpenFile(handle,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path),
|
||||
openFlags,
|
||||
attributes);
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, path),
|
||||
openFlags,
|
||||
attributes);
|
||||
if (R_FAILED(res)) {
|
||||
FILE_ERROR(ERRNO, -1);
|
||||
}
|
||||
|
|
@ -372,21 +410,8 @@ static int open_internal_inner1(const char *path, int oflag)
|
|||
FILE_ERROR(ERRNO, -8);
|
||||
}
|
||||
|
||||
int pageSize = 4096; /* 4096 bytes */
|
||||
int bufferSize = 512 * 1024; /* 512 kB */
|
||||
|
||||
/* streamed file formats like flac and mp3 need very large page
|
||||
sizes to avoid stuttering */
|
||||
if (((oflag & O_ACCMODE) == O_RDONLY) && (size > 0x200000)) {
|
||||
/* printf("open(%s)_BIG_pageSize\n", path); */
|
||||
pageSize = 32 * 1024;
|
||||
bufferSize = MIN(size, defaultBufferSize);
|
||||
}
|
||||
|
||||
file->stream.cache = NewPagerSize(file->stream.handle,
|
||||
pageSize,
|
||||
bufferSize);
|
||||
if (file->stream.cache == nil) {
|
||||
file->stream.cache = NewPageReader(file->stream.handle, defaultPageSize);
|
||||
if (file->stream.cache == NULL) {
|
||||
FILE_ERROR(ERRNO, -7);
|
||||
}
|
||||
|
||||
|
|
@ -402,10 +427,10 @@ static int open_internal_inner1(const char *path, int oflag)
|
|||
|
||||
file_error:
|
||||
if (fildes >= 0) {
|
||||
if (file->stream.cache != nil) {
|
||||
PagerFlush(file->stream.cache);
|
||||
PagerClear(file->stream.cache);
|
||||
file->stream.cache = nil;
|
||||
if (file->stream.cache != NULL) {
|
||||
/* FSFILE_Flush(file->stream.handle); */
|
||||
PageReader_Free(file->stream.cache);
|
||||
file->stream.cache = NULL;
|
||||
}
|
||||
|
||||
FSFILE_Close(file->stream.handle);
|
||||
|
|
@ -445,16 +470,16 @@ int ctru_close(int fildes)
|
|||
|
||||
/* needs to work even if marked "nonexistant" */
|
||||
struct filestr_desc *file = &open_streams[fildes];
|
||||
if ((unsigned int)fildes >= MAX_OPEN_FILES || (file->stream.cache == nil))
|
||||
if ((unsigned int)fildes >= MAX_OPEN_FILES || (file->stream.cache == NULL))
|
||||
{
|
||||
logf("filedes %d not open\n", fildes);
|
||||
FILE_ERROR(EBADF, -2);
|
||||
}
|
||||
|
||||
if (file->stream.cache != nil) {
|
||||
PagerFlush(file->stream.cache);
|
||||
PagerClear(file->stream.cache);
|
||||
file->stream.cache = nil;
|
||||
if (file->stream.cache != NULL) {
|
||||
/* FSFILE_Flush(file->stream.handle); */
|
||||
PageReader_Free(file->stream.cache);
|
||||
file->stream.cache = NULL;
|
||||
}
|
||||
|
||||
FSFILE_Close(file->stream.handle);
|
||||
|
|
@ -490,8 +515,8 @@ int ctru_ftruncate(int fildes, off_t length)
|
|||
FILE_ERROR(EINVAL, -3);
|
||||
}
|
||||
|
||||
file_error_t err = PagerTruncate(file->stream.cache, length);
|
||||
if (err) {
|
||||
Result res = FSFILE_SetSize(file->stream.handle, length);
|
||||
if (R_FAILED(res)) {
|
||||
FILE_ERROR(ERRNO, -11);
|
||||
}
|
||||
|
||||
|
|
@ -521,8 +546,8 @@ int ctru_fsync(int fildes)
|
|||
}
|
||||
|
||||
/* flush all pending changes to disk */
|
||||
file_error_t err = PagerFlush(file->stream.cache);
|
||||
if (err != nil) {
|
||||
Result res = FSFILE_Flush(file->stream.handle);
|
||||
if (R_FAILED(res)) {
|
||||
FILE_ERROR(ERRNO, -3);
|
||||
}
|
||||
|
||||
|
|
@ -646,10 +671,10 @@ int ctru_rename(const char *old, const char *new)
|
|||
/* open 'old'; it must exist */
|
||||
Handle open1rc;
|
||||
Result res = FSUSER_OpenFile(&open1rc,
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, old),
|
||||
FS_OPEN_READ,
|
||||
0);
|
||||
sdmcArchive,
|
||||
fsMakePath(PATH_ASCII, old),
|
||||
FS_OPEN_READ,
|
||||
0);
|
||||
if (R_FAILED(res)) {
|
||||
/* not a file, try to open a directory */
|
||||
res = FSUSER_OpenDirectory(&open1rc,
|
||||
|
|
@ -774,4 +799,3 @@ ssize_t ctru_readlink(const char *path, char *buf, size_t bufsiz)
|
|||
{
|
||||
return readlink(path, buf, bufsiz);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
#define AtomicAdd(ptr, value) __atomic_add_fetch((u32*)(ptr), value, __ATOMIC_SEQ_CST)
|
||||
|
||||
struct filestr_base {
|
||||
Pager* cache; /* buffer IO implementation (cache) */
|
||||
PageReader* cache; /* buffer IO implementation (cache) */
|
||||
Handle handle; /* file handle */
|
||||
u64 size; /* file size */
|
||||
int flags; /* stream flags */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue