331 lines
7.5 KiB
C++
331 lines
7.5 KiB
C++
/* Mednafen - Multi-system Emulator
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "MemoryStream.h"
|
|
#include "math_ops.h"
|
|
#include "error.h"
|
|
|
|
//work around gettext
|
|
#define _(X) X
|
|
|
|
/*
|
|
TODO: Copy and assignment constructor fixes.
|
|
|
|
Proper negative position behavior?
|
|
*/
|
|
|
|
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;
|
|
|
|
// first condition will happen at new_required_size > (1ULL << 63) due to round_up_pow2() "wrapping".
|
|
// second condition can occur when running on a 32-bit system.
|
|
if(new_required_alloced < new_required_size || new_required_alloced > SIZE_MAX)
|
|
new_required_alloced = SIZE_MAX;
|
|
|
|
// If constrained alloc size isn't enough, throw an out-of-memory/address-space type error.
|
|
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));
|
|
|
|
//
|
|
// Assign all in one go after the realloc() so we don't leave our object in an inconsistent state if the realloc() fails.
|
|
//
|
|
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)
|
|
{
|
|
//printf("%llu %llu %llu\n", position, count, data_buffer_size);
|
|
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Don't add code to reduce the amount of memory allocated(when possible) without providing a
|
|
// per-stream setting to disable that behavior.
|
|
//
|
|
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(); // or str.resize(0)??
|
|
|
|
while((uint64)position < data_buffer_size)
|
|
{
|
|
uint8 c = data_buffer[position++];
|
|
|
|
if(c == '\r' || c == '\n' || c == 0)
|
|
return(c);
|
|
|
|
str.push_back(c); // Should be faster than str.append(1, c)
|
|
}
|
|
|
|
return(str.length() ? 256 : -1);
|
|
}
|
|
|