// read the entire file into a ConfigVar instance and then use that to decode // into mapped variables. bool ConfigReader::load(const char *fileName) { struct stat stbuf; memset(&stbuf, 0, sizeof(struct stat)); if (lstat(fileName, &stbuf) != 0) return false; int size = stbuf.st_size; int fd = open(fileName, O_RDONLY); if (fd < 0) return false; char *buf = new char[size]; int res = ::read(fd, buf, size); close(fd); if (res != size) { RLOG(WARNING) << "Partial read of config file, expecting " << size << " bytes, got " << res; delete[] buf; return false; } ConfigVar in; in.write((unsigned char *)buf, size); delete[] buf; return loadFromVar(in); }
// read the entire file into a ConfigVar instance and then use that to decode // into mapped variables. bool ConfigReader::load(const char *fileName) { struct stat_st stbuf; memset(&stbuf, 0, sizeof(struct stat_st)); if (unix::lstat(fileName, &stbuf) != 0) return false; int size = stbuf.st_size; int fd = unix::open(fileName, O_RDONLY); if (fd < 0) return false; char *buf = new char[size]; int res = ::_read(fd, buf, size); close(fd); if (res != size) { rWarning("Partial read of config file, expecting %i bytes, got %i", size, res); delete[] buf; return false; } ConfigVar in; in.write((unsigned char *)buf, size); delete[] buf; return loadFromVar(in); }
bool Config::Init(const char* filename_) { FILE* fp; char buffer[1024]; char token[1024]; char name[1024]; char* p; int nline; ConfigVar* var; filename = filename_; fp = fopen(filename, "r"); if (!fp) return false; nline = 0; while (fgets(buffer, sizeof(buffer) - 1, fp)) { buffer[sizeof(buffer) - 1] = '\0'; nline++; p = ParseToken(buffer, token, sizeof(token)); if (!p) { // empty or commented line continue; } strncpy(name, token, strlen(token)); name[strlen(token)] = '\0'; p = ParseToken(p, token, sizeof(token)); if (!p || token[0] != '=') { // syntax error Log_Message("syntax error at %s, line %d", filename, nline); fclose(fp); return false; } var = new ConfigVar(name); vars.Enqueue(var); while (true) { p = ParseToken(p, token, sizeof(token)); if (!p) break; if (token[0] == ',') continue; var->Append(token); } } fclose(fp); return true; }
const char* Config::GetListValue(const char* name, int num, const char* defval) { ConfigVar* var; var = GetVar(name); if (!var) return defval; return var->GetListValue(num, defval); }
int64_t Config::GetInt64Value(const char* name, int64_t defval) { ConfigVar* var; var = GetVar(name); if (!var) return defval; return var->GetInt64Value(defval); }
bool Config::GetBoolValue(const char* name, bool defval) { ConfigVar* var; var = GetVar(name); if (!var) return defval; return var->GetBoolValue(defval); }
int Config::GetListNum(const char* name) { ConfigVar* var; var = GetVar(name); if (!var) return 0; return var->GetListNum(); }
// take in a DM plugin configuration file, find the DMPlugin descendent matching the value of plugname, and store its class factory funcs for later use DMPlugin* dm_plugin_load(const char *pluginConfigPath) { ConfigVar cv; if (cv.readVar(pluginConfigPath, "=") > 0) { if (!is_daemonised) { std::cerr << "Unable to load plugin config: " << pluginConfigPath << std::endl; } syslog(LOG_ERR, "Unable to load plugin config %s", pluginConfigPath); return NULL; } String plugname(cv["plugname"]); if (plugname.length() < 1) { if (!is_daemonised) { std::cerr << "Unable read plugin config plugname variable: " << pluginConfigPath << std::endl; } syslog(LOG_ERR, "Unable read plugin config plugname variable %s", pluginConfigPath); return NULL; } if (plugname == "default") { #ifdef DGDEBUG std::cout << "Enabling default DM plugin" << std::endl; #endif return defaultdmcreate(cv); } #ifdef ENABLE_FANCYDM if (plugname == "fancy") { #ifdef DGDEBUG std::cout << "Enabling fancy DM plugin" << std::endl; #endif return fancydmcreate(cv); } #endif #ifdef ENABLE_TRICKLEDM if (plugname == "trickle") { #ifdef DGDEBUG std::cout << "Enabling trickle DM plugin" << std::endl; #endif return trickledmcreate(cv); } #endif if (!is_daemonised) { std::cerr << "Unable to load plugin: " << plugname << std::endl; } syslog(LOG_ERR, "Unable to load plugin %s", plugname.toCharArray()); return NULL; }
void Config::AppendListValue(const char* name, const char* value) { ConfigVar* var; var = GetVar(name); if (!var) { var = new ConfigVar(name); vars.Enqueue(var); } var->Append(value); }
void Config::SetValue(const char* name, const char *value) { ConfigVar* var; var = GetVar(name); if (!var) { var = new ConfigVar(name); vars.Enqueue(var); } else var->ClearValue(); var->Append(value); }
ConfigVar* Config::GetVar(const char* name) { ConfigVar* var; var = vars.First(); while (var) { if (var->NameEquals(name)) return var; var = var->next; } return NULL; }
//------------------------------------------------------------------------------ void Background2D::renderQueueEnded( Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation ) { if( cv_show_background2d.GetB() == false ) { return; } if( queueGroupId == Ogre::RENDER_QUEUE_MAIN ) { m_RenderSystem->_setWorldMatrix( Ogre::Matrix4::IDENTITY ); m_RenderSystem->_setProjectionMatrix( Ogre::Matrix4::IDENTITY ); Ogre::Viewport *viewport( CameraManager::getSingleton().getViewport() ); float width = viewport->getActualWidth(); float height = viewport->getActualHeight(); Ogre::Matrix4 view; view.makeTrans( Ogre::Vector3( m_PositionReal.x * 2 / width, -m_PositionReal.y * 2 / height, 0 ) ); m_RenderSystem->_setViewMatrix( view ); if( m_AlphaRenderOp.vertexData->vertexCount != 0 ) { m_SceneManager->_setPass( m_AlphaMaterial->getTechnique( 0 )->getPass( 0 ), true, false ); m_RenderSystem->_render( m_AlphaRenderOp ); } if( m_AddRenderOp.vertexData->vertexCount != 0 ) { m_SceneManager->_setPass( m_AddMaterial->getTechnique( 0 )->getPass( 0 ), true, false ); m_RenderSystem->_render( m_AddRenderOp ); } } }
static bool runTest(TestListener *listener, const std::string &suite, const std::string &testName, TestDg test) { if (listener) listener->testStarted(suite, testName); bool protect = !isDebuggerAttached(); protect = protect || g_protect->val(); if (protect) { try { test(); if (listener) listener->testComplete(suite, testName); } catch (const Assertion &assertion) { if (listener) listener->testAsserted(suite, testName, assertion); return false; } catch (...) { if (listener) listener->testException(suite, testName); return false; } } else { test(); if (listener) listener->testComplete(suite, testName); } return true; }
//------------------------------------------------------------------------------ void Background2D::InputDebug( const Event& event ) { if( cv_background2d_manual.GetB() == true ) { if( event.type == ET_KEY_IMPULSE && event.param1 == OIS::KC_W ) { m_PositionReal.y += 2; } else if( event.type == ET_KEY_IMPULSE && event.param1 == OIS::KC_A ) { m_PositionReal.x += 2; } else if( event.type == ET_KEY_IMPULSE && event.param1 == OIS::KC_S ) { m_PositionReal.y -= 2; } else if( event.type == ET_KEY_IMPULSE && event.param1 == OIS::KC_D ) { m_PositionReal.x -= 2; } CameraManager::getSingleton().Set2DScroll( m_PositionReal ); } }
BufferedStream::BufferedStream(Stream::ptr parent, bool own) : FilterStream(parent, own) { m_bufferSize = g_defaultBufferSize->val(); m_allowPartialReads = false; m_flushMultiplesOfBuffer = false; }
TempStream::TempStream(const std::string &prefix, bool deleteOnClose, IOManager *ioManager, Scheduler *scheduler) { std::string tempdir; bool absolutePath = #ifdef WINDOWS (prefix.size() >= 2 && (prefix[1] == ':' || prefix[1] == '\\')) || (!prefix.empty() && prefix[0] == '\\'); #else !prefix.empty() && prefix[0] == '/'; #endif if (!absolutePath) tempdir = g_tempDir->val(); #ifdef WINDOWS std::wstring wtempdir = toUtf16(tempdir); if (!absolutePath && wtempdir.empty()) { wtempdir.resize(MAX_PATH); DWORD len = GetTempPathW(MAX_PATH, &wtempdir[0]); if (len == 0) wtempdir = L"."; else wtempdir.resize(len); } std::wstring prefixW = toUtf16(prefix); size_t backslash = prefixW.rfind(L'\\'); if (backslash != std::wstring::npos) { wtempdir += prefixW.substr(0, backslash); prefixW = prefixW.substr(backslash + 1); } std::wstring tempfile; tempfile.resize(MAX_PATH); UINT len = GetTempFileNameW(wtempdir.c_str(), prefixW.c_str(), 0, &tempfile[0]); if (len == 0) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("GetTempFileNameW"); init(tempfile, FileStream::READWRITE, (FileStream::CreateFlags)(FileStream::OPEN | (deleteOnClose ? FileStream::DELETE_ON_CLOSE : 0)), ioManager, scheduler); #else if (!absolutePath && tempdir.empty()) tempdir = "/tmp/" + prefix + "XXXXXX"; else if (!absolutePath) tempdir += prefix + "XXXXXX"; else tempdir = prefix + "XXXXXX"; int fd = mkstemp(&tempdir[0]); if (fd < 0) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("mkstemp"); init(fd, ioManager, scheduler); if (deleteOnClose) { int rc = unlink(tempdir.c_str()); if (rc != 0) MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("unlink"); } m_path = tempdir; #endif }
void prefs_set_config_val_real (char *key, char *value, gboolean callback) { ConfigVar *entry = prefs_config_vars; while (entry->key) { if (strcasecmp (entry->key, key) == 0) { // NOTE: here the old value should not be freed because it is static entry->cur_val = g_strdup (value); if (callback && entry->callback) entry->callback (key, value); return; } entry++; } fprintf (stderr, "Warning: invalid key %s in config file\n", key); }
//------------------------------------------------------------------------------ void Background2D::applyScroll() { if( cv_background2d_manual.GetB() != true ) { m_PositionReal = GetScreenScroll(); CameraManager::getSingleton().Set2DScroll( m_PositionReal ); } }
bool ConfigReader::save(const char *fileName) const { // write everything to a ConfigVar, then output to disk ConfigVar out = toVar(); int fd = unix::open(fileName, O_RDWR | O_CREAT, 0640); if (fd >= 0) { int retVal = ::write(fd, out.buffer(), out.size()); close(fd); if (retVal != out.size()) { rError("Error writing to config file %s", fileName); return false; } } else { rError("Unable to open or create file %s", fileName); return false; } return true; }
bool ConfigReader::loadFromVar(ConfigVar &in) { in.resetOffset(); // parse. int numEntries = in.readInt(); for (int i = 0; i < numEntries; ++i) { string key, value; in >> key >> value; if (key.length() == 0) { rError("Invalid key encoding in buffer"); return false; } ConfigVar newVar(value); vars.insert(make_pair(key, newVar)); } return true; }
bool GameFrameListener::frameEnded( const Ogre::FrameEvent& evt ) { if( cv_debug_fps.GetB() == true ) { const Ogre::RenderTarget::FrameStats& stats = m_Window->getStatistics(); DEBUG_DRAW.SetTextAlignment( DEBUG_DRAW.LEFT ); DEBUG_DRAW.SetScreenSpace( true ); DEBUG_DRAW.SetColour( Ogre::ColourValue( 1, 1, 1, 1 ) ); DEBUG_DRAW.Text( 10, 10, "Current FPS:" + Ogre::StringConverter::toString( stats.lastFPS ) ); } return true; }
bool Config::Save() { FILE* fp; ConfigVar* var; fp = fopen(filename, "w"); if (!fp) return false; fprintf(fp, "# Automatically generated configuration file. DO NOT EDIT!\n\n"); for (var = vars.First(); var != NULL; var = var->next) { fprintf(fp, "%s = \"%s", var->name.GetBuffer(), var->GetListValue(0, "")); for (int i = 1; i < var->numelem; i++) fprintf(fp, "\",\"%s", var->GetListValue(i, "")); fprintf(fp, "\"\n"); } fclose(fp); return true; }
bool TimerManager::detectClockRollover(unsigned long long nowUs) { // If the time jumps backward, expire timers (rather than have them // expire in the distant future or not at all). // We check this way because now() will not roll from 0xffff... to zero // since the underlying hardware counter doesn't count microseconds. // Use a threshold value so we don't overreact to minor clock jitter. bool rollover = false; if (nowUs < m_previousTime && // check first in case the next line would underflow nowUs < m_previousTime - g_clockRolloverThreshold->val()) { MORDOR_LOG_ERROR(g_log) << this << " clock has rolled back from " << m_previousTime << " to " << nowUs << "; expiring all timers"; rollover = true; } m_previousTime = nowUs; return rollover; }
ConfigVar ConfigReader::toVar() const { // write everything to a ConfigVar, then output to disk ConfigVar out; out.writeInt(vars.size()); map<string, ConfigVar>::const_iterator it; for (it = vars.begin(); it != vars.end(); ++it) { out.writeInt(it->first.size()); out.write((unsigned char *)it->first.data(), it->first.size()); out.writeInt(it->second.size()); out.write((unsigned char *)it->second.buffer(), it->second.size()); } return out; }
//------------------------------------------------------------------------------ void Background2D::UpdateDebug() { // is this necessary? does it cost to apply camera 2d Scroll? // if so maybe move this check to applyScroll if( m_PositionReal != GetScreenScroll() ) { applyScroll(); } if( cv_debug_background2d.GetB() == true ) { DEBUG_DRAW.SetTextAlignment( DEBUG_DRAW.LEFT ); DEBUG_DRAW.SetScreenSpace( true ); DEBUG_DRAW.SetColour( Ogre::ColourValue( 0.0, 0.8, 0.8, 1 ) ); for( unsigned int i = 0; i < m_AnimationPlayed.size(); ++i ) { DEBUG_DRAW.Text( 150, 34 + i * 12, "Background 2D animation: " + m_AnimationPlayed[ i ].name ); } } }
static bool runTests(const TestSuites *suites, TestListener *listener) { Assertion::throwOnAssertion = true; if (g_wait->val()) { while (!isDebuggerAttached()) sleep(10000ull); debugBreak(); } bool result = true; if (!suites) suites = g_allTests; if (suites) { for (TestSuites::const_iterator it(suites->begin()); it != suites->end(); ++it) { for (TestSuite::second_type::const_iterator it2(it->second.second.begin()); it2 != it->second.second.end(); ++it2) { if (it->second.first) { result = result && runTest(listener, it->first, "<invariant>", it->second.first); } result = runTest(listener, it->first, it2->first, it2->second) && result; } if (it->second.first) { result = runTest(listener, it->first, "<invariant>", it->second.first) && result; } } } if (listener) listener->testsComplete(); return result; }
void Entity::UpdateDebug() { int debug = cv_debug_entity.GetI(); m_DirectionNode->setVisible( debug > 0 ); m_SolidCollisionNode->setVisible( debug > 0 && m_Solid ); m_TalkCollisionNode->setVisible( debug > 0 && m_Talkable ); // debug output if( debug > 0 ) { DEBUG_DRAW.SetColour( Ogre::ColourValue::White ); DEBUG_DRAW.SetScreenSpace( true ); DEBUG_DRAW.SetTextAlignment( DEBUG_DRAW.CENTER ); DEBUG_DRAW.SetFadeDistance( 40, 50 ); Ogre::Vector3 entity_pos = GetPosition(); DEBUG_DRAW.Text( entity_pos, 0, 0, m_Name ); DEBUG_DRAW.Text( entity_pos, 0, 12, m_AnimationCurrentName ); if( debug > 1 ) { static Ogre::String move_state_string[] = { "NONE", "WALKMESH", "LINEAR", "JUMP" }; DEBUG_DRAW.Text( entity_pos, 0, 24, Ogre::StringConverter::toString( entity_pos ) ); DEBUG_DRAW.Text( entity_pos, 0, 36, "Triangle: " + Ogre::StringConverter::toString( m_MoveTriangleId ) ); DEBUG_DRAW.Text( entity_pos, 0, 48, "Move state: " + move_state_string[ m_State ] ); switch( m_State ) { case Entity::WALKMESH: { DEBUG_DRAW.Line3d( entity_pos, m_MovePosition ); } break; case Entity::LINEAR: { DEBUG_DRAW.Text( m_LinearStart, 0, 0, "Start: " + Ogre::StringConverter::toString( m_LinearStart ) ); DEBUG_DRAW.Text( m_LinearEnd, 0, 0, "End: " + Ogre::StringConverter::toString( m_LinearEnd ) ); DEBUG_DRAW.Line3d( m_LinearStart, m_LinearEnd ); } break; case Entity::JUMP: { DEBUG_DRAW.Text( m_JumpStart, 0, 0, "Start: " + Ogre::StringConverter::toString( m_JumpStart ) ); DEBUG_DRAW.Text( m_JumpEnd, 0, 0, "End: " + Ogre::StringConverter::toString( m_JumpEnd ) ); Ogre::Vector3 distance = m_JumpEnd - m_JumpStart; Ogre::Vector3 pos1, pos2, pos3; pos1.x = m_JumpStart.x + distance.x * 0.25f; pos1.y = m_JumpStart.y + distance.y * 0.25f; float current1 = m_JumpSeconds / 4; pos1.z = current1 * current1 * -13.08f + current1 * ( ( distance.z ) / m_JumpSeconds + m_JumpSeconds * 13.08f ) + m_JumpStart.z; pos2.x = m_JumpStart.x + distance.x * 0.5f; pos2.y = m_JumpStart.y + distance.y * 0.5f; float current2 = current1 * 2; pos2.z = current2 * current2 * -13.08f + current2 * ( ( distance.z ) / m_JumpSeconds + m_JumpSeconds * 13.08f ) + m_JumpStart.z; pos3.x = m_JumpStart.x + distance.x * 0.75f; pos3.y = m_JumpStart.y + distance.y * 0.75f; float current3 = current1 * 3; pos3.z = current3 * current3 * -13.08f + current3 * ( ( distance.z ) / m_JumpSeconds + m_JumpSeconds * 13.08f ) + m_JumpStart.z; DEBUG_DRAW.Line3d( m_JumpStart, pos1 ); DEBUG_DRAW.Line3d( pos1, pos2 ); DEBUG_DRAW.Line3d( pos2, pos3 ); DEBUG_DRAW.Line3d( pos3, m_JumpEnd ); } break; } } } }
// take in a configuration file, find the CSPlugin class associated with the plugname variable, and return an instance CSPlugin *cs_plugin_load(const char *pluginConfigPath) { ConfigVar cv; if (cv.readVar(pluginConfigPath, "=") > 0) { if (!is_daemonised) { std::cerr << "Unable to load plugin config: " << pluginConfigPath << std::endl; } syslog(LOG_ERR, "Unable to load plugin config %s", pluginConfigPath); return NULL; } String plugname(cv["plugname"]); if (plugname.length() < 1) { if (!is_daemonised) { std::cerr << "Unable read plugin config plugname variable: " << pluginConfigPath << std::endl; } syslog(LOG_ERR, "Unable read plugin config plugname variable %s", pluginConfigPath); return NULL; } #ifdef ENABLE_CLAMD if (plugname == "clamdscan") { #ifdef DGDEBUG std::cout << "Enabling ClamDscan CS plugin" << std::endl; #endif return clamdcreate(cv); } #endif #ifdef ENABLE_AVASTD if (plugname == "avastdscan") { #ifdef DGDEBUG std::cout << "Enabling AvastDscan CS plugin" << std::endl; #endif return avastdcreate(cv); } #endif #ifdef ENABLE_KAVD if (plugname == "kavdscan") { #ifdef DGDEBUG std::cout << "Enabling KAVDscan CS plugin" << std::endl; #endif return kavdcreate(cv); } #endif #ifdef ENABLE_ICAP if (plugname == "icapscan") { #ifdef DGDEBUG std::cout << "Enabling ICAPscan CS plugin" << std::endl; #endif return icapcreate(cv); } #endif #ifdef ENABLE_COMMANDLINE if (plugname == "commandlinescan") { #ifdef DGDEBUG std::cout << "Enabling command-line CS plugin" << std::endl; #endif return commandlinecreate(cv); } #endif if (!is_daemonised) { std::cerr << "Unable to load plugin: " << pluginConfigPath << std::endl; } syslog(LOG_ERR, "Unable to load plugin %s\n", pluginConfigPath); return NULL; }
static int daemonMain(int argc, char *argv[]) { try { std::string macAddressString = g_macAddress->val(); replace(macAddressString, "-", ""); if (macAddressString.size() != 12u) { std::cerr << "MAC address must be 12 characters" << std::endl; return -1; } std::string macAddress = dataFromHexstring(macAddressString); std::set<Address::ptr> blacklistedAddresses; std::vector<std::string> blacklistedAddressesString = split( g_blacklist->val(), ";, "); for(std::vector<std::string>::const_iterator it( blacklistedAddressesString.begin()); it != blacklistedAddressesString.end(); ++it) { if(it->empty()) continue; blacklistedAddresses.insert(IPAddress::create(it->c_str())); } std::vector<std::pair<Address::ptr, unsigned int> > addresses = Address::getInterfaceAddresses(g_interface->val(), AF_INET); if (addresses.empty()) { std::cerr << "Couldn't find interface " << g_interface->val() << std::endl; return -1; } IPAddress::ptr localAddress = boost::static_pointer_cast<IPAddress>( addresses.front().first); IPAddress::ptr broadcastAddress = localAddress->broadcastAddress( addresses.front().second); broadcastAddress->port(9u); IPv4Address multicastAddress("239.255.255.250", 1900); IOManager ioManager; Socket::ptr broadcastSocket(broadcastAddress->createSocket(ioManager, SOCK_DGRAM)); broadcastSocket->setOption(SOL_SOCKET, SO_BROADCAST, 1); broadcastSocket->connect(broadcastAddress); Socket::ptr listenSocket(multicastAddress.createSocket(ioManager, SOCK_DGRAM)); listenSocket->setOption(SOL_SOCKET, SO_REUSEADDR, 1); listenSocket->bind(IPv4Address(0u, 1900u)); // TODO: listenSocket->joinGroup(multicastAddress, addresses.front().first); struct ip_mreq multicastGroup; memcpy(&multicastGroup.imr_multiaddr, &((sockaddr_in *)multicastAddress.name())->sin_addr, sizeof(struct in_addr)); memcpy(&multicastGroup.imr_interface, &((sockaddr_in *)addresses.front().first->name())->sin_addr, sizeof(struct in_addr)); listenSocket->setOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, multicastGroup); Daemon::onTerminate.connect(boost::bind(&Socket::cancelReceive, listenSocket)); try { IPv4Address sender; char buffer[4096]; size_t size; while((size = listenSocket->receiveFrom(buffer, 4096, sender))) { IPAddress::ptr senderDuplicate = sender.clone(); senderDuplicate->port(0u); if (blacklistedAddresses.find(senderDuplicate) != blacklistedAddresses.end()) { MORDOR_LOG_VERBOSE(Log::root()) << "Skipping broadcast from " << sender; continue; } HTTP::Request request; HTTP::RequestParser parser(request); parser.run(buffer, size); if (parser.complete() && !parser.error()) { if (request.requestLine.method == "M-SEARCH") { MORDOR_LOG_INFO(Log::root()) << "Relaying M-SEARCH to WOL from " << sender; wol(broadcastSocket, macAddress); } } else { MORDOR_LOG_WARNING(Log::root()) << "Unable to parse HTTP request from " << sender << ": " << charslice(buffer, size); } } } catch (OperationAbortedException &) { } catch (...) { MORDOR_LOG_FATAL(Log::root()) << boost::current_exception_diagnostic_information(); return -1; } } catch (...) { std::cerr << boost::current_exception_diagnostic_information() << std::endl; return -1; } return 0; }
namespace Mordor { static ConfigVar<size_t>::ptr g_defaultBufferSize = Config::lookup<size_t>("stream.buffered.defaultbuffersize", 65536, "Default buffer size for new BufferedStreams"); static Logger::ptr g_log = Log::lookup("mordor:streams:buffered"); BufferedStream::BufferedStream(Stream::ptr parent, bool own) : FilterStream(parent, own) { m_bufferSize = g_defaultBufferSize->val(); m_allowPartialReads = false; m_flushMultiplesOfBuffer = false; } void BufferedStream::close(CloseType type) { MORDOR_LOG_VERBOSE(g_log) << this << " close(" << type << ")"; if (type & READ) m_readBuffer.clear(); try { if ((type & WRITE) && m_writeBuffer.readAvailable()) flush(false); } catch (...) { if (ownsParent()) parent()->close(type); throw; } if (ownsParent()) parent()->close(type); } size_t BufferedStream::read(Buffer &buffer, size_t length) { return readInternal(buffer, length); } size_t BufferedStream::read(void *buffer, size_t length) { return readInternal(buffer, length); } // Buffer keeps track of its position automatically static void advance(Buffer &buffer, size_t amount) {} // void * does not static void advance(void *&buffer, size_t amount) { (unsigned char *&)buffer += amount; } template <class T> size_t BufferedStream::readInternal(T &buffer, size_t length) { if (supportsSeek()) flush(false); size_t remaining = length; size_t buffered = std::min(m_readBuffer.readAvailable(), remaining); m_readBuffer.copyOut(buffer, buffered); m_readBuffer.consume(buffered); remaining -= buffered; MORDOR_LOG_VERBOSE(g_log) << this << " read(" << length << "): " << buffered << " read from buffer"; if (remaining == 0) return length; if (buffered == 0 || !m_allowPartialReads) { size_t result; do { // Read enough to satisfy this request, plus up to a multiple of // the buffer size size_t todo = ((remaining - 1) / m_bufferSize + 1) * m_bufferSize; try { MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << todo << ")"; result = parent()->read(m_readBuffer, todo); MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << todo << "): " << result; } catch (...) { if (remaining == length) { MORDOR_LOG_VERBOSE(g_log) << this << " forwarding exception"; throw; } else { MORDOR_LOG_VERBOSE(g_log) << this << " swallowing exception"; // Swallow the exception return length - remaining; } } buffered = std::min(m_readBuffer.readAvailable(), remaining); m_readBuffer.copyOut(buffer, buffered); m_readBuffer.consume(buffered); advance(buffer, buffered); remaining -= buffered; } while (remaining > 0 && !m_allowPartialReads && result != 0); } return length - remaining; } size_t BufferedStream::write(const Buffer &buffer, size_t length) { m_writeBuffer.copyIn(buffer, length); size_t result = flushWrite(length); // Partial writes not allowed MORDOR_ASSERT(result == length); return result; } size_t BufferedStream::write(const void *buffer, size_t length) { m_writeBuffer.reserve(std::max(m_bufferSize, length)); m_writeBuffer.copyIn(buffer, length); size_t result = flushWrite(length); // Partial writes not allowed MORDOR_ASSERT(result == length); return result; } size_t BufferedStream::flushWrite(size_t length) { while (m_writeBuffer.readAvailable() >= m_bufferSize) { size_t result; try { if (m_readBuffer.readAvailable() && supportsSeek()) { parent()->seek(-(long long)m_readBuffer.readAvailable(), CURRENT); m_readBuffer.clear(); } size_t toWrite = m_writeBuffer.readAvailable(); if (m_flushMultiplesOfBuffer) toWrite = toWrite / m_bufferSize * m_bufferSize; MORDOR_LOG_TRACE(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << ")"; result = parent()->write(m_writeBuffer, toWrite); MORDOR_LOG_DEBUG(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << "): " << result; m_writeBuffer.consume(result); } catch (...) { // If this entire write is still in our buffer, // back it out and report the error if (m_writeBuffer.readAvailable() >= length) { MORDOR_LOG_VERBOSE(g_log) << this << " forwarding exception"; Buffer tempBuffer; tempBuffer.copyIn(m_writeBuffer, m_writeBuffer.readAvailable() - length); m_writeBuffer.clear(); m_writeBuffer.copyIn(tempBuffer); throw; } else { // Otherwise we have to say we succeeded, // because we're not allowed to have a partial // write, and we can't report an error because // the caller will think he needs to repeat // the entire write MORDOR_LOG_VERBOSE(g_log) << this << " swallowing exception"; return length; } } } return length; } long long BufferedStream::seek(long long offset, Anchor anchor) { MORDOR_ASSERT(parent()->supportsTell()); long long parentPos = parent()->tell(); long long bufferedPos = parentPos - m_readBuffer.readAvailable() + m_writeBuffer.readAvailable(); long long parentSize = parent()->supportsSize() ? -1ll : parent()->size(); // Check for no change in position if ((offset == 0 && anchor == CURRENT) || (offset == bufferedPos && anchor == BEGIN) || (parentSize != -1ll && offset + parentSize == bufferedPos && anchor == END)) return bufferedPos; MORDOR_ASSERT(supportsSeek()); flush(false); MORDOR_ASSERT(m_writeBuffer.readAvailable() == 0u); switch (anchor) { case BEGIN: MORDOR_ASSERT(offset >= 0); if (offset >= bufferedPos && offset <= parentPos) { m_readBuffer.consume((size_t)(offset - bufferedPos)); return offset; } m_readBuffer.clear(); break; case CURRENT: if (offset > 0 && offset <= (long long)m_readBuffer.readAvailable()) { m_readBuffer.consume((size_t)offset); return bufferedPos + offset; } offset -= m_readBuffer.readAvailable(); m_readBuffer.clear(); break; case END: if (parentSize == -1ll) throw std::invalid_argument("Can't seek from end without known size"); if (parentSize + offset >= bufferedPos && parentSize + offset <= parentPos) { m_readBuffer.consume((size_t)(parentSize + offset - bufferedPos)); return parentSize + offset; } m_readBuffer.clear(); break; default: MORDOR_NOTREACHED(); } return parent()->seek(offset, anchor); } long long BufferedStream::size() { long long size = parent()->size(); if (parent()->supportsTell()) { return std::max(size, tell()); } else { // not a seekable stream; we can only write to the end size += m_writeBuffer.readAvailable(); } return size; } void BufferedStream::truncate(long long size) { if (!parent()->supportsTell() || parent()->tell() + (long long)m_writeBuffer.readAvailable() >= size) flush(false); // TODO: truncate/clear m_readBuffer only if necessary m_readBuffer.clear(); parent()->truncate(size); } void BufferedStream::flush(bool flushParent) { while (m_writeBuffer.readAvailable()) { if (m_readBuffer.readAvailable() && supportsSeek()) { parent()->seek(-(long long)m_readBuffer.readAvailable(), CURRENT); m_readBuffer.clear(); } MORDOR_LOG_TRACE(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << ")"; size_t result = parent()->write(m_writeBuffer, m_writeBuffer.readAvailable()); MORDOR_LOG_DEBUG(g_log) << this << " parent()->write(" << m_writeBuffer.readAvailable() << "): " << result; MORDOR_ASSERT(result > 0); m_writeBuffer.consume(result); } if (flushParent) parent()->flush(); } ptrdiff_t BufferedStream::find(char delim, size_t sanitySize, bool throwIfNotFound) { if (supportsSeek()) flush(false); if (sanitySize == (size_t)~0) sanitySize = 2 * m_bufferSize; ++sanitySize; while (true) { size_t readAvailable = m_readBuffer.readAvailable(); if (readAvailable > 0) { ptrdiff_t result = m_readBuffer.find(delim, std::min(sanitySize, readAvailable)); if (result != -1) { return result; } } if (readAvailable >= sanitySize) { if (throwIfNotFound) MORDOR_THROW_EXCEPTION(BufferOverflowException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << m_bufferSize << ")"; size_t result = parent()->read(m_readBuffer, m_bufferSize); MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << m_bufferSize << "): " << result; if (result == 0) { // EOF if (throwIfNotFound) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } } } ptrdiff_t BufferedStream::find(const std::string &str, size_t sanitySize, bool throwIfNotFound) { if (supportsSeek()) flush(false); if (sanitySize == (size_t)~0) sanitySize = 2 * m_bufferSize; sanitySize += str.size(); while (true) { size_t readAvailable = m_readBuffer.readAvailable(); if (readAvailable > 0) { ptrdiff_t result = m_readBuffer.find(str, std::min(sanitySize, readAvailable)); if (result != -1) { return result; } } if (readAvailable >= sanitySize) { if (throwIfNotFound) MORDOR_THROW_EXCEPTION(BufferOverflowException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } MORDOR_LOG_TRACE(g_log) << this << " parent()->read(" << m_bufferSize << ")"; size_t result = parent()->read(m_readBuffer, m_bufferSize); MORDOR_LOG_DEBUG(g_log) << this << " parent()->read(" << m_bufferSize << "): " << result; if (result == 0) { // EOF if (throwIfNotFound) MORDOR_THROW_EXCEPTION(UnexpectedEofException()); return -(ptrdiff_t)m_readBuffer.readAvailable() - 1; } } } void BufferedStream::unread(const Buffer &b, size_t len) { MORDOR_ASSERT(supportsUnread()); Buffer tempBuffer; tempBuffer.copyIn(b, len); tempBuffer.copyIn(m_readBuffer); m_readBuffer.clear(); m_readBuffer.copyIn(tempBuffer); } }