Merge pull request #266 from Geertiebear/open_memstream
Open memstream implementation and global_file_list fix
This commit is contained in:
commit
4e0d8211d2
|
@ -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"
|
||||
|
|
79
options/posix/generic/posix-file-io.cpp
Normal file
79
options/posix/generic/posix-file-io.cpp
Normal 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
|
|
@ -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) {
|
||||
|
|
34
options/posix/include/mlibc/posix-file-io.hpp
Normal file
34
options/posix/include/mlibc/posix-file-io.hpp
Normal 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
|
|
@ -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',
|
||||
|
|
|
@ -25,7 +25,8 @@ posix_test_cases = [
|
|||
'posix_spawn',
|
||||
'index',
|
||||
'rindex',
|
||||
'search'
|
||||
'search',
|
||||
'open_memstream'
|
||||
]
|
||||
|
||||
glibc_test_cases = [
|
||||
|
|
63
tests/posix/open_memstream.c
Normal file
63
tests/posix/open_memstream.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue