📄 tu_file.cpp
字号:
// tu_file.cpp -- Ignacio Casta駉, Thatcher Ulrich <tu@tulrich.com> 2003
// This source code has been donated to the Public Domain. Do
// whatever you want with it.
// A file class that can be customized with callbacks.
#include "base/tu_file.h"
#include "base/utility.h"
#include "base/container.h"
#include "base/membuf.h"
//
// tu_file functions using FILE
//
static int std_read_func(void* dst, int bytes, void* appdata)
// Return the number of bytes actually read. EOF or an error would
// cause that to not be equal to "bytes".
{
assert(appdata);
assert(dst);
return fread( dst, 1, bytes, (FILE *)appdata );
}
static int std_write_func(const void* src, int bytes, void* appdata)
// Return the number of bytes actually written.
{
assert(appdata);
assert(src);
return fwrite( src, 1, bytes, (FILE *)appdata );
}
static int std_seek_func(int pos, void *appdata)
// Return 0 on success, or TU_FILE_SEEK_ERROR on failure.
{
assert(appdata);
clearerr((FILE*) appdata); // make sure EOF flag is cleared.
int result = fseek((FILE*)appdata, pos, SEEK_SET);
if (result == EOF)
{
// @@ TODO should set m_error to something relevant based on errno.
return TU_FILE_SEEK_ERROR;
}
return 0;
}
static int std_seek_to_end_func(void *appdata)
// Return 0 on success, TU_FILE_SEEK_ERROR on failure.
{
assert(appdata);
int result = fseek((FILE*)appdata, 0, SEEK_END);
if (result == EOF)
{
// @@ TODO should set m_error to something relevant based on errno.
return TU_FILE_SEEK_ERROR;
}
return 0;
}
static int std_tell_func(const void *appdata)
// Return the file position, or -1 on failure.
{
assert(appdata);
return ftell((FILE*)appdata);
}
static bool std_get_eof_func(void *appdata)
// Return true if we're at EOF.
{
assert(appdata);
if (feof((FILE*) appdata))
{
return true;
}
else
{
return false;
}
}
static int std_close_func(void *appdata)
// Return 0 on success, or TU_FILE_CLOSE_ERROR on failure.
{
assert(appdata);
int result = fclose((FILE*)appdata);
if (result == EOF)
{
// @@ TODO should set m_error to something relevant based on errno.
return TU_FILE_CLOSE_ERROR;
}
return 0;
}
//
// tu_file functions using a readable/writable memory buffer
//
struct filebuf
{
membuf m_;
int m_position;
bool m_read_only;
filebuf()
:
m_position(0),
m_read_only(false)
{
}
filebuf(int size, void* data)
:
m_(membuf::READ_ONLY, data, size),
m_position(0),
m_read_only(true)
{
}
~filebuf()
{
}
bool resize(int new_size)
// Return false if we couldn't resize.
{
if (m_read_only) return false;
m_.resize(new_size);
// Hm, does this make sense? We're truncating the file, so clamping the cursor.
// Alternative would be to disallow resize, but that doesn't seem good either.
if (m_position > m_.size())
{
m_position = m_.size();
}
return true;
}
bool is_valid()
{
return
m_position >= 0
&& m_position <= m_.size();
}
unsigned char* get_cursor() { return ((unsigned char*) m_.data()) + m_position; }
};
static int mem_read_func(void* dst, int bytes, void* appdata)
// Return the number of bytes actually read. EOF or an error would
// cause that to not be equal to "bytes".
{
assert(appdata);
assert(dst);
filebuf* buf = (filebuf*) appdata;
assert(buf->is_valid());
int bytes_to_read = imin(bytes, buf->m_.size() - buf->m_position);
if (bytes_to_read)
{
memcpy(dst, buf->get_cursor(), bytes_to_read);
}
buf->m_position += bytes_to_read;
return bytes_to_read;
}
static int mem_write_func(const void* src, int bytes, void* appdata)
// Return the number of bytes actually written.
{
assert(appdata);
assert(src);
filebuf* buf = (filebuf*) appdata;
assert(buf->is_valid());
// Expand buffer if necessary.
int bytes_to_expand = imax(0, buf->m_position + bytes - buf->m_.size());
if (bytes_to_expand)
{
if (buf->resize(buf->m_.size() + bytes_to_expand) == false)
{
// Couldn't expand!
return 0;
}
}
memcpy(buf->get_cursor(), src, bytes);
buf->m_position += bytes;
return bytes;
}
static int mem_seek_func(int pos, void *appdata)
// Return 0 on success, or TU_FILE_SEEK_ERROR on failure.
{
assert(appdata);
assert(pos >= 0);
filebuf* buf = (filebuf*) appdata;
assert(buf->is_valid());
if (pos < 0)
{
buf->m_position = 0;
return TU_FILE_SEEK_ERROR;
}
if (pos > buf->m_.size())
{
buf->m_position = buf->m_.size();
return TU_FILE_SEEK_ERROR;
}
buf->m_position = pos;
return 0;
}
static int mem_seek_to_end_func(void* appdata)
// Return 0 on success, TU_FILE_SEEK_ERROR on failure.
{
assert(appdata);
filebuf* buf = (filebuf*) appdata;
assert(buf->is_valid());
buf->m_position = buf->m_.size();
return 0;
}
static int mem_tell_func(const void* appdata)
// Return the file position, or -1 on failure.
{
assert(appdata);
filebuf* buf = (filebuf*) appdata;
assert(buf->is_valid());
return buf->m_position;
}
static bool mem_get_eof_func(void* appdata)
// Return true if we're positioned at the end of the buffer.
{
assert(appdata);
filebuf* buf = (filebuf*) appdata;
assert(buf->is_valid());
return buf->m_position >= buf->m_.size();
}
static int mem_close_func(void* appdata)
// Return 0 on success, or TU_FILE_CLOSE_ERROR on failure.
{
assert(appdata);
filebuf* buf = (filebuf*) appdata;
assert(buf->is_valid());
delete buf;
return 0;
}
//
// generic functionality
//
tu_file::tu_file(
void * appdata,
read_func rf,
write_func wf,
seek_func sf,
seek_to_end_func ef,
tell_func tf,
get_eof_func gef,
close_func cf)
// Create a file using the custom callbacks.
{
m_data = appdata;
m_read = rf;
m_write = wf;
m_seek = sf;
m_seek_to_end = ef;
m_tell = tf;
m_get_eof = gef;
m_close = cf;
m_error = TU_FILE_NO_ERROR;
}
tu_file::tu_file(FILE* fp, bool autoclose=false)
// Create a file from a standard file pointer.
{
m_data = (void *)fp;
m_read = std_read_func;
m_write = std_write_func;
m_seek = std_seek_func;
m_seek_to_end = std_seek_to_end_func;
m_tell = std_tell_func;
m_get_eof = std_get_eof_func;
m_close = autoclose ? std_close_func : NULL;
m_error = TU_FILE_NO_ERROR;
}
tu_file::tu_file(const char * name, const char * mode)
// Create a file from the given name and the given mode.
{
m_data = fopen(name, mode);
if (m_data)
{
m_read = std_read_func;
m_write = std_write_func;
m_seek = std_seek_func;
m_seek_to_end = std_seek_to_end_func;
m_tell = std_tell_func;
m_get_eof = std_get_eof_func;
m_close = std_close_func;
m_error = TU_FILE_NO_ERROR;
}
else
{
m_read = NULL;
m_write = NULL;
m_seek = NULL;
m_seek_to_end = NULL;
m_tell = NULL;
m_get_eof = NULL;
m_close = NULL;
m_error = TU_FILE_OPEN_ERROR;
}
}
tu_file::tu_file(memory_buffer_enum m)
// Create a read/write memory buffer.
{
m_data = new membuf;
m_read = mem_read_func;
m_write = mem_write_func;
m_seek = mem_seek_func;
m_seek_to_end = mem_seek_to_end_func;
m_tell = mem_tell_func;
m_get_eof = mem_get_eof_func;
m_close = mem_close_func;
m_error = TU_FILE_NO_ERROR;
}
tu_file::tu_file(memory_buffer_enum m, int size, void* data)
// Create a read-only memory buffer, using the given data.
{
m_data = new membuf(data, size);
m_read = mem_read_func;
m_write = mem_write_func;
m_seek = mem_seek_func;
m_seek_to_end = mem_seek_to_end_func;
m_tell = mem_tell_func;
m_get_eof = mem_get_eof_func;
m_close = mem_close_func;
m_error = TU_FILE_NO_ERROR;
}
tu_file::~tu_file()
// Close this file when destroyed.
{
close();
}
void tu_file::close()
// Close this file.
{
if (m_close)
{
m_close(m_data);
}
m_data = NULL;
m_read = NULL;
m_write = NULL;
m_seek = NULL;
m_tell = NULL;
m_close = NULL;
}
void tu_file::copy_from(tu_file* src)
// Copy remaining contents of *src into *this.
{
// @@ bah, should buffer this!
while (src->get_eof() == false)
{
Uint8 b = src->read8();
if (src->get_error())
{
break;
}
write8(b);
}
}
void tu_file::copy_to(membuf* dst)
// Copy remaining contents of *this into *dst.
{
static const int BUFSIZE = 4096;
while (get_eof() == false)
{
// Make room at the end of dst.
dst->resize(dst->size() + BUFSIZE);
int bytes_read = read_bytes(((char*) dst->data()) + dst->size() - BUFSIZE, BUFSIZE);
if (bytes_read < BUFSIZE) {
// Didn't use everything we allocated; trim the unused bytes.
dst->resize(dst->size() - (BUFSIZE - bytes_read));
}
if (get_error())
{
break;
}
}
}
int tu_file::copy_bytes(tu_file* src, int byte_count)
// Copy a fixed number of bytes from *src into *this. Return the
// number of bytes copied.
{
static const int BUFSIZE = 4096;
char buffer[BUFSIZE];
int bytes_left = byte_count;
while (bytes_left)
{
int to_copy = imin(bytes_left, BUFSIZE);
int read_count = src->read_bytes(buffer, to_copy);
int write_count = write_bytes(buffer, read_count);
assert(write_count <= read_count);
assert(read_count <= to_copy);
assert(to_copy <= bytes_left);
bytes_left -= write_count;
if (write_count < to_copy)
{
// Some kind of error; abort.
return byte_count - bytes_left;
}
}
assert(bytes_left == 0);
return byte_count;
}
void tu_file::write_string(const char* src)
{
for (;;)
{
write8(*src);
if (*src == 0)
{
break;
}
src++;
}
}
int tu_file::read_string(char* dst, int max_length)
{
int i=0;
while (i<max_length)
{
dst[i] = read8();
if (dst[i]=='\0')
{
return i;
}
i++;
}
dst[max_length - 1] = 0; // force termination.
return -1;
}
#include <stdarg.h>
#include <string.h>
#ifdef _WIN32
#define vsnprintf _vsnprintf
#endif // _WIN32
int tu_file::printf(const char* fmt, ...)
// Use printf-like semantics to send output to this stream.
{
// Workspace for vsnprintf formatting.
static const int BUFFER_SIZE = 1000;
char buffer[BUFFER_SIZE];
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, BUFFER_SIZE, fmt, ap);
va_end(ap);
return write_bytes(buffer, strlen(buffer));
}
// Local Variables:
// mode: C++
// c-basic-offset: 8
// tab-width: 8
// indent-tabs-mode: t
// End:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -