mirror of
https://github.com/ratspeak/microReticulum
synced 2026-08-12 18:07:25 -04:00
Introduces a flexible two-class allocator architecture (RNS_DEFAULT_ALLOCATOR and RNS_CONTAINER_ALLOCATOR) supporting heap, PSRAM, and TLSF pool variants for fine-grained control over embedded memory layout. - New Memory.h/cpp with ContainerAllocator<T>, heap/PSRAM/pool allocator types, and heap stats reporting - IdentityTable and PathTable now use ContainerAllocator - Replaced RNS_USE_TLSF/RNS_USE_ALLOCATOR/RNS_USE_PSRAM flags with per-class allocator flags (RNS_DEFAULT_ALLOCATOR, RNS_CONTAINER_ALLOCATOR) - Added ESP32 PSRAM allocator support (RNS_PSRAM_ALLOCATOR, RNS_PSRAM_POOL_ALLOCATOR) - Added Transport::get_path()/remove_path() helpers - Added loop callback (set_loop_callback) to enable more responsive app during long operations (like clean_cache) - Aded guard against re-entrancy to clean_cache() - Added callback option to list_directory() to avoid heap allocation - Added OOM auto-restart (ESP.restart/NVIC_SystemReset) in Interface send/receive and Reticulum loop. - Renamed setLogCallback to set_log_callback - Made Link/Identity static members private - Added native14 env to CI and platformio.ini - Bumped version to 0.2.10
718 lines
17 KiB
C++
718 lines
17 KiB
C++
#pragma once
|
|
|
|
#include <FileSystem.h>
|
|
#include <FileStream.h>
|
|
#include <Bytes.h>
|
|
#include <Log.h>
|
|
#include <Utilities/OS.h>
|
|
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
//#include <FS.h>
|
|
#include <SPIFFS.h>
|
|
#elif BOARD_NRF52
|
|
//#include <Adafruit_LittleFS.h>
|
|
#include <InternalFileSystem.h>
|
|
using namespace Adafruit_LittleFS_Namespace;
|
|
#endif
|
|
#else
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
class FileSystem : public RNS::FileSystemImpl {
|
|
|
|
public:
|
|
FileSystem() {}
|
|
|
|
bool init() {
|
|
TRACE("FileSystem initializing...");
|
|
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
// Setup FileSystem
|
|
INFO("SPIFFS mounting FileSystem");
|
|
if (!SPIFFS.begin(true, "")){
|
|
ERROR("SPIFFS FileSystem mount failed");
|
|
return false;
|
|
}
|
|
uint32_t size = SPIFFS.totalBytes() / (1024 * 1024);
|
|
Serial.print("size: ");
|
|
Serial.print(size);
|
|
Serial.println(" MB");
|
|
uint32_t used = SPIFFS.usedBytes() / (1024 * 1024);
|
|
Serial.print("used: ");
|
|
Serial.print(used);
|
|
Serial.println(" MB");
|
|
// ensure FileSystem is writable and format if not
|
|
RNS::Bytes test("test");
|
|
if (write_file("/test", test) < 4) {
|
|
INFO("SPIFFS FileSystem is being formatted, please wait...");
|
|
SPIFFS.format();
|
|
}
|
|
else {
|
|
remove_file("/test");
|
|
}
|
|
DEBUG("SPIFFS FileSystem is ready");
|
|
#elif BOARD_NRF52
|
|
// Initialize Internal File System
|
|
INFO("InternalFS mounting FileSystem");
|
|
InternalFS.begin();
|
|
INFO("InternalFS FileSystem is ready");
|
|
#endif
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
static void listDir(const char* dir) {
|
|
#ifdef ARDUINO
|
|
Serial.print("DIR: ");
|
|
Serial.println(dir);
|
|
#ifdef BOARD_ESP32
|
|
File root = SPIFFS.open(dir);
|
|
if (!root) {
|
|
Serial.println("Failed to opend directory");
|
|
return;
|
|
}
|
|
File file = root.openNextFile();
|
|
while (file) {
|
|
if (file.isDirectory()) {
|
|
Serial.print(" DIR: ");
|
|
}
|
|
else {
|
|
Serial.print(" FILE: ");
|
|
}
|
|
Serial.println(file.name());
|
|
file.close();
|
|
file = root.openNextFile();
|
|
}
|
|
root.close();
|
|
#elif BOARD_NRF52
|
|
File root = InternalFS.open(dir);
|
|
if (!root) {
|
|
Serial.println("Failed to opend directory");
|
|
return;
|
|
}
|
|
File file = root.openNextFile();
|
|
while (file) {
|
|
if (file.isDirectory()) {
|
|
Serial.print(" DIR: ");
|
|
}
|
|
else {
|
|
Serial.print(" FILE: ");
|
|
}
|
|
Serial.println(file.name());
|
|
file.close();
|
|
file = root.openNextFile();
|
|
}
|
|
root.close();
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
public:
|
|
virtual bool file_exists(const char* file_path) {
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
File file = SPIFFS.open(file_path, FILE_READ);
|
|
if (file) {
|
|
#elif BOARD_NRF52
|
|
File file(InternalFS);
|
|
if (file.open(file_path, FILE_O_READ)) {
|
|
#else
|
|
if (false) {
|
|
#endif
|
|
#else
|
|
// Native
|
|
FILE* file = fopen(file_path, "r");
|
|
if (file != nullptr) {
|
|
#endif
|
|
//TRACE("file_exists: file exists, closing file");
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
file.close();
|
|
#elif BOARD_NRF52
|
|
file.close();
|
|
#endif
|
|
#else
|
|
// Native
|
|
fclose(file);
|
|
#endif
|
|
return true;
|
|
}
|
|
else {
|
|
ERRORF("file_exists: failed to open file %s", file_path);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
virtual size_t read_file(const char* file_path, RNS::Bytes& data) {
|
|
size_t read = 0;
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
File file = SPIFFS.open(file_path, FILE_READ);
|
|
if (file) {
|
|
size_t size = file.size();
|
|
read = file.readBytes((char*)data.writable(size), size);
|
|
#elif BOARD_NRF52
|
|
//File file(InternalFS);
|
|
//if (file.open(file_path, FILE_O_READ)) {
|
|
File file = InternalFS.open(file_path, FILE_O_READ);
|
|
if (file) {
|
|
size_t size = file.size();
|
|
read = file.readBytes((char*)data.writable(size), size);
|
|
#else
|
|
if (false) {
|
|
size_t size = 0;
|
|
#endif
|
|
#else
|
|
// Native
|
|
FILE* file = fopen(file_path, "r");
|
|
if (file != nullptr) {
|
|
fseek(file, 0, SEEK_END);
|
|
size_t size = ftell(file);
|
|
rewind(file);
|
|
//size_t read = fread(data.writable(size), size, 1, file);
|
|
read = fread(data.writable(size), 1, size, file);
|
|
#endif
|
|
TRACEF("read_file: read %zu bytes from file %s", read, file_path);
|
|
if (read != size) {
|
|
ERRORF("read_file: failed to read file %s", file_path);
|
|
data.clear();
|
|
}
|
|
//TRACE("read_file: closing input file");
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
file.close();
|
|
#elif BOARD_NRF52
|
|
file.close();
|
|
#endif
|
|
#else
|
|
// Native
|
|
fclose(file);
|
|
#endif
|
|
}
|
|
else {
|
|
ERRORF("read_file: failed to open input file %s", file_path);
|
|
}
|
|
return read;
|
|
}
|
|
|
|
virtual size_t write_file(const char* file_path, const RNS::Bytes& data) {
|
|
// CBA TODO Replace remove with working truncation
|
|
remove_file(file_path);
|
|
size_t wrote = 0;
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
File file = SPIFFS.open(file_path, FILE_WRITE);
|
|
if (file) {
|
|
wrote = file.write(data.data(), data.size());
|
|
#elif BOARD_NRF52
|
|
File file(InternalFS);
|
|
if (file.open(file_path, FILE_O_WRITE)) {
|
|
wrote = file.write(data.data(), data.size());
|
|
#else
|
|
if (false) {
|
|
#endif
|
|
#else
|
|
// Native
|
|
FILE* file = fopen(file_path, "w");
|
|
if (file != nullptr) {
|
|
//size_t wrote = fwrite(data.data(), data.size(), 1, file);
|
|
wrote = fwrite(data.data(), 1, data.size(), file);
|
|
#endif
|
|
TRACEF("write_file: wrote %zu bytes to file %s", wrote, file_path);
|
|
if (wrote < data.size()) {
|
|
WARNINGF("write_file: not all data was written to file %s", file_path);
|
|
}
|
|
//TRACE("write_file: closing output file");
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
file.close();
|
|
#elif BOARD_NRF52
|
|
file.close();
|
|
#endif
|
|
#else
|
|
// Native
|
|
fclose(file);
|
|
#endif
|
|
}
|
|
else {
|
|
ERRORF("write_file: failed to open output file %s", file_path);
|
|
}
|
|
return wrote;
|
|
}
|
|
|
|
virtual RNS::FileStream open_file(const char* file_path, RNS::FileStream::MODE file_mode) {
|
|
TRACEF("open_file: opening file %s", file_path);
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
const char* mode;
|
|
if (file_mode == RNS::FileStream::MODE_READ) {
|
|
mode = FILE_READ;
|
|
}
|
|
else if (file_mode == RNS::FileStream::MODE_WRITE) {
|
|
mode = FILE_WRITE;
|
|
}
|
|
else if (file_mode == RNS::FileStream::MODE_APPEND) {
|
|
mode = FILE_APPEND;
|
|
}
|
|
else {
|
|
ERRORF("open_file: unsupported mode %d", file_mode);
|
|
return {RNS::Type::NONE};
|
|
}
|
|
TRACEF("open_file: opening file %s in mode %s", file_path, mode);
|
|
//// Using copy constructor to create a File* instead of local
|
|
//File file = SPIFFS.open(file_path, mode);
|
|
//if (!file) {
|
|
File* file = new File(SPIFFS.open(file_path, mode));
|
|
if (file == nullptr || !(*file)) {
|
|
if (file != nullptr) delete file;
|
|
ERRORF("open_file: failed to open output file %s", file_path);
|
|
return {RNS::Type::NONE};
|
|
}
|
|
TRACEF("open_file: successfully opened file %s", file_path);
|
|
return RNS::FileStream(new FileStreamImpl(file));
|
|
#elif BOARD_NRF52
|
|
int mode;
|
|
if (file_mode == RNS::FileStream::MODE_READ) {
|
|
mode = FILE_O_READ;
|
|
}
|
|
else if (file_mode == RNS::FileStream::MODE_WRITE) {
|
|
mode = FILE_O_WRITE;
|
|
// CBA TODO Replace remove with working truncation
|
|
if (InternalFS.exists(file_path)) {
|
|
InternalFS.remove(file_path);
|
|
}
|
|
}
|
|
else if (file_mode == RNS::FileStream::MODE_APPEND) {
|
|
// CBA This is the default write mode for nrf52 littlefs
|
|
mode = FILE_O_WRITE;
|
|
}
|
|
else {
|
|
ERRORF("open_file: unsupported mode %d", file_mode);
|
|
return {RNS::Type::NONE};
|
|
}
|
|
//File file = File(InternalFS);
|
|
File* file = new File(InternalFS);
|
|
if (file == nullptr || !file->open(file_path, mode)) {
|
|
if (file != nullptr) delete file;
|
|
ERRORF("open_file: failed to open output file %s", file_path);
|
|
return {RNS::Type::NONE};
|
|
}
|
|
|
|
// Seek to beginning to overwrite (this is failing on nrf52)
|
|
//if (file_mode == RNS::FileStream::MODE_WRITE) {
|
|
// file->seek(0);
|
|
// file->truncate(0);
|
|
//}
|
|
TRACEF("open_file: successfully opened file %s", file_path);
|
|
return RNS::FileStream(new FileStreamImpl(file));
|
|
#else
|
|
#warning("unsupported");
|
|
return RNS::FileStream(RNS::Type::NONE);
|
|
#endif
|
|
#else // ARDUINO
|
|
// Native
|
|
const char* mode;
|
|
if (file_mode == RNS::FileStream::MODE_READ) {
|
|
mode = "r";
|
|
}
|
|
else if (file_mode == RNS::FileStream::MODE_WRITE) {
|
|
mode = "w";
|
|
}
|
|
else if (file_mode == RNS::FileStream::MODE_APPEND) {
|
|
mode = "a";
|
|
}
|
|
else {
|
|
ERRORF("open_file: unsupported mode %d", file_mode);
|
|
return {RNS::Type::NONE};
|
|
}
|
|
TRACEF("open_file: opening file %s in mode %s", file_path, mode);
|
|
FILE* file = fopen(file_path, mode);
|
|
if (file == nullptr) {
|
|
ERRORF("open_file: failed to open output file %s", file_path);
|
|
return {RNS::Type::NONE};
|
|
}
|
|
TRACEF("open_file: successfully opened file %s", file_path);
|
|
return RNS::FileStream(new FileStreamImpl(file));
|
|
#endif
|
|
}
|
|
|
|
virtual bool remove_file(const char* file_path) {
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
return SPIFFS.remove(file_path);
|
|
#elif BOARD_NRF52
|
|
return InternalFS.remove(file_path);
|
|
#else
|
|
return false;
|
|
#endif
|
|
#else
|
|
// Native
|
|
return (remove(file_path) == 0);
|
|
#endif
|
|
}
|
|
|
|
virtual bool rename_file(const char* from_file_path, const char* to_file_path) {
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
return SPIFFS.rename(from_file_path, to_file_path);
|
|
#elif BOARD_NRF52
|
|
return InternalFS.rename(from_file_path, to_file_path);
|
|
#else
|
|
return false;
|
|
#endif
|
|
#else
|
|
// Native
|
|
return (rename(from_file_path, to_file_path) == 0);
|
|
#endif
|
|
}
|
|
|
|
/*virtua*/ bool directory_exists(const char* directory_path) {
|
|
TRACEF("directory_exists: checking for existence of directory %s", directory_path);
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
File file = SPIFFS.open(directory_path, FILE_READ);
|
|
if (file) {
|
|
bool is_directory = file.isDirectory();
|
|
file.close();
|
|
return is_directory;
|
|
}
|
|
#elif BOARD_NRF52
|
|
File file(InternalFS);
|
|
if (file.open(directory_path, FILE_O_READ)) {
|
|
bool is_directory = file.isDirectory();
|
|
file.close();
|
|
return is_directory;
|
|
}
|
|
#else
|
|
if (false) {
|
|
return false;
|
|
}
|
|
#endif
|
|
else {
|
|
return false;
|
|
}
|
|
#else
|
|
// Native
|
|
struct stat st = {0};
|
|
return (stat(directory_path, &st) == 0);
|
|
#endif
|
|
}
|
|
|
|
virtual bool create_directory(const char* directory_path) {
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
if (!SPIFFS.mkdir(directory_path)) {
|
|
ERRORF("create_directory: failed to create directorty %s", directory_path);
|
|
return false;
|
|
}
|
|
return true;
|
|
#elif BOARD_NRF52
|
|
if (!InternalFS.mkdir(directory_path)) {
|
|
ERRORF("create_directory: failed to create directorty %s", directory_path);
|
|
return false;
|
|
}
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
#else
|
|
// Native
|
|
struct stat st = {0};
|
|
if (stat(directory_path, &st) == 0) {
|
|
return true;
|
|
}
|
|
return (mkdir(directory_path, 0700) == 0);
|
|
#endif
|
|
}
|
|
|
|
/*virtua*/ bool remove_directory(const char* directory_path) {
|
|
TRACEF("remove_directory: removing directory %s", directory_path);
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
//if (!LittleFS.rmdir_r(directory_path)) {
|
|
if (!SPIFFS.rmdir(directory_path)) {
|
|
ERRORF("remove_directory: failed to remove directorty %s", directory_path);
|
|
return false;
|
|
}
|
|
return true;
|
|
#elif BOARD_NRF52
|
|
if (!InternalFS.rmdir_r(directory_path)) {
|
|
ERRORF("remove_directory: failed to remove directory %s", directory_path);
|
|
return false;
|
|
}
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
#else
|
|
// Native
|
|
if (rmdir(directory_path) == 0) {
|
|
ERRORF("remove_directory: failed to remove directory %s", directory_path);
|
|
return false;
|
|
}
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
/*virtua*/ std::list<std::string> list_directory(const char* directory_path, Callbacks::DirectoryListing callback = nullptr) {
|
|
TRACEF("list_directory: listing directory %s", directory_path);
|
|
std::list<std::string> files;
|
|
#ifdef ARDUINO
|
|
#ifdef BOARD_ESP32
|
|
File root = SPIFFS.open(directory_path);
|
|
#elif BOARD_NRF52
|
|
File root = InternalFS.open(directory_path);
|
|
#endif
|
|
if (!root) {
|
|
ERRORF("list_directory: failed to open directory %s", directory_path);
|
|
return files;
|
|
}
|
|
File file = root.openNextFile();
|
|
while (file) {
|
|
if (!file.isDirectory()) {
|
|
char* name = (char*)file.name();
|
|
if (callback) callback(name);
|
|
else files.push_back(name);
|
|
}
|
|
// CBA Following close required to avoid leaking memory
|
|
file.close();
|
|
file = root.openNextFile();
|
|
}
|
|
TRACE("list_directory: returning directory listing");
|
|
root.close();
|
|
return files;
|
|
#else
|
|
// Native
|
|
DIR *dir = opendir(directory_path);
|
|
if (dir == NULL) {
|
|
ERRORF("list_directory: failed to open directory %s", directory_path);
|
|
return files;
|
|
}
|
|
struct dirent *entry;
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
|
continue;
|
|
}
|
|
char* name = entry->d_name;
|
|
if (callback) callback(name);
|
|
else files.push_back(name);
|
|
}
|
|
if (closedir(dir) == -1) {
|
|
ERRORF("list_directory: failed to close directory %s", directory_path);
|
|
}
|
|
|
|
return files;
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef ARDUINO
|
|
|
|
#ifdef BOARD_ESP32
|
|
|
|
virtual size_t storage_size() {
|
|
return SPIFFS.totalBytes();
|
|
}
|
|
|
|
virtual size_t storage_available() {
|
|
return (SPIFFS.totalBytes() - SPIFFS.usedBytes());
|
|
}
|
|
|
|
#elif BOARD_NRF52
|
|
|
|
static int _countLfsBlock(void *p, lfs_block_t block){
|
|
lfs_size_t *size = (lfs_size_t*) p;
|
|
*size += 1;
|
|
return 0;
|
|
}
|
|
|
|
static lfs_ssize_t getUsedBlockCount() {
|
|
lfs_size_t size = 0;
|
|
lfs_traverse(InternalFS._getFS(), _countLfsBlock, &size);
|
|
return size;
|
|
}
|
|
|
|
static int totalBytes() {
|
|
const lfs_config* config = InternalFS._getFS()->cfg;
|
|
return config->block_size * config->block_count;
|
|
}
|
|
|
|
static int usedBytes() {
|
|
const lfs_config* config = InternalFS._getFS()->cfg;
|
|
const int usedBlockCount = getUsedBlockCount();
|
|
return config->block_size * usedBlockCount;
|
|
}
|
|
|
|
virtual size_t storage_size() {
|
|
//return totalBytes();
|
|
return InternalFS.totalBytes();
|
|
}
|
|
|
|
virtual size_t storage_available() {
|
|
//return (totalBytes() - usedBytes());
|
|
return (InternalFS.totalBytes() - InternalFS.usedBytes());
|
|
}
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
virtual size_t storage_size() {
|
|
return 0;
|
|
}
|
|
|
|
virtual size_t storage_available() {
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
protected:
|
|
|
|
#if defined(BOARD_ESP32) || defined(BOARD_NRF52)
|
|
class FileStreamImpl : public RNS::FileStreamImpl {
|
|
|
|
private:
|
|
std::unique_ptr<File> _file;
|
|
bool _closed = false;
|
|
|
|
public:
|
|
FileStreamImpl(File* file) : RNS::FileStreamImpl(), _file(file) {}
|
|
virtual ~FileStreamImpl() { if (!_closed) close(); }
|
|
|
|
public:
|
|
inline virtual const char* name() { return _file->name(); }
|
|
inline virtual size_t size() { return _file->size(); }
|
|
inline virtual void close() { _closed = true; _file->close(); }
|
|
|
|
// Print overrides
|
|
inline virtual size_t write(uint8_t byte) { return _file->write(byte); }
|
|
inline virtual size_t write(const uint8_t *buffer, size_t size) { return _file->write(buffer, size); }
|
|
|
|
// Stream overrides
|
|
inline virtual int available() { return _file->available(); }
|
|
inline virtual int read() { return _file->read(); }
|
|
inline virtual int peek() { return _file->peek(); }
|
|
inline virtual void flush() { _file->flush(); }
|
|
|
|
};
|
|
#else
|
|
class FileStreamImpl : public RNS::FileStreamImpl {
|
|
|
|
private:
|
|
FILE* _file = nullptr;
|
|
bool _closed = false;
|
|
size_t _available = 0;
|
|
char _filename[1024];
|
|
|
|
public:
|
|
FileStreamImpl(FILE* file) : RNS::FileStreamImpl(), _file(file) {
|
|
_available = size();
|
|
}
|
|
virtual ~FileStreamImpl() { if (!_closed) close(); }
|
|
|
|
public:
|
|
inline virtual const char* name() {
|
|
assert(_file);
|
|
#if 0
|
|
char proclnk[1024];
|
|
snprintf(proclnk, sizeof(proclnk), "/proc/self/fd/%d", fileno(_file));
|
|
int r = readlink(proclnk, _filename, sizeof(_filename));
|
|
if (r < 0) {
|
|
return nullptr);
|
|
}
|
|
_filename[r] = '\0';
|
|
return _filename;
|
|
#elif 0
|
|
if (fcntl(fd, F_GETPATH, _filename) < 0) {
|
|
rerturn nullptr;
|
|
}
|
|
return _filename;
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
inline virtual size_t size() {
|
|
assert(_file);
|
|
struct stat st;
|
|
fstat(fileno(_file), &st);
|
|
return st.st_size;
|
|
}
|
|
inline virtual void close() {
|
|
assert(_file);
|
|
_closed = true;
|
|
fclose(_file);
|
|
_file = nullptr;
|
|
}
|
|
|
|
// Print overrides
|
|
inline virtual size_t write(uint8_t byte) {
|
|
assert(_file);
|
|
int ch = fputc(byte, _file);
|
|
if (ch == EOF) {
|
|
return 0;
|
|
}
|
|
++_available;
|
|
return 1;
|
|
}
|
|
inline virtual size_t write(const uint8_t *buffer, size_t size) {
|
|
assert(_file);
|
|
size_t wrote = fwrite(buffer, sizeof(uint8_t), size, _file);
|
|
_available += wrote;
|
|
return wrote;
|
|
}
|
|
|
|
// Stream overrides
|
|
inline virtual int available() {
|
|
#if 0
|
|
assert(_file);
|
|
int size = 0;
|
|
ioctl(fileno(_file), FIONREAD, &size);
|
|
TRACEF("FileStream::available: %d", size);
|
|
return size;
|
|
#else
|
|
return _available;
|
|
#endif
|
|
}
|
|
inline virtual int read() {
|
|
if (_available <= 0) {
|
|
return EOF;
|
|
}
|
|
assert(_file);
|
|
int ch = fgetc(_file);
|
|
if (ch == EOF) {
|
|
return ch;
|
|
}
|
|
--_available;
|
|
//TRACEF("FileStream::read: %c", ch);
|
|
return ch;
|
|
}
|
|
inline virtual int peek() {
|
|
if (_available <= 0) {
|
|
return EOF;
|
|
}
|
|
assert(_file);
|
|
int ch = fgetc(_file);
|
|
ungetc(ch, _file);
|
|
TRACEF("FileStream::peek: %c", ch);
|
|
return ch;
|
|
}
|
|
inline virtual void flush() {
|
|
assert(_file);
|
|
fflush(_file);
|
|
TRACE("FileStream::flush");
|
|
}
|
|
|
|
};
|
|
#endif
|
|
|
|
};
|