Merge pull request #266 from Geertiebear/open_memstream

Open memstream implementation and global_file_list fix
This commit is contained in:
Alexander van der Grinten 2021-06-08 22:02:32 +02:00 committed by GitHub
commit 4e0d8211d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 193 additions and 13 deletions

View file

@ -38,7 +38,10 @@ namespace {
constexpr bool globallyDisableBuffering = false;
// List of files that will be flushed before exit().
file_list global_file_list;
file_list &global_file_list() {
static frg::eternal<file_list> list;
return list.get();
};
}
// For pipe-like streams (seek returns ESPIPE), we need to make sure
@ -60,7 +63,7 @@ abstract_file::abstract_file(void (*do_dispose)(abstract_file *))
__io_mode = 0;
__status_bits = 0;
global_file_list.push_back(this);
global_file_list().push_back(this);
}
abstract_file::~abstract_file() {
@ -71,8 +74,8 @@ abstract_file::~abstract_file() {
if(__buffer_ptr)
getAllocator().free(__buffer_ptr);
auto it = global_file_list.iterator_to(this);
global_file_list.erase(it);
auto it = global_file_list().iterator_to(this);
global_file_list().erase(it);
}
void abstract_file::dispose() {
@ -475,7 +478,7 @@ namespace {
~stdio_guard() {
// Only flush the files but do not close them.
for(auto it : mlibc::global_file_list) {
for(auto it : mlibc::global_file_list()) {
if(int e = it->flush(); e)
mlibc::infoLogger() << "mlibc warning: Failed to flush file before exit()"
<< frg::endlog;
@ -589,7 +592,7 @@ long ftell(FILE *file_base) {
int fflush_unlocked(FILE *file_base) {
if(file_base == NULL) {
// Only flush the files but do not close them.
for(auto it : mlibc::global_file_list) {
for(auto it : mlibc::global_file_list()) {
if(int e = it->flush(); e)
mlibc::infoLogger() << "mlibc warning: Failed to flush file"
<< frg::endlog;
@ -604,7 +607,7 @@ int fflush_unlocked(FILE *file_base) {
int fflush(FILE *file_base) {
if(file_base == NULL) {
// Only flush the files but do not close them.
for(auto it : mlibc::global_file_list) {
for(auto it : mlibc::global_file_list()) {
frg::unique_lock<FutexLock> lock(it->_lock);
if(int e = it->flush(); e)
mlibc::infoLogger() << "mlibc warning: Failed to flush file"

View file

@ -0,0 +1,79 @@
#include <mlibc/posix-file-io.hpp>
#include <mlibc/debug.hpp>
#include <errno.h>
namespace mlibc {
mem_file::mem_file(char **ptr, size_t *sizeloc, void (*do_dispose)(abstract_file *))
: abstract_file{do_dispose}, _bufloc{ptr}, _sizeloc{sizeloc}, _buf{getAllocator()} { }
int mem_file::close() {
_update_ptrs();
_buf.detach();
return 0;
}
int mem_file::determine_type(stream_type *type) {
*type = stream_type::file_like;
return 0;
}
int mem_file::determine_bufmode(buffer_mode *mode) {
*mode = buffer_mode::no_buffer;
return 0;
}
int mem_file::io_read(char *buffer, size_t max_size, size_t *actual_size) {
return EINVAL;
}
int mem_file::io_write(const char *buffer, size_t max_size, size_t *actual_size) {
if (_pos + max_size >= _buf.size()) {
_buf.resize(_pos + max_size + 1, '\0');
_update_ptrs();
}
memcpy(_buf.data() + _pos, buffer, max_size);
_pos += max_size;
*actual_size = max_size;
return 0;
}
int mem_file::io_seek(off_t offset, int whence, off_t *new_offset) {
switch (whence) {
case SEEK_SET:
_pos = offset;
if (_pos >= _buf.size()) {
_buf.resize(_pos + 1, '\0');
_update_ptrs();
}
*new_offset = _pos;
break;
case SEEK_CUR:
_pos += offset;
if (_pos >= _buf.size()) {
_buf.resize(_pos + 1, '\0');
_update_ptrs();
}
*new_offset = _pos;
break;
case SEEK_END:
_pos = _buf.size() ? _buf.size() - 1 + offset : _buf.size() + offset;
_buf.resize(_pos + 1, '\0');
_update_ptrs();
*new_offset = _pos;
break;
default:
__ensure(!"Unknown whence value passed!");
__builtin_unreachable();
}
return 0;
}
void mem_file::_update_ptrs() {
*_bufloc = _buf.data();
*_sizeloc = _buf.size() - 1;
}
} // namespace mlibc

View file

@ -6,6 +6,7 @@
#include <bits/ensure.h>
#include <mlibc/debug.hpp>
#include <mlibc/file-io.hpp>
#include <mlibc/posix-file-io.hpp>
FILE *fmemopen(void *__restrict, size_t, const char *__restrict) {
__ensure(!"Not implemented");
@ -22,11 +23,9 @@ FILE *popen(const char*, const char *) {
__builtin_unreachable();
}
FILE *open_memstream(char **, size_t *) {
mlibc::infoLogger() << "\e[31mmlibc: open_memstream() always fails"
<< "\e[39m" << frg::endlog;
errno = ENOMEM;
return nullptr;
FILE *open_memstream(char **buf, size_t *sizeloc) {
return frg::construct<mlibc::mem_file>(getAllocator(), buf, sizeloc,
[] (mlibc::abstract_file *abstract) { frg::destruct(getAllocator(), abstract); });
}
int fseeko(FILE *file_base, off_t offset, int whence) {

View file

@ -0,0 +1,34 @@
#ifndef MLIBC_POSIX_FILE_IO_HPP
#define MLIBC_POSIX_FILE_IO_HPP
#include <frg/vector.hpp>
#include <mlibc/file-io.hpp>
#include <mlibc/allocator.hpp>
namespace mlibc {
struct mem_file : abstract_file {
mem_file(char **ptr, size_t *sizeloc, void (*do_dispose)(abstract_file *) = nullptr);
int close() override;
protected:
int determine_type(stream_type *type) override;
int determine_bufmode(buffer_mode *mode) override;
int io_read(char *buffer, size_t max_size, size_t *actual_size) override;
int io_write(const char *buffer, size_t max_size, size_t *actual_size) override;
int io_seek(off_t offset, int whence, off_t *new_offset) override;
private:
void _update_ptrs();
// Where to write back buffer and size on flush and close.
char **_bufloc;
size_t *_sizeloc;
// Actual buffer.
frg::vector<char, MemoryAllocator> _buf;
size_t _pos;
};
} // namespace mlibc
#endif // MLIBC_POSIX_FILE_IO_HPP

View file

@ -15,6 +15,7 @@ libc_sources += files(
'generic/netdb-stubs.cpp',
'generic/net-if-stubs.cpp',
'generic/posix_ctype.cpp',
'generic/posix-file-io.cpp',
'generic/posix_locale.cpp',
'generic/posix_signal.cpp',
'generic/posix_stdio.cpp',

View file

@ -25,7 +25,8 @@ posix_test_cases = [
'posix_spawn',
'index',
'rindex',
'search'
'search',
'open_memstream'
]
glibc_test_cases = [

View file

@ -0,0 +1,63 @@
#include <stdio.h>
#include <assert.h>
#define WRITE_NO 1024
int main() {
size_t size;
char *buf;
FILE *fp = open_memstream(&buf, &size);
assert(fp);
char c = 'A';
for (int i = 0; i < WRITE_NO; i++)
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
// Flush the file to update the pointers.
assert(!fflush(fp));
assert(size == WRITE_NO);
for (int i = 0; i < size; i++)
assert(buf[i] == c);
// Check if the stream maintains a null-bye at the end.
assert(buf[size] == '\0');
// Stream should be expanded, size should be == 2*WRITE_NO.
assert(!fseek(fp, WRITE_NO, SEEK_END));
assert(!fflush(fp));
assert(size == 2*WRITE_NO);
assert(buf[size] == '\0');
// Check if it's filled with zero's.
for (int i = WRITE_NO; i < size; i++)
assert(buf[i] == '\0');
// Go back and overwrite the 0's with 'B'.
assert(!fseek(fp, -WRITE_NO, SEEK_CUR));
c = 'B';
for (int i = 0; i < WRITE_NO; i++)
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
// Check if that happened.
assert(size == 2*WRITE_NO);
for (int i = WRITE_NO; i < size; i++)
assert(buf[i] == c);
assert(buf[size] == '\0');
// Go to the front and write 'B'.
assert(!fseek(fp, 0, SEEK_SET));
for (int i = 0; i < WRITE_NO; i++)
assert(fwrite(&c, sizeof(char), 1, fp) == 1);
// Check if that happened.
assert(size == 2*WRITE_NO);
for (int i = 0; i < size; i++)
assert(buf[i] == c);
assert(buf[size] == '\0');
// Close the file, we have tested everything.
assert(!fclose(fp));
return 0;
}