/** * 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 #include #ifndef _WIN32 #include #include #include #include #include #endif // _WIN32 #if !defined(_WIN32) && !defined(__APPLE__) && !defined(__FreeBSD__) #include #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; }