open_toontown_panda3d/direct/src/plugin/handleStreamBuf.cxx

343 lines
6.8 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file handleStreamBuf.cxx
* @author drose
* @date 2009-06-05
*/
#include "handleStreamBuf.h"
#include <assert.h>
#include <string.h>
#ifndef _WIN32
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#endif // _WIN32
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__FreeBSD__)
#include <libio.h>
#endif // !_WIN32 && !__APPLE__ && !__FreeBSD__
using std::cerr;
using std::dec;
using std::hex;
static const size_t handle_buffer_size = 4096;
/**
*
*/
HandleStreamBuf::
HandleStreamBuf() {
_is_open_read = false;
_is_open_write = false;
_handle = invalid_fhandle;
INIT_LOCK(_lock);
_buffer = new char[handle_buffer_size];
char *ebuf = _buffer + handle_buffer_size;
setg(_buffer, ebuf, ebuf);
setp(_buffer, ebuf);
}
/**
*
*/
HandleStreamBuf::
~HandleStreamBuf() {
close();
delete[] _buffer;
DESTROY_LOCK(_lock);
}
/**
* Attempts to open the given handle for input. The stream may not be
* simultaneously open for input and output.
*/
void HandleStreamBuf::
open_read(FHandle handle) {
close();
_handle = handle;
_is_open_read = true;
}
/**
* Attempts to open the given handle for output. The stream may not be
* simultaneously open for input and output.
*/
void HandleStreamBuf::
open_write(FHandle handle) {
close();
_handle = handle;
_is_open_write = true;
}
/**
* Returns true if the file is open for input, false otherwise.
*/
bool HandleStreamBuf::
is_open_read() const {
return _is_open_read;
}
/**
* Returns true if the file is open for output, false otherwise.
*/
bool HandleStreamBuf::
is_open_write() const {
return _is_open_write;
}
/**
* Empties the buffer and closes the file.
*/
void HandleStreamBuf::
close() {
// Make sure the write buffer is flushed.
sync();
close_handle();
pbump(pbase() - pptr());
gbump(egptr() - gptr());
}
/**
* Closes the underlying handle, *without* attempting to flush the stream.
*/
void HandleStreamBuf::
close_handle() {
#ifdef _WIN32
if (_handle != nullptr) {
CloseHandle(_handle);
}
_handle = nullptr;
#else
if (_handle != -1) {
::close(_handle);
}
_handle = -1;
#endif // _WIN32
_is_open_read = false;
_is_open_write = false;
}
/**
* Called by the system ostream implementation when its internal buffer is
* filled, plus one character.
*/
int HandleStreamBuf::
overflow(int ch) {
ACQUIRE_LOCK(_lock);
bool okflag = true;
assert(pptr() >= pbase());
size_t n = pptr() - pbase();
if (n != 0) {
size_t wrote = write_chars(pbase(), n);
assert(wrote <= n);
pbump(-(int)wrote);
if (wrote != n) {
okflag = false;
}
}
if (okflag && ch != EOF) {
if (pptr() != epptr()) {
// Store the extra character back in the buffer.
*(pptr()) = ch;
pbump(1);
} else {
// No room to store ch.
okflag = false;
}
}
RELEASE_LOCK(_lock);
if (!okflag) {
return EOF;
}
return 0;
}
/**
* Called by the system iostream implementation to implement a flush
* operation.
*/
int HandleStreamBuf::
sync() {
ACQUIRE_LOCK(_lock);
assert(pptr() >= pbase());
size_t n = pptr() - pbase();
size_t wrote = write_chars(pbase(), n);
assert(wrote <= n);
pbump(-(int)wrote);
RELEASE_LOCK(_lock);
if (n != wrote) {
return EOF;
}
return 0;
}
/**
* Called by the system istream implementation when its internal buffer needs
* more characters.
*/
int HandleStreamBuf::
underflow() {
ACQUIRE_LOCK(_lock);
// Sometimes underflow() is called even if the buffer is not empty.
if (gptr() >= egptr()) {
// Mark the buffer filled (with buffer_size bytes).
size_t buffer_size = egptr() - eback();
gbump(-(int)buffer_size);
size_t num_bytes = buffer_size;
size_t read_count = read_chars(gptr(), buffer_size);
if (read_count != num_bytes) {
// Oops, we didn't read what we thought we would.
if (read_count == 0) {
gbump(num_bytes);
RELEASE_LOCK(_lock);
return EOF;
}
// Slide what we did read to the top of the buffer.
assert(read_count < num_bytes);
size_t delta = num_bytes - read_count;
memmove(gptr() + delta, gptr(), read_count);
gbump(delta);
}
}
unsigned char next = *gptr();
RELEASE_LOCK(_lock);
return next;
}
/**
* Attempts to extract the indicated number of characters from the current
* file position. Returns the number of characters extracted.
*/
size_t HandleStreamBuf::
read_chars(char *start, size_t length) {
if (length == 0 || !_is_open_read) {
// Trivial no-op.
return 0;
}
// Make sure the write buffer is flushed.
sync();
if (length == 0) {
return 0;
}
#ifdef _WIN32
// Windows case.
DWORD bytes_read = 0;
BOOL success = ReadFile(_handle, start, length, &bytes_read, nullptr);
if (!success) {
DWORD error = GetLastError();
if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) {
cerr << "Error reading " << length
<< " bytes, windows error code 0x" << hex
<< error << dec << ".\n";
return 0;
}
}
length = bytes_read;
#else
// Posix case.
ssize_t result = ::read(_handle, start, length);
if (result < 0) {
cerr << "Error reading " << length << " bytes\n";
return 0;
}
length = result;
#endif // _WIN32
return length;
}
/**
* Outputs the indicated stream of characters to the current file position.
*/
size_t HandleStreamBuf::
write_chars(const char *start, size_t length) {
if (length == 0) {
// Trivial no-op.
return 0;
}
// Make sure the read buffer is flushed.
size_t n = egptr() - gptr();
gbump(n);
if (length == 0 || !_is_open_write) {
return 0;
}
#ifdef _WIN32
// Windows case.
DWORD bytes_written = 0;
BOOL success = WriteFile(_handle, start, length, &bytes_written, nullptr);
if (!success) {
assert(bytes_written <= length);
DWORD error = GetLastError();
if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) {
cerr << "Error writing " << length
<< " bytes, windows error code 0x" << hex
<< error << dec << ".\n";
}
return bytes_written;
}
assert(bytes_written == length);
#else
// Posix case.
size_t remaining = length;
while (remaining > 0) {
ssize_t result = ::write(_handle, start, remaining);
if (result < 0) {
if (errno != EPIPE) {
cerr << "Error writing " << remaining << " bytes\n";
}
return length - remaining;
}
start += result;
remaining -= result;
}
#endif // _WIN32
return length;
}