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:
Mauricio Garrido 2025-12-30 12:37:37 -06:00 committed by Solomon Peachy
parent fcf67a2ea9
commit 0b3e0d1432
11 changed files with 192 additions and 1320 deletions

View file

@ -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

View file

@ -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); */

View file

@ -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_ */

View file

@ -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);
}
}

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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);

View file

@ -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);
}

View file

@ -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 */