#include <stdlib.h>
#include "MemoryStream.h"
#include "math_ops.h"
#include "error.h"
#define _(X) X
MemoryStream::MemoryStream() : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
{
data_buffer_size = 0;
data_buffer_alloced = 64;
if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
}
MemoryStream::MemoryStream(uint64 alloc_hint, int alloc_hint_is_size) : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
{
if(alloc_hint_is_size != 0)
{
data_buffer_size = alloc_hint;
data_buffer_alloced = alloc_hint;
if(alloc_hint > SIZE_MAX)
throw MDFN_Error(ErrnoHolder(ENOMEM));
}
else
{
data_buffer_size = 0;
data_buffer_alloced = (alloc_hint > SIZE_MAX) ? SIZE_MAX : alloc_hint;
}
if(!(data_buffer = (uint8*)realloc(data_buffer, data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
if(alloc_hint_is_size > 0)
memset(data_buffer, 0, data_buffer_size);
}
MemoryStream::MemoryStream(Stream *stream, uint64 size_limit) : data_buffer(NULL), data_buffer_size(0), data_buffer_alloced(0), position(0)
{
try
{
if((position = stream->tell()) != 0)
stream->seek(0, SEEK_SET);
void* tp;
data_buffer_size = data_buffer_alloced = stream->alloc_and_read(&tp, size_limit);
data_buffer = (uint8*)tp;
stream->close();
}
catch(...)
{
if(data_buffer)
{
free(data_buffer);
data_buffer = NULL;
}
delete stream;
throw;
}
delete stream;
}
MemoryStream::MemoryStream(const MemoryStream &zs)
{
data_buffer_size = zs.data_buffer_size;
data_buffer_alloced = zs.data_buffer_alloced;
if(!(data_buffer = (uint8*)malloc(data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
memcpy(data_buffer, zs.data_buffer, data_buffer_size);
position = zs.position;
}
#if 0
MemoryStream & MemoryStream::operator=(const MemoryStream &zs)
{
if(this != &zs)
{
if(data_buffer)
{
free(data_buffer);
data_buffer = NULL;
}
data_buffer_size = zs.data_buffer_size;
data_buffer_alloced = zs.data_buffer_alloced;
if(!(data_buffer = (uint8*)malloc(data_buffer_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
memcpy(data_buffer, zs.data_buffer, data_buffer_size);
position = zs.position;
}
return(*this);
}
#endif
MemoryStream::~MemoryStream()
{
if(data_buffer)
{
free(data_buffer);
data_buffer = NULL;
}
}
uint64 MemoryStream::attributes(void)
{
return (ATTRIBUTE_READABLE | ATTRIBUTE_WRITEABLE | ATTRIBUTE_SEEKABLE);
}
uint8 *MemoryStream::map(void) noexcept
{
return data_buffer;
}
uint64 MemoryStream::map_size(void) noexcept
{
return data_buffer_size;
}
void MemoryStream::unmap(void) noexcept
{
}
INLINE void MemoryStream::grow_if_necessary(uint64 new_required_size, uint64 hole_end)
{
if(new_required_size > data_buffer_size)
{
const uint64 old_data_buffer_size = data_buffer_size;
if(new_required_size > data_buffer_alloced)
{
uint64 new_required_alloced = round_up_pow2(new_required_size);
uint8 *new_data_buffer;
if(new_required_alloced < new_required_size || new_required_alloced > SIZE_MAX)
new_required_alloced = SIZE_MAX;
if(new_required_alloced < new_required_size)
throw MDFN_Error(ErrnoHolder(ENOMEM));
if(!(new_data_buffer = (uint8*)realloc(data_buffer, new_required_alloced)))
throw MDFN_Error(ErrnoHolder(errno));
data_buffer = new_data_buffer;
data_buffer_size = new_required_size;
data_buffer_alloced = new_required_alloced;
}
else
data_buffer_size = new_required_size;
if(hole_end > old_data_buffer_size)
memset(data_buffer + old_data_buffer_size, 0, hole_end - old_data_buffer_size);
}
}
void MemoryStream::shrink_to_fit(void) noexcept
{
if(data_buffer_alloced > data_buffer_size)
{
uint8 *new_data_buffer;
new_data_buffer = (uint8*)realloc(data_buffer, data_buffer_size);
if(new_data_buffer != NULL)
{
data_buffer = new_data_buffer;
data_buffer_alloced = data_buffer_size;
}
}
}
uint64 MemoryStream::read(void *data, uint64 count, bool error_on_eos)
{
if(count > data_buffer_size)
{
if(error_on_eos)
throw MDFN_Error(0, _("Unexpected EOF"));
count = data_buffer_size;
}
if(position > (data_buffer_size - count))
{
if(error_on_eos)
throw MDFN_Error(0, _("Unexpected EOF"));
if(data_buffer_size > position)
count = data_buffer_size - position;
else
count = 0;
}
memmove(data, &data_buffer[position], count);
position += count;
return count;
}
void MemoryStream::write(const void *data, uint64 count)
{
uint64 nrs = position + count;
if(nrs < position)
throw MDFN_Error(ErrnoHolder(EFBIG));
grow_if_necessary(nrs, position);
memmove(&data_buffer[position], data, count);
position += count;
}
void MemoryStream::truncate(uint64 length)
{
grow_if_necessary(length, length);
data_buffer_size = length;
}
void MemoryStream::seek(int64 offset, int whence)
{
uint64 new_position;
switch(whence)
{
default:
throw MDFN_Error(ErrnoHolder(EINVAL));
break;
case SEEK_SET:
new_position = offset;
break;
case SEEK_CUR:
new_position = position + offset;
break;
case SEEK_END:
new_position = data_buffer_size + offset;
break;
}
if(new_position < 0)
throw MDFN_Error(ErrnoHolder(EINVAL));
position = new_position;
}
uint64 MemoryStream::tell(void)
{
return position;
}
uint64 MemoryStream::size(void)
{
return data_buffer_size;
}
void MemoryStream::flush(void)
{
}
void MemoryStream::close(void)
{
}
int MemoryStream::get_line(std::string &str)
{
str.clear();
while((uint64)position < data_buffer_size)
{
uint8 c = data_buffer[position++];
if(c == '\r' || c == '\n' || c == 0)
return(c);
str.push_back(c);
}
return(str.length() ? 256 : -1);
}