git-svn-id: https://svn.microneil.com/svn/CodeDweller/trunk@1 d34b734f-a00e-4b39-a726-e4eeb87269abwx
| @@ -0,0 +1,34 @@ | |||
| ## Process this file with automake to produce Makefile.in | |||
| ## | |||
| ## $Id$ | |||
| ## | |||
| ## | |||
| ## Author: Alban Deniz | |||
| ## | |||
| ## Copyright (C) 2008 by MicroNeil Corporation. All rights reserved. | |||
| ## | |||
| CXXFLAGS = $(SNF_CXXFLAGS) | |||
| noinst_LIBRARIES = \ | |||
| libCodeDweller.a | |||
| libCodeDweller_a_SOURCES = \ | |||
| @top_srcdir@/CodeDweller/base64codec.cpp \ | |||
| @top_srcdir@/CodeDweller/configuration.cpp \ | |||
| @top_srcdir@/CodeDweller/networking.cpp \ | |||
| @top_srcdir@/CodeDweller/threading.cpp \ | |||
| @top_srcdir@/CodeDweller/timing.cpp | |||
| noinst_HEADERS = \ | |||
| @top_srcdir@/CodeDweller/base64codec.hpp \ | |||
| @top_srcdir@/CodeDweller/configuration.hpp \ | |||
| @top_srcdir@/CodeDweller/configuration.inline.hpp \ | |||
| @top_srcdir@/CodeDweller/histogram.hpp \ | |||
| @top_srcdir@/CodeDweller/networking.hpp \ | |||
| @top_srcdir@/CodeDweller/networking.inline.hpp \ | |||
| @top_srcdir@/CodeDweller/threading.hpp \ | |||
| @top_srcdir@/CodeDweller/timing.hpp | |||
| clean-local: | |||
| rm -f *.gcno *.gcov *.gcda *~ | |||
| @@ -0,0 +1,276 @@ | |||
| // base64codec.cpp | |||
| // Copyright (C) 2006 - 2009 MicroNeil Research Corporation | |||
| // See base64codec.hpp | |||
| //typedef vector<char> base64codec_buffer; | |||
| //typedef vector<char>::iterator base64codec_iterator; | |||
| #include "base64codec.hpp" | |||
| namespace base64codec { | |||
| char base64encode[65] = // Base64 encoding characters. | |||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |||
| // The following table makes conversion fast because it's all lookups. The | |||
| // special value XX64 is used everywhere a bad byte is found in the table. | |||
| const static unsigned char XXXX = 0xFF; // Bad base64 character. | |||
| const static unsigned char PAD0 = 0xFE; // Pad base64 character. | |||
| const static unsigned char IGNR = 0xFD; // Ingoreable base64 character. | |||
| const static unsigned char STOP = 0xFC; // STOP -- all done. | |||
| // Note the special case '=' is used for pad. It is given the value 0xFE. | |||
| // Also the IGNR case is any whitespace (Tab, CR, NL) that can be ignored. | |||
| // The input to this table is the incoming byte. The output is either XX64 | |||
| // or a valid base64 numerical value. | |||
| const static unsigned char base64decode[256] = { | |||
| // 0 1 2 3 4 5 6 7 8 9 A B C D E F | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,IGNR,IGNR,XXXX,XXXX,IGNR,XXXX,XXXX, // 0 | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX, // 1 | |||
| IGNR,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,0x3E,XXXX,XXXX,XXXX,0x3F, // 2 | |||
| 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,XXXX,XXXX,XXXX,PAD0,XXXX,XXXX, // 3 | |||
| XXXX,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 4 | |||
| 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,XXXX,XXXX,XXXX,XXXX,XXXX, // 5 | |||
| XXXX,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, // 6 | |||
| 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,XXXX,XXXX,XXXX,XXXX,XXXX, // 7 | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX, // 8 | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX, // 9 | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX, // A | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX, // B | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX, // C | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX, // D | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX, // E | |||
| XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX,XXXX // F | |||
| }; | |||
| } // End namespace base64codec | |||
| using namespace base64codec; | |||
| //// to_base64 ///////////////////////////////////////////////////////////////// | |||
| void to_base64::convert(const unsigned char* bfr, const int len) { // Converts from a char buffer. | |||
| if(NULL == bfr || 0 >= len) { // If there's NULL or no length | |||
| BadConversion = true; // that was a bad conversion. | |||
| return; // lets get out of here. | |||
| } | |||
| int NewSize = (len / 3) * 4; // Base64 takes 4 bytes for every 3; | |||
| if(0 < len % 3) NewSize += 4; // If there are more, add an other 4; | |||
| reserve(NewSize); // Set aside enough memory for the job. | |||
| int cursor = 0; // Starting at zero chunk it off. | |||
| while(len > cursor) { | |||
| // Chunk off 4 bytes into an unsigned int for conversion. | |||
| enum EndGames { // Describe the end game for this | |||
| OneByte, // chunk as containing either one, | |||
| TwoBytes, // two, | |||
| ThreeBytes // or three bytes. | |||
| } EndGame; // We use this to code the end. | |||
| // Byte 0 | |||
| unsigned long REGISTER = 0; // Start with a clear register. | |||
| REGISTER += bfr[cursor]; REGISTER <<= 8; ++cursor; // Load Byte 0. | |||
| EndGame = OneByte; // We've added a byte. | |||
| // Byte 1 | |||
| if(len > cursor) { // If we've got bytes left. | |||
| REGISTER += bfr[cursor]; // load the next one and | |||
| ++cursor; // move the cursor. | |||
| EndGame = TwoBytes; // We're up to 2 bytes. | |||
| } | |||
| REGISTER <<= 8; // Shift to the next byte. | |||
| // Byte 2 | |||
| if(len > cursor) { // If we've got bytes left. | |||
| REGISTER += bfr[cursor]; // load the next one and | |||
| ++cursor; // move the cursor. | |||
| EndGame = ThreeBytes; // That's a full house. | |||
| } | |||
| // No shift this time, the register is full ;-) | |||
| // Now that we have 3 bytes and a characterization we can encode the | |||
| // base64 bytes into our vector. | |||
| const int SixBitMask = 0x0000003f; // This is how far to shift. | |||
| char code3 = base64encode[(REGISTER & SixBitMask)]; REGISTER >>= 6; // Encode four characters for this | |||
| char code2 = base64encode[(REGISTER & SixBitMask)]; REGISTER >>= 6; // three bytes. | |||
| char code1 = base64encode[(REGISTER & SixBitMask)]; REGISTER >>= 6; | |||
| char code0 = base64encode[(REGISTER & SixBitMask)]; | |||
| push_back(code0); // Push the first 2 encoded bytes onto | |||
| push_back(code1); // the vector in the original order. | |||
| switch(EndGame) { // Now handle the end game. | |||
| case OneByte: { // If the end contains one valid byte | |||
| push_back('='); // push back two = to indicate that | |||
| push_back('='); // the last two bytes are padding. | |||
| break; | |||
| } | |||
| case TwoBytes: { // If the end contains two valid bytes | |||
| push_back(code2); // push back one more code byte and | |||
| push_back('='); // push back only one = indicating one | |||
| break; // byte of padding. | |||
| } | |||
| case ThreeBytes: // If we had the full three bytes to | |||
| default: { // work with then we have no padding. | |||
| push_back(code2); // Push back the remaining two | |||
| push_back(code3); // code bytes to capture the full | |||
| break; // encoding. This also works | |||
| } // in the middle of the input. | |||
| } // That's it for the end game. | |||
| } // That's it for this chunk. | |||
| BadConversion = false; // If we get here we've done good. | |||
| } | |||
| to_base64::to_base64(const vector<unsigned char>& bfr) : // Converts from a base64buffer. | |||
| BadConversion(true) { // No conversion yet ;-) | |||
| convert(&bfr[0], bfr.size()); // Recast the pointer and do it. | |||
| } | |||
| to_base64::to_base64(const vector<char>& bfr) : // Converts from a base64codec buffer. | |||
| BadConversion(true) { // No conversion yet ;-) | |||
| convert(reinterpret_cast<const unsigned char*>(&bfr[0]), bfr.size()); // Do this to get it done. | |||
| } | |||
| to_base64::to_base64(const unsigned char* bfr, const int len) : // Converts from a uchar buffer. | |||
| BadConversion(true) { // No conversion yet ;-) | |||
| convert(bfr, len); // Do this to get it done. | |||
| } | |||
| to_base64::to_base64(const char* bfr, const int len) : // Converts from a char buffer. | |||
| BadConversion(true) { // No conversion yet ;-) | |||
| convert(reinterpret_cast<const unsigned char*>(bfr), len); // Do this to get it done. | |||
| } | |||
| to_base64::to_base64(const string& s) : // Converts from a c++ string. | |||
| BadConversion(true) { // No conversion yet ;-) | |||
| convert(reinterpret_cast<const unsigned char*>(s.c_str()), s.length()); // Do this to get it done. | |||
| } | |||
| to_base64::to_base64(const char* s) : // Converts from a c string. | |||
| BadConversion(true) { // No conversion yet ;-) | |||
| convert(reinterpret_cast<const unsigned char*>(s), strlen(s)); // Do this to get it done. | |||
| } | |||
| bool to_base64::Bad() { // Look at the flag. | |||
| return BadConversion; | |||
| } | |||
| //// from_base64 /////////////////////////////////////////////////////////////// | |||
| unsigned char from_base64::NextSixBits( // Get the next base64 byte. | |||
| int& cursor, | |||
| const unsigned char* bfr, | |||
| const int len) { | |||
| while(len > cursor) { // Prepare to eat IGNR chars. | |||
| unsigned char c = base64decode[bfr[cursor]]; // Get the next 6 bits. | |||
| ++cursor; // Move the cursor for next time. | |||
| if(IGNR == c) continue; // If we should ignore it, eat. | |||
| if(XXXX == c) return c; // If it's bad, return it. | |||
| return c; // If it's ordinary return it. | |||
| } // If we run out of bytes | |||
| return STOP; // return STOP | |||
| } | |||
| //// Since the BadConversion flag is set on construction, if we bail out | |||
| //// of the convert() for any reason then the conversion will be bad. | |||
| void from_base64::convert(const unsigned char* bfr, const int len) { // Converts bfr from base64 to plaintext. | |||
| if(NULL == bfr || 0 >= len) { return; } // If there's nothing to do return bad. | |||
| // Estimate our conversion buffer size. | |||
| int NewSize = len / 4 * 3; // Four bytes of base64 could be 3 bytes. | |||
| reserve(NewSize); // Reserve that much space for speed. | |||
| // Start the conversion process. | |||
| int cursor = 0; | |||
| while(len > cursor) { // Go through the buffer and convert. | |||
| int REGISTER = 0; // We will use these to convert as we | |||
| unsigned char LOOKUP = 0; // go through the data. | |||
| // First two base64 bytes | |||
| const int MakeRoomFor6Bits = 6; | |||
| LOOKUP = NextSixBits(cursor, bfr, len); // Grab the next six bits. | |||
| if(STOP == LOOKUP) { break; } // If we ran out here it's ok. | |||
| if(XXXX == LOOKUP) { return; } // If the byte is bad bail out! | |||
| REGISTER += LOOKUP; REGISTER <<= MakeRoomFor6Bits; // Shift that one into place. | |||
| LOOKUP = NextSixBits(cursor, bfr, len); // Grab the next six bits. | |||
| if(XXXX == LOOKUP || STOP == LOOKUP) { return; } // If bad or empty here bail out! | |||
| REGISTER += LOOKUP; // Load in the six bits. | |||
| // Now we have 12 bits so we can grab our first byte. | |||
| const int GetMS8OutOf12Bits = 4; | |||
| const int BottomFourBits = 0x0000000F; | |||
| push_back(REGISTER >> GetMS8OutOf12Bits); // Push back the converted byte. | |||
| REGISTER = (REGISTER & BottomFourBits) << MakeRoomFor6Bits; // Make room for the next 6 bits. | |||
| // Grab the next 6 bits. | |||
| LOOKUP = NextSixBits(cursor, bfr, len); // Grab the next six bits. | |||
| if(XXXX == LOOKUP || STOP == LOOKUP) { return; } // If bad or empty here bail out! | |||
| if(PAD0 == LOOKUP) { break; } // If we've come to a pad we're done! | |||
| REGISTER += LOOKUP; // Load in the six bits. | |||
| // Now we have 10 bits so we can grab our Second byte. | |||
| const int GetMS8OutOf10Bits = 2; | |||
| const int BottomTwoBits = 0x00000003; | |||
| push_back(REGISTER >> GetMS8OutOf10Bits); // Push back the converted byte. | |||
| REGISTER = (REGISTER & BottomTwoBits) << MakeRoomFor6Bits; // Make room for the next 6 bits. | |||
| LOOKUP = NextSixBits(cursor, bfr, len); // Grab the final six bits. | |||
| if(XXXX == LOOKUP || STOP == LOOKUP) { return; } // If bad or empty here bail out! | |||
| if(PAD0 == LOOKUP) { break; } // If we've come to a pad we're done! | |||
| REGISTER += LOOKUP; // Load in the six bits. | |||
| // Now we should have our final 8 bits :-) | |||
| push_back(REGISTER); // push back the converted byte. | |||
| } | |||
| BadConversion = false; // If we get here we did ok. | |||
| } | |||
| from_base64::from_base64(const vector<unsigned char>& bfr) : // Converts from a base64buffer. | |||
| BadConversion(true) { // It's bad until we've done it. | |||
| convert(&bfr[0], bfr.size()); // Recast the pointer and do it. | |||
| } | |||
| from_base64::from_base64(const vector<char>& bfr) : // Converts from a buffer. | |||
| BadConversion(true) { // It's bad until we've done it. | |||
| convert(reinterpret_cast<const unsigned char*>(&bfr[0]), bfr.size()); // This is how we do it. | |||
| } | |||
| from_base64::from_base64(const string& s) : // Converts from a c++ string. | |||
| BadConversion(true) { // It's bad until we've done it. | |||
| convert(reinterpret_cast<const unsigned char*>(s.c_str()), s.length()); // This is how we do it. | |||
| } | |||
| from_base64::from_base64(const char* s) : // Converts from a c_string. | |||
| BadConversion(true) { // It's bad until we've done it. | |||
| convert(reinterpret_cast<const unsigned char*>(s), strlen(s)); // This is how we do it. | |||
| } | |||
| bool from_base64::Bad() { // Look at the flag. | |||
| return BadConversion; | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| // base64codec.hpp | |||
| // Copyright (C) 2006 - 2009 MicroNeil Research Corporation | |||
| // BASE64 encoder decoder objects extending vectors | |||
| #ifndef base64codec_included | |||
| #define base64codec_included | |||
| #include <vector> | |||
| #include <cstring> | |||
| #include <string> | |||
| using namespace std; | |||
| typedef vector<unsigned char> base64buffer; | |||
| class to_base64 : public base64buffer { // Converts binary data to base 64. | |||
| private: | |||
| bool BadConversion; // True if something went wrong. | |||
| void convert(const unsigned char* bfr, const int len); // Does the actual work. | |||
| public: | |||
| to_base64(const vector<unsigned char>& bfr); // Converts from a base64buffer. | |||
| to_base64(const vector<char>& bfr); // Converts from a buffer. | |||
| to_base64(const string& s); // Converts from a c++ string. | |||
| to_base64(const char* s); // Converts from a c string. | |||
| to_base64(const unsigned char* bfr, const int len); // Converts from a uchar buffer. | |||
| to_base64(const char* bfr, const int len); // Converts from a char buffer. | |||
| bool Bad(); | |||
| }; | |||
| class from_base64 : public base64buffer { // Convert base64 data to binary. | |||
| private: | |||
| bool BadConversion; // True if the conversion didn't go well. | |||
| unsigned char NextSixBits( // Helper for decoding & ingoring spaces. | |||
| int& cursor, | |||
| const unsigned char* bfr, | |||
| const int len); | |||
| void convert(const unsigned char* bfr, const int len); // Does the actual work. | |||
| public: | |||
| from_base64(const vector<unsigned char>& bfr); // Converts from a base64buffer. | |||
| from_base64(const vector<char>& bfr); // Converts from a buffer. | |||
| from_base64(const string& s); // Converts from a c++ string. | |||
| from_base64(const char*); // Converts from a c_string. | |||
| bool Bad(); // True if conversion wasn't complete. | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,734 @@ | |||
| // configuration.hpp | |||
| // | |||
| // (C) 2006 - 2009 MicroNeil Research Corporation. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 | |||
| // | |||
| // What about this ============================================================= | |||
| // The configuration module provides a platform for reading configuration files | |||
| // (or string data) containing well-formed xml and mapping that data to program | |||
| // variables. | |||
| // The idea is to provide the ability for an object or application to provide | |||
| // a modular "configuration" object that models a hierarchical collection of | |||
| // "settings" that can be represented easily in code and in xml. | |||
| // | |||
| // The following is an example model of a configuration in code and that same | |||
| // configuration fully populated in xml. | |||
| // | |||
| // The code might look like this... | |||
| // | |||
| // int IntValue, DefaultInt = 3; | |||
| // double DblValue, DefaultDbl = 3.14159; | |||
| // bool BooleanValue, DefaultBool = false; | |||
| // string StringValue, DefaultString = "NoStringHere"; | |||
| // | |||
| // SpecialConfigurator : public ConfigurationHandler { // Create a special handler to build a list | |||
| // ... | |||
| // public: | |||
| // | |||
| // ConfigurationHandler& Startup(ConfigurationElement& E) { // This function returns a handy handler to | |||
| // return MyStartupConfigurationHandler; // (re) initialize this handler ;-) | |||
| // } | |||
| // | |||
| // void Operator()() { // Each time the configurator is called | |||
| // ... | |||
| // } | |||
| // | |||
| // int Attribute1; // these items are interpreted and added | |||
| // double Attribute2; // to the list. A ConfigurationHandler COULD | |||
| // string Attribute3; // do something entirely different though ;-) | |||
| // string Contents; | |||
| // ... | |||
| // } Special; | |||
| // | |||
| // ConfigurationElement SampleConfig("SampleConfiguration"); // Define a sample config (doc element) | |||
| // SampleConfig // Populate the SampleConfig | |||
| // .atStartCall(Special.Startup()) | |||
| // .Element("Integer", IntValue, DefaultInt).End() // Link an element to an int w/ default. | |||
| // .Element("Double", DblValue, DefaultDbl).End("Double") // Link an element to a dbl w/ default. | |||
| // .Element("String", StringValue, DefaultString).End("String") // Link an element to a string w/ default. | |||
| // .Element("ComplexElements") // Create a sub element. | |||
| // .Element("Complex1") // Sub element Complex1 has attributes. | |||
| // .Attribute("IntAtt", IntValue, DefaultInt) // Complex1 has an integer attribute. | |||
| // .Attribute("DblAtt", DblValue, DefaultDbl) // Complex1 has a dbl attribute. | |||
| // .Element("IntAtt", IntValue).End() // IntAtt can also be set by a sub element. | |||
| // .Element("DblAtt", DblValue).End() // DblAtt can also be set by a sub element. | |||
| // .End() // That's it for Complex1. | |||
| // .Element("Complex2") // Create the Complex2 sub element. | |||
| // .Attribute("C2I", IntValue, DefaultInt) // C2I attribute. | |||
| // .Attribute("C2D", DblValue) // C2D attribute - no default. | |||
| // .Attribute("C2S", StringValue, DefultString) // C2S attribute - string w/ default. | |||
| // .End("Complex2") // End of element throws if doesn't match. | |||
| // .Element("Complex3", Special.Contents) // Element 3 using a special configurator. | |||
| // .Attribute("A1", Special.Attribute1) // Set A1 and A2 and A3 and when the | |||
| // .Attribute("A2", Special.Attribute2) // element has been completed, Special() | |||
| // .Attribute("A3", Special.Attribute3) // will be called to record the entries. | |||
| // .atEndCall(Special) // Here's where we register the handler. | |||
| // .End() // Closing Complex3 to be ice. | |||
| // .End() // Closing ComplexElements to be nice. | |||
| // .End(); // Closing SampleConfiguration to be nice. | |||
| // | |||
| // The XML might look like this... | |||
| // | |||
| // <SampleConfiguration> | |||
| // <Integer>10</Integer> | |||
| // <Double>2.4</Double> | |||
| // <String>This is a sample string</String> | |||
| // <ComplexElements> | |||
| // <Complex1 IntAtt="4" DblAtt="2.1324"> | |||
| // <IntAtt>24</IntAtt> <!-- changed IntAtt --> | |||
| // </Complex1> | |||
| // <Complex2 C2I='3' C2D='5.14' C2S='Some "string" we like' /> | |||
| // <Complex3> stuff in here </Complex3> | |||
| // <Complex3> Another instance </Complex3> | |||
| // <Complex3> Each one gets passed to Special() on activation </Complex3> | |||
| // <Complex3> This way, Special() can build a list or some other </Complex3> | |||
| // <Complex3> interesting thing with all of these. </Complex3> | |||
| // <ComplexElements> | |||
| // </SampleConfiguration> | |||
| // | |||
| // Include This Header Once Only =============================================== | |||
| #ifndef configuration_included | |||
| #define configuration_included | |||
| #include <string> | |||
| #include <sstream> | |||
| #include <fstream> | |||
| #include <cstring> | |||
| #include <cstdlib> | |||
| #include <list> | |||
| using namespace std; | |||
| class ConfigurationElement; // Elements exist | |||
| class ConfigurationAttribute; // Attributes exist | |||
| class ConfigurationData; // Data exists | |||
| class ConfigurationTranslator; // Translators exist | |||
| class ConfigurationMnemonic; // Mnemonics exist | |||
| class Configurator; // Configurators exist | |||
| //// Configuration Element ///////////////////////////////////////////////////// | |||
| // | |||
| // Elements make up the core of a configuration. That is, a configuration is a | |||
| // tree of elements. Elements translate directly to well formed xml elements in | |||
| // a configuration file or string. | |||
| class ConfigurationElement { | |||
| private: | |||
| string myName; // Elements have a name. | |||
| // External important things I remember but don't touch... | |||
| ConfigurationElement* myParent; // They may have a parrent. | |||
| list<Configurator*> myStartConfigurators; // Call these when we start Interpret() | |||
| list<Configurator*> myEndConfigurators; // Call these when we finish Interpret() | |||
| // Internal / subordinate things I own and kill... | |||
| list<ConfigurationAttribute*> myAttributes; // They may have a list of attributes. | |||
| list<ConfigurationElement*> myElements; // They may have a list of sub-elements. | |||
| list<ConfigurationMnemonic*> myMnemonics; // They may have a list of mnemonics. | |||
| list<ConfigurationTranslator*> myTranslators; // They may have a list of translators. | |||
| // During Interpret() operations we keep track of where we are seen... | |||
| int myLine; // Last line number I was seen on. | |||
| int myIndex; // Last char position I was seen on. | |||
| int myLength; // Last segment length. | |||
| bool myCleanFlag; // Keep track of initialization. | |||
| bool myInitOnInterpretFlag; // Initialize() at each Interpret()? | |||
| void runStartConfigurators(ConfigurationData& D); // Does what it says ;-) | |||
| void runEndConfigurators(ConfigurationData& D); // Does what it says ;-) | |||
| public: | |||
| ConfigurationElement(const char* Name); // Must be constructed with a name | |||
| ConfigurationElement(const string Name); // either c string or c++ string. | |||
| ConfigurationElement(const char* Name, ConfigurationElement& Parent); // Sub-elements are constructed with a | |||
| ConfigurationElement(const string Name, ConfigurationElement& Parent); // parrent. | |||
| // Upon desctruction an element will delete all subordinate objects: | |||
| // * All sub element objects. | |||
| // * All attribute objects. | |||
| // * All mnemonic objects. | |||
| // * All translator objects. | |||
| // It is important to use new when passing one of these objects to an | |||
| // element or attribute to prevent problems with the delete operation. | |||
| // NORMALLY these things would be created using factory methods on the | |||
| // element and attribute objects themselves - so be careful. | |||
| // It will not delete Configurators - they must | |||
| // be deleted elsewhere because they may have been | |||
| // re-used and this element wouldn't know about it ;-) | |||
| ~ConfigurationElement(); // The descrutor clears and deletes all! | |||
| // Elements can be probed for some simple, useful things. | |||
| string Name(); // Get the name of this element. | |||
| ConfigurationElement& Parent(); // Get the parent of this element. | |||
| ConfigurationElement& Parent(ConfigurationElement& newParent); // Set the parent of this element. | |||
| // Note - if there is no parent (an element is the root) then it will | |||
| // return a reference to itself when Parent() is called. | |||
| int Line(); // Get the last line number. | |||
| int Index(); // Get the last data position. | |||
| int Length(); // Get the last length. | |||
| // Elements can contain either data or sub-elements. | |||
| ConfigurationElement& Element(const char* Name); // Add a new sub element by c string name. | |||
| ConfigurationElement& Element(const string Name); // Add a new sub element by c++ string name. | |||
| //// Mapping element factory methods for convenience. | |||
| //// Root-Node elements are _usually_ empty and without attributes in xml | |||
| //// so we don't make any of that type of convenience constructor here. | |||
| // char* versions | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| string& x, string init = string("")); // Map to a string. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| bool& x, bool init = false); // Map to a boolean. | |||
| // string versions | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| string& x, string init = string("")); // Map to a string. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| bool& x, bool init = false); // Map to a boolean. | |||
| // End methods for heading back up the tree at the end of an element. | |||
| class EndNameDoesNotMatch {}; // Throw when End(name) doesn't match. | |||
| ConfigurationElement& End(); // Return this element's parent. | |||
| ConfigurationElement& End(const char* Name); // Check the name and return the parent | |||
| ConfigurationElement& End(const string Name); // if the name is correct - or throw! | |||
| // Elements can have attributes. | |||
| ConfigurationAttribute& Attribute(const char* Name); // Add an attribute using a cstring. | |||
| ConfigurationAttribute& Attribute(const string Name); // Add an attribute using a c++ string. | |||
| //// Mapping Attribute factory methods for convenience. | |||
| // char* versions | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| string& x, string init = string("")); // Map to a string. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| bool& x, bool init = false); // Map to a boolean. | |||
| // string versions | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| string& x, string init = string("")); // Map to a string. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| bool& x, bool init = false); // Map to a boolean. | |||
| // Elements can Initialize() at each Interpret() call. | |||
| ConfigurationElement& setInitOnInterpret(); // Set the init on interpret flag. | |||
| // Elements can call external functions to aid in special operations | |||
| // such as building lists. | |||
| ConfigurationElement& atStartCall(Configurator& Functor); // Add an atStart call-back to this element. | |||
| ConfigurationElement& atEndCall(Configurator& Functor); // Add an atEnd call-back to this element. | |||
| // Extracting data from the element's contents is done with | |||
| // translators. A good set of primatives are built in, but the user | |||
| // can also make their own. If an Element is mapped to more than | |||
| // one then they are all called once the element's contents are | |||
| // collected. A translator takes the data provided by the element, | |||
| // converts it into the expected type, and sets one or more variables | |||
| // to the converted value. Usually - just one variable. | |||
| ConfigurationElement& mapTo(ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationElement& mapTo(string& x, string init = string("")); // Map to a string. | |||
| ConfigurationElement& mapTo(int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationElement& mapTo(double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationElement& mapTo(bool& x, bool init = false); // Map to a boolean. | |||
| // An Element's contents may use some special mnemonics to make a | |||
| // configuration easier to understand and less error prone. When the | |||
| // contents match a mnemnoic then the translation of the mnemonic is | |||
| // passed to the Translators instead of the raw contents. | |||
| ConfigurationElement& Mnemonic(const char* name, const char* value); // Add a mnemonic using c strings. | |||
| ConfigurationElement& Mnemonic(const char* name, const string value); // Add a mnemonic using c & c++ strings. | |||
| ConfigurationElement& Mnemonic(const string name, const char* value); // Add a mnemonic using c++ & c strings. | |||
| ConfigurationElement& Mnemonic(const string name, const string value); // Add a mnemonic using c++ strings. | |||
| // The way data gets into an element tree is that it is Interpret()ed | |||
| // recursively. The data is loaded into a ConfigurationData object which | |||
| // is passed to the top Element. That element interpretes the data, moves | |||
| // the interpretation pointers, and passes the data on to it's subordinate | |||
| // elements in turn. They do the same recursively. When the last sub - | |||
| // element has had it's way with the data, the interpretation process is | |||
| // complete. The ConfigurationData object will contain the original data | |||
| // and a log of anything that happened during the interpretation process. | |||
| // | |||
| // Each time an element is asked to Interpret() data, it calls any atStart | |||
| // configurators, translates any attributes, then either translates it's | |||
| // contents or passes the data to it's children, then calls any atEnd | |||
| // configurators. | |||
| // | |||
| // To ensure that the correct default values are used the Initialize() is | |||
| // always called on all internal attributes and elements before any data is | |||
| // interpreted. To prevent this from being inefficient, a boolean flag is | |||
| // kept in each element to keep track of whether it is clean and if it is | |||
| // then the call to Initialize will simply return (skipping subordinate | |||
| // elements along the way). | |||
| // | |||
| // Interpret returns true if this object found itself at the current | |||
| // Data.Index and false if not. This helps keep the recursive parsing | |||
| // code simpler ;-) | |||
| void initialize(); // Reset all translators to defaults. | |||
| void notifyDirty(); // Set dirty (if translators change). | |||
| bool interpret(ConfigurationData& Data); // (re) Interpret this data. | |||
| }; | |||
| //// Configuration Attribute /////////////////////////////////////////////////// | |||
| // | |||
| // Attributes translate directly to well formed xml attributes (within the | |||
| // start tag of an element). | |||
| class ConfigurationAttribute { | |||
| private: | |||
| string myName; // Elements have a name. | |||
| ConfigurationElement& myParent; // They may have a parrent. | |||
| list<ConfigurationMnemonic*> myMnemonics; // They may have a list of mnemonics. | |||
| list<ConfigurationTranslator*> myTranslators; // They may have a list of translators. | |||
| int myLine; // Last line number I was seen on. | |||
| int myIndex; // Last char position I was seen on. | |||
| int myLength; // Last segment length. | |||
| public: | |||
| ConfigurationAttribute(const char* Name, ConfigurationElement& Parent); // Sub-elements are constructed with a | |||
| ConfigurationAttribute(const string Name, ConfigurationElement& Parent); // parrent. | |||
| // Attributes delete their Mnemonics and Translators when they go. | |||
| // See Elements for similar warnings about objects provided to | |||
| // this object... you must use new to be safe, or better yet - stick to | |||
| // the built in factory methods ;-) | |||
| ~ConfigurationAttribute(); // Crush, Kill, Destroy! | |||
| // Attributes can be probed for some simple, useful things. | |||
| string Name(); // Get the name of this attribute. | |||
| ConfigurationElement& Parent(); // Get the parent of this attribute. | |||
| int Line(); // Get the last line number. | |||
| int Index(); // Get the last data position. | |||
| int Length(); // Get the last length. | |||
| void notifyDirty(); // Attributes use this when they change. | |||
| // For convenience in building configurations, an Attribute offers | |||
| // some call-through methods to it's parrent Element. This allows for | |||
| // clear, concise .method() coding that mimics an outline of the | |||
| // configuration structure. | |||
| //// For switching back to the parent element and adding new sub-elements. | |||
| ConfigurationElement& Element(const char* Name); // Add a new sub element by c string name. | |||
| ConfigurationElement& Element(const string Name); // Add a new sub element by c++ string name. | |||
| //// Mapping element factory methods for convenience. | |||
| //// Root-Node elements are _usually_ empty and without attributes in xml | |||
| //// so we don't make any of that type of convenience constructor here. | |||
| // char* versions | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| string& x, string init = string("")); // Map to a string. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| bool& x, bool init = false); // Map to a boolean. | |||
| // string versions | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| string& x, string init = string("")); // Map to a string. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationElement& Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| bool& x, bool init = false); // Map to a boolean. | |||
| // End methods for heading back up the tree at the end of an element. | |||
| ConfigurationElement& End(); // Return this element's parent. | |||
| ConfigurationElement& End(const char* Name); // Check the name and return the parent | |||
| ConfigurationElement& End(const string Name); // if the name is correct - or throw! | |||
| //// For adding new attributes to the parent element. | |||
| ConfigurationAttribute& Attribute(const char* Name); // Add an attribute using a cstring. | |||
| ConfigurationAttribute& Attribute(const string Name); // Add an attribute using a c++ string. | |||
| //// Mapping Attribute factory methods for convenience. | |||
| // char* versions | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| string& x, string init = string("")); // Map to a string. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| bool& x, bool init = false); // Map to a boolean. | |||
| // string versions | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator); // Add a Translator to this element. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| string& x, string init = string("")); // Map to a string. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| int& x, int init = 0, int radix = 0); // Map to an int. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationAttribute& Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| bool& x, bool init = false); // Map to a boolean. | |||
| //// Set Init On Interprete for the parent element. | |||
| ConfigurationElement& setInitOnInterpret(); // Set the init on interpret flag. | |||
| //// For adding configurators to the parent element. | |||
| ConfigurationElement& atStartCall(Configurator& Functor); // Add an atStart call-back to this element. | |||
| ConfigurationElement& atEndCall(Configurator& Functor); // Add an atEnd call-back to this element. | |||
| // Of course, the most useful thing about attributes is that they can | |||
| // be mapped to variables using translators. The same as those that | |||
| // apply to the parent element's contents. Here they are for use on this | |||
| // attribute. | |||
| ConfigurationAttribute& mapTo(ConfigurationTranslator& newTranslator); // Add a Translator to this attribute. | |||
| ConfigurationAttribute& mapTo(string& x, string init = string("")); // Map to a string. | |||
| ConfigurationAttribute& mapTo(int& x, int init, int radix = 0); // Map to an int. | |||
| ConfigurationAttribute& mapTo(double& x, double init = 0.0); // Map to a double. | |||
| ConfigurationAttribute& mapTo(bool& x, bool init = false); // Map to a boolean. | |||
| // Attributes can have mnemonics just like elements. | |||
| ConfigurationAttribute& Mnemonic(const char* name, const char* value); // Add a mnemonic using a c string. | |||
| ConfigurationAttribute& Mnemonic(const char* name, const string value); // Add a mnemonic using c & c++ strings. | |||
| ConfigurationAttribute& Mnemonic(const string name, const char* value); // Add a mnemonic using c++ & c strings. | |||
| ConfigurationAttribute& Mnemonic(const string name, const string value); // Add a mnemonic using a c++ string. | |||
| // Attributes participate in the Interprete() task just like elements. | |||
| void initialize(); // Reset all translators to defaults. | |||
| bool interpret(ConfigurationData& Data); // (re) Interpret this data. | |||
| }; | |||
| //// Configuration Data //////////////////////////////////////////////////////// | |||
| // | |||
| // A ConfigurationData object holds on to the configuration source data and | |||
| // provideds a place to log any information about how the configuration was | |||
| // interpreted. It also creates and destroys a handy char[] to contain the | |||
| // data. To make this beastie easier to handle, we use the Named Constructor | |||
| // Idiom and hide the true constructor in the private section. | |||
| class ConfigurationData { // Configuration Data Source | |||
| private: | |||
| char* myDataBuffer; // The actual data buffer. | |||
| int myBufferSize; // Size of the current buffer. | |||
| int myIndex; // The current interpretation index. | |||
| int myLine; // Current line number. | |||
| public: | |||
| ConfigurationData(const char* FileName); // Constructor from c string file name. | |||
| ConfigurationData(const string FileName); // Constructor from c++ string file name. | |||
| ConfigurationData(const char* Data, int Length); // Raw constructor from text buffer. | |||
| ~ConfigurationData(); // Destroys the internal buffer etc. | |||
| char Data(int Index); // Returns char from Data[Index] | |||
| int Index(); // Reads the current Index. | |||
| int Index(int i); // Changes the current Index. | |||
| int Line(); // Reads the current Line number. | |||
| int addNewLines(int Count); // Increments the Line number. | |||
| stringstream Log; // Convenient Interpret log. | |||
| }; | |||
| //// Configuration Translator ////////////////////////////////////////////////// | |||
| // | |||
| // A Translator converts the contents provided to it in string form into some | |||
| // other data type. The object here is a prototype for that, followed by a | |||
| // collection of the basic translators used for built-in mapTo()s. | |||
| class ConfigurationTranslator { // Translators exist | |||
| public: | |||
| virtual void translate(const char* Value) = 0; // Pure virtual translator. | |||
| virtual void initialize() = 0; // Pure virtual initializer. | |||
| }; | |||
| class StringTranslator : public ConfigurationTranslator { | |||
| private: | |||
| string& myVariable; // Variable to map. | |||
| string myInitializer; // Initial/Default value. | |||
| public: | |||
| StringTranslator( // Construct this with | |||
| string& Variable, // the variable to map, | |||
| string Inititializer); // and the default value. | |||
| void translate(const char* Value); // Provide a translation method. | |||
| void initialize(); // Provide an initialization method. | |||
| }; | |||
| class IntegerTranslator : public ConfigurationTranslator { | |||
| private: | |||
| int& myVariable; // Variable to map. | |||
| int myInitializer; // Initial/Default value. | |||
| int myRadix; // Radix for strtol() | |||
| public: | |||
| IntegerTranslator( // Construct this with | |||
| int& Variable, // the variable to map, | |||
| int Inititializer, // and the default value. | |||
| int Radix); // For this one we also need a Radix. | |||
| void translate(const char* Value); // Provide a translation method. | |||
| void initialize(); // Provide an initialization method. | |||
| }; | |||
| class DoubleTranslator : public ConfigurationTranslator { | |||
| private: | |||
| double& myVariable; // Variable to map. | |||
| double myInitializer; // Initial/Default value. | |||
| public: | |||
| DoubleTranslator( // Construct this with | |||
| double& Variable, // the variable to map, | |||
| double Inititializer); // and the default value. | |||
| void translate(const char* Value); // Provide a translation method. | |||
| void initialize(); // Provide an initialization method. | |||
| }; | |||
| class BoolTranslator : public ConfigurationTranslator { | |||
| private: | |||
| bool& myVariable; // Variable to map. | |||
| bool myInitializer; // Initial/Default value. | |||
| public: | |||
| BoolTranslator( // Construct this with | |||
| bool& Variable, // the variable to map, | |||
| bool Inititializer); // and the default value. | |||
| void translate(const char* Value); // Provide a translation method. | |||
| void initialize(); // Provide an initialization method. | |||
| }; | |||
| //// Configuration Mnemonic //////////////////////////////////////////////////// | |||
| // | |||
| // A Mnemonic allows the actual contents of an element or attribute to be | |||
| // exchanged for a different "named" value to help eliminate "magic numbers" | |||
| // and "secret codes" from configurations. One way this might be used is to | |||
| // map an enumeration to the appropriate integer values, or things like YES and | |||
| // NO to boolean true and false (respectively) when turning on/off program | |||
| // options. | |||
| class ConfigurationMnemonic { // Mnemonics | |||
| private: | |||
| string myName; // What is the Mnemonic? | |||
| string myValue; // What is the translation? | |||
| public: | |||
| ConfigurationMnemonic(string Name, string Value); // To make one, provide both parts. | |||
| bool test(string Name); // Test to see if this Mnemonic matches. | |||
| string Value(); // If it does then we will need it's value. | |||
| }; | |||
| //// Configurator ////////////////////////////////////////////////////////////// | |||
| // | |||
| // A configurator is a "functor" or "closure" or "callback" that can be used to | |||
| // support sophisticated interpretation options. The most basic and necessary | |||
| // of these is support for list building. Consider an object created to contain | |||
| // a list of records where each record might be represented as a collection of | |||
| // attributes and elements. The object would have data elements mapped to the | |||
| // attributes and elements in the configuration and then control elements which | |||
| // are functors for initializing the list and storing new entries as they are | |||
| // completed. The object here is a pure virtual prototype. | |||
| class Configurator { // Configurators exist | |||
| public: | |||
| virtual void operator()(ConfigurationElement& E, ConfigurationData& D) = 0; // Pure virtual configurator. | |||
| }; | |||
| //// Include our inline methods //////////////////////////////////////////////// | |||
| #include "configuration.inline.hpp" | |||
| //// Utilities ///////////////////////////////////////////////////////////////// | |||
| // SetTrueOnComplete Configurator ////////////////////////////////////////////// | |||
| class ConfiguratorSetTrueOnComplete : public Configurator { // Configurator set's a boolean true. | |||
| private: | |||
| bool* myBoolean; // The boolean to set. | |||
| public: | |||
| ConfiguratorSetTrueOnComplete(); // Must init to NULL for safety. | |||
| void setup(bool& Target); // Link to the target boolean. | |||
| void operator()(ConfigurationElement& E, ConfigurationData& D); // Handle the operation. | |||
| }; | |||
| #endif | |||
| // End Of Include Only Once | |||
| @@ -0,0 +1,576 @@ | |||
| // configuration.inline.hpp | |||
| // | |||
| // (C) 2006-2009 MicroNeil Research Corporation. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 | |||
| // See configuration.hpp for details | |||
| //// Configuration Element ///////////////////////////////////////////////////// | |||
| inline ConfigurationElement::ConfigurationElement(const char* Name) : // Construct with a cstring. | |||
| myName(string(Name)), | |||
| myParent(NULL), | |||
| myLine(0), | |||
| myIndex(0), | |||
| myLength(0), | |||
| myInitOnInterpretFlag(false), | |||
| myCleanFlag(true) { | |||
| } | |||
| inline ConfigurationElement::ConfigurationElement(const string Name) : // Construct with a c++ string. | |||
| myName(Name), | |||
| myParent(NULL), | |||
| myLine(0), | |||
| myIndex(0), | |||
| myLength(0), | |||
| myInitOnInterpretFlag(false), | |||
| myCleanFlag(true) { | |||
| } | |||
| inline ConfigurationElement::ConfigurationElement( // Construct sub element w/ cstring. | |||
| const char* Name, | |||
| ConfigurationElement& Parent) : | |||
| myName(string(Name)), | |||
| myParent(&Parent), | |||
| myLine(0), | |||
| myIndex(0), | |||
| myLength(0), | |||
| myInitOnInterpretFlag(false), | |||
| myCleanFlag(true) { | |||
| } | |||
| inline ConfigurationElement::ConfigurationElement( // Construct sub element w/ string. | |||
| const string Name, | |||
| ConfigurationElement& Parent) : | |||
| myName(Name), | |||
| myParent(&Parent), | |||
| myLine(0), | |||
| myIndex(0), | |||
| myLength(0), | |||
| myInitOnInterpretFlag(false), | |||
| myCleanFlag(true) { | |||
| } | |||
| inline string ConfigurationElement::Name() { return myName; } // Get the name of this element. | |||
| inline ConfigurationElement& ConfigurationElement::Parent() { // Get the parrent of this element. | |||
| if(NULL != myParent) { // If I have a parent | |||
| return (*myParent); // then I dereference and return it. | |||
| } // If I don't have a parent | |||
| return (*this); // then I return myself. | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Parent( // Set the parrent of this element. | |||
| ConfigurationElement& Parent) { // Given this parent | |||
| myParent = &Parent; // I take and store it's address | |||
| return (*myParent); // then dereference and return it. | |||
| } | |||
| inline int ConfigurationElement::Line() { return myLine; } // Get the last line number. | |||
| inline int ConfigurationElement::Index() { return myIndex; } // Get the last data position. | |||
| inline int ConfigurationElement::Length() { return myLength; } // Get the last length. | |||
| inline void ConfigurationElement::notifyDirty() { myCleanFlag = false; } // Attributes do this when they change. | |||
| inline ConfigurationElement& ConfigurationElement::Element(const char* Name) { // Add a new sub element by c string name. | |||
| return Element(string(Name)); // Use the string name version | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Element(const string Name) { // Add a new sub element by c++ string name. | |||
| ConfigurationElement* N = new ConfigurationElement( // Create a new Element with the | |||
| Name, // name provided and | |||
| (*this)); // myself as the parent. | |||
| myElements.push_back(N); // Add it to the list. | |||
| return (*N); // Return the new element. | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator) { // Add a Translator to this element. | |||
| return Element(string(Name), newTranslator); // Use the string name version | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| string& x, string init) { // Map to a string. | |||
| return Element(string(Name), x, init); // Use the string name version | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| int& x, int init, int radix) { // Map to an int. | |||
| return Element(string(Name), x, init, radix); // Use the string name version | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| double& x, double init) { // Map to a double. | |||
| return Element(string(Name), x, init); // Use the string name version | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| bool& x, bool init) { // Map to a boolean. | |||
| return Element(string(Name), x, init); // Use the string name version | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::End() { // Return this element's parent. | |||
| return Parent(); // Borrow Parent() | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::End(const char* Name) { // Check the name and return the parent | |||
| return End(string(Name)); // Borrow End(string) | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::End(const string Name) { // if the name is correct - or throw! | |||
| if(0 != Name.compare(myName)) { // If Name is not myName | |||
| throw EndNameDoesNotMatch(); // throw an exception! | |||
| } // If the names match then | |||
| return Parent(); // return the parent. | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationElement::Attribute( // Add an attribute using a cstring. | |||
| const char* Name) { // Given this cstring name | |||
| return Attribute(string(Name)); // Convert it to a string and borrow | |||
| } // Attribute(string) | |||
| inline ConfigurationAttribute& ConfigurationElement::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator) { // Add a Translator to this element. | |||
| return Attribute(string(Name), newTranslator); // Borrow the string name version | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationElement::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| string& x, string init) { // Map to a string. | |||
| return Attribute(string(Name), x, init); // Borrow the string name version | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationElement::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| int& x, int init, int radix) { // Map to an int. | |||
| return Attribute(string(Name), x, init); // Borrow the string name version | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationElement::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| double& x, double init) { // Map to a double. | |||
| return Attribute(string(Name), x, init); // Borrow the string name version | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationElement::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| bool& x, bool init) { // Map to a boolean. | |||
| return Attribute(string(Name), x, init); // Borrow the string name version | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::setInitOnInterpret() { // Set the init on interpret flag. | |||
| myInitOnInterpretFlag = true; // Set the flag. | |||
| return(*this); // Dereference and return self. | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::atStartCall( // Add an atStart call-back. | |||
| Configurator& Functor) { // Given this Functor, | |||
| myStartConfigurators.push_back(&Functor); // add it to my atStart list then | |||
| return(*this); // dereference and return myself. | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::atEndCall( // Add an atEnd call-back. | |||
| Configurator& Functor) { // Given this Functor, | |||
| myEndConfigurators.push_back(&Functor); // add it to my atEnd list then | |||
| return(*this); // dereference and return myself. | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Mnemonic( // Add a mnemonic using c strings. | |||
| const char* name, const char* value) { // Given char* and char* | |||
| return Mnemonic(string(name), string(value)); // make strings and borrow that method. | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Mnemonic( // Add a mnemonic using mixed strings. | |||
| const char* name, const string value) { // Given char* and string | |||
| return Mnemonic(string(name), value); // make strings and borrow that method. | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Mnemonic( // Add a mnemonic using mixed strings. | |||
| const string name, const char* value) { // Given string and char* | |||
| return Mnemonic(name, string(value)); // make strings and borrow that method. | |||
| } | |||
| inline ConfigurationElement& ConfigurationElement::Mnemonic( // Add a mnemonic using c++ strings. | |||
| const string name, const string value) { // Givent string and string | |||
| ConfigurationMnemonic* N = // Create a new Mnemonic | |||
| new ConfigurationMnemonic(name, value); // using the values provided, | |||
| myMnemonics.push_back(N); // add it to my list, then | |||
| return(*this); // dereference and return myself. | |||
| } | |||
| //// Configuration Attribute /////////////////////////////////////////////////// | |||
| inline ConfigurationAttribute::ConfigurationAttribute( // Attributes are constructed with a | |||
| const char* Name, ConfigurationElement& Parent) : // Name and a Parent. | |||
| myName(string(Name)), // We convert the name to a string. | |||
| myParent(Parent), // We just grab the parent. | |||
| myLine(0), // Everything else gets zeroed. | |||
| myLength(0), | |||
| myIndex(0) { | |||
| } | |||
| inline ConfigurationAttribute::ConfigurationAttribute( // Attributes are constrictued with a | |||
| const string Name, ConfigurationElement& Parent) : // Name and a Parent. | |||
| myName(Name), // We grab them and zero the rest. | |||
| myParent(Parent), | |||
| myLine(0), | |||
| myLength(0), | |||
| myIndex(0) { | |||
| } | |||
| inline string ConfigurationAttribute::Name() { // Get the name of this attribute. | |||
| return myName; | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Parent() { // Get the parent of this attribute. | |||
| return myParent; | |||
| } | |||
| inline int ConfigurationAttribute::Line() { // Get the last line number. | |||
| return myLine; | |||
| } | |||
| inline int ConfigurationAttribute::Index() { // Get the last data position. | |||
| return myIndex; | |||
| } | |||
| inline int ConfigurationAttribute::Length() { // Get the last length. | |||
| return myLength; | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Add a new sub element by c string name. | |||
| const char* Name) { | |||
| return myParent.Element(Name); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Add a new sub element by c++ string name. | |||
| const string Name) { | |||
| return myParent.Element(Name); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator) { // Add a Translator to this element. | |||
| return myParent.Element(Name, newTranslator); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| string& x, string init) { // Map to a string. | |||
| return myParent.Element(Name, x, init); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| int& x, int init, int radix) { // Map to an int. | |||
| return myParent.Element(Name, x, init, radix); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| double& x, double init) { // Map to a double. | |||
| return myParent.Element(Name, x, init); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| bool& x, bool init) { // Map to a boolean. | |||
| return myParent.Element(Name, x, init); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator) { // Add a Translator to this element. | |||
| return myParent.Element(Name, newTranslator); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| string& x, string init) { // Map to a string. | |||
| return myParent.Element(Name, x, init); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| int& x, int init, int radix) { // Map to an int. | |||
| return myParent.Element(Name, x, init, radix); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| double& x, double init) { // Map to a double. | |||
| return myParent.Element(Name, x, init); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::Element( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| bool& x, bool init) { // Map to a boolean. | |||
| return myParent.Element(Name, x, init); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::End() { // Return this element's parent. | |||
| return myParent.End(); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::End(const char* Name) { // Check the name and return the parent | |||
| return myParent.End(Name); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::End(const string Name) { // if the name is correct - or throw! | |||
| return myParent.End(Name); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Add an attribute using a cstring. | |||
| const char* Name) { | |||
| return myParent.Attribute(Name); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Add an attribute using a c++ string. | |||
| const string Name) { | |||
| return myParent.Attribute(Name); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator) { // Add a Translator to this element. | |||
| return myParent.Attribute(Name, newTranslator); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| string& x, string init) { // Map to a string. | |||
| return myParent.Attribute(Name, x, init); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| int& x, int init, int radix) { // Map to an int. | |||
| return myParent.Attribute(Name, x, init, radix); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| double& x, double init) { // Map to a double. | |||
| return myParent.Attribute(Name, x, init); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const char* Name, // requires a name, of course, | |||
| bool& x, bool init) { // Map to a boolean. | |||
| return myParent.Attribute(Name, x, init); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| ConfigurationTranslator& newTranslator) { // Add a Translator to this element. | |||
| return myParent.Attribute(Name, newTranslator); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| string& x, string init) { // Map to a string. | |||
| return myParent.Attribute(Name, x, init); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| int& x, int init, int radix) { // Map to an int. | |||
| return myParent.Attribute(Name, x, init, radix); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| double& x, double init) { // Map to a double. | |||
| return myParent.Attribute(Name, x, init); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Attribute( // Mapping factory for convenience, | |||
| const string Name, // requires a name, of course, | |||
| bool& x, bool init) { // Map to a boolean. | |||
| return myParent.Attribute(Name, x, init); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::setInitOnInterpret() { // Set the init on interpret flag. | |||
| return myParent.setInitOnInterpret(); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::atStartCall( // Add an atStart call-back to this element. | |||
| Configurator& Functor) { | |||
| return myParent.atStartCall(Functor); | |||
| } | |||
| inline ConfigurationElement& ConfigurationAttribute::atEndCall( // Add an atEnd call-back to this element. | |||
| Configurator& Functor) { | |||
| return myParent.atEndCall(Functor); | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Mnemonic( // Add a mnemonic using c strings. | |||
| const char* name, const char* value) { // Given char* and char* | |||
| return Mnemonic(string(name), string(value)); // make strings and borrow that method. | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Mnemonic( // Add a mnemonic using mixed strings. | |||
| const char* name, const string value) { // Given char* and string | |||
| return Mnemonic(string(name), value); // make strings and borrow that method. | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Mnemonic( // Add a mnemonic using mixed strings. | |||
| const string name, const char* value) { // Given string and char* | |||
| return Mnemonic(name, string(value)); // make strings and borrow that method. | |||
| } | |||
| inline ConfigurationAttribute& ConfigurationAttribute::Mnemonic( // Add a mnemonic using c++ strings. | |||
| const string name, const string value) { // Givent string and string | |||
| ConfigurationMnemonic* N = // Create a new Mnemonic | |||
| new ConfigurationMnemonic(name, value); // using the values provided, | |||
| myMnemonics.push_back(N); // add it to my list, then | |||
| return(*this); // dereference and return myself. | |||
| } | |||
| //// Configuration Data //////////////////////////////////////////////////////// | |||
| inline char ConfigurationData::Data(int Index) { // Returns char from Data[Index] | |||
| if(0 > Index || Index >= myBufferSize) { // Check that index is in range | |||
| return 0; // and return 0 if it is not. | |||
| } // If Index is within range then | |||
| return myDataBuffer[Index]; // return the byte requested. | |||
| } | |||
| inline int ConfigurationData::Index() { // Reads the current Index. | |||
| return myIndex; | |||
| } | |||
| inline int ConfigurationData::Index(int i) { // Changes the current Index. | |||
| if(0 > i || i >= myBufferSize) { // If i is out of range then | |||
| return myIndex; // return the current Index unchanged. | |||
| } // If i is within range then | |||
| myIndex = i; // change the Index to i and | |||
| return myIndex; // return the changed Index. | |||
| } | |||
| inline int ConfigurationData::Line() { // Reads the current Line number. | |||
| return myLine; | |||
| } | |||
| inline int ConfigurationData::addNewLines(int Count) { // Increments the Line number. | |||
| myLine += Count; // Add the number of new lines. | |||
| return myLine; // Return the current Line number. | |||
| } | |||
| //// Configuration Translator ////////////////////////////////////////////////// | |||
| inline StringTranslator::StringTranslator( // Construct this with | |||
| string& Variable, // the variable to map, | |||
| string Initializer) : // and the default value. | |||
| myVariable(Variable), | |||
| myInitializer(Initializer) { | |||
| } | |||
| inline void StringTranslator::translate(const char* Value) { // Provide a translation method. | |||
| myVariable = string(Value); // String to String = simple copy. | |||
| } | |||
| inline void StringTranslator::initialize() { // Provide an initialization method. | |||
| myVariable = myInitializer; // Revert to the initializer value. | |||
| } | |||
| inline IntegerTranslator::IntegerTranslator( // Construct this with | |||
| int& Variable, // the variable to map, | |||
| int Initializer, // and the default value. | |||
| int Radix) : // For this one we also need a Radix. | |||
| myVariable(Variable), | |||
| myInitializer(Initializer), | |||
| myRadix(Radix) { | |||
| } | |||
| inline void IntegerTranslator::translate(const char* Value) { // Provide a translation method. | |||
| char* dummy; // Throw away ptr for strtol(). | |||
| myVariable = strtol(Value, &dummy, myRadix); // Convert the string w/ strtol(). | |||
| } | |||
| inline void IntegerTranslator::initialize() { // Provide an initialization method. | |||
| myVariable = myInitializer; // Revert to the initializer value. | |||
| } | |||
| inline DoubleTranslator::DoubleTranslator( // Construct this with | |||
| double& Variable, // the variable to map, | |||
| double Initializer) : // and the default value. | |||
| myVariable(Variable), | |||
| myInitializer(Initializer) { | |||
| } | |||
| inline void DoubleTranslator::translate(const char* Value) { // Provide a translation method. | |||
| char* dummy; // Throw away ptr for strtod(). | |||
| myVariable = strtod(Value, &dummy); // Convert the string w/ strtod(). | |||
| } | |||
| inline void DoubleTranslator::initialize() { // Provide an initialization method. | |||
| myVariable = myInitializer; // Revert to the initializer value. | |||
| } | |||
| inline BoolTranslator::BoolTranslator( // Construct this with | |||
| bool& Variable, // the variable to map, | |||
| bool Initializer) : // and the default value. | |||
| myVariable(Variable), | |||
| myInitializer(Initializer) { | |||
| } | |||
| inline void BoolTranslator::translate(const char* Value) { // Provide a translation method. | |||
| if( | |||
| (0 == strcmp(Value,"on")) || | |||
| (0 == strcmp(Value,"true")) || // on, true, yes, and 1 are | |||
| (0 == strcmp(Value, "yes")) || // interpreted as a boolean true. | |||
| (0 == strcmp(Value, "1")) | |||
| ) { | |||
| myVariable = true; | |||
| } else { // Anything else is interpreted as | |||
| myVariable = false; // boolean false. | |||
| } | |||
| } | |||
| inline void BoolTranslator::initialize() { // Provide an initialization method. | |||
| myVariable = myInitializer; // Revert to the initializer value. | |||
| } | |||
| //// Configuration Mnemonic //////////////////////////////////////////////////// | |||
| inline ConfigurationMnemonic::ConfigurationMnemonic( // To make one, provide both parts. | |||
| string Name, string Value) : | |||
| myName(Name), | |||
| myValue(Value) { | |||
| } | |||
| inline bool ConfigurationMnemonic::test(string Name) { // Test to see if this Mnemonic matches. | |||
| return (0 == Name.compare(myName)); // Return true if Name and myName match. | |||
| } | |||
| inline string ConfigurationMnemonic::Value() { // If it does then we will need it's value. | |||
| return myValue; | |||
| } | |||
| @@ -0,0 +1,60 @@ | |||
| // histogram.hpp | |||
| // Copyright (C) 2006 - 2009 MicroNeil Research Corporation | |||
| // Class to capture a histogram of events using a <set> | |||
| #ifndef mn_histogram_included | |||
| #define mn_histogram_included | |||
| #include <set> | |||
| using namespace std; | |||
| /** The Histogram class is managed set of HistogramRecords. | |||
| *** We play some naughty tricks with pointers to break the rules and | |||
| *** directly manipulate the counts of HistogramRecords stored in the | |||
| *** set - thus saving space, complexity, and cycles. The set allows us | |||
| *** to add new records as needed and locate existing records quickly. | |||
| *** At any point in time, the set contains all of the event (hit) counts | |||
| *** ordered by key. | |||
| **/ | |||
| class HistogramRecord { // A record to assocate a key and count. | |||
| public: | |||
| int Key; // Here is the key. | |||
| int Count; // Here is the count. | |||
| HistogramRecord(const int NewKey) : // We must have a key to make one. | |||
| Key(NewKey), Count(0) {} // and a new one starts at count 0. | |||
| bool operator<(const HistogramRecord& Right) const { // To live in a set we need to < | |||
| return (Key < Right.Key); // properly based on the key. | |||
| } | |||
| }; | |||
| class Histogram : public set<HistogramRecord> { // A Histogram is a set of HistogramRecords | |||
| private: // and a private hit counter... | |||
| int HitCount; | |||
| public: | |||
| Histogram() : HitCount(0) {} | |||
| // with a few extra public functions. The | |||
| int hit(const int EventKey, const int Adjustment = 1) { // hit() method increments a specific count. | |||
| HistogramRecord E(EventKey); // First, make a record for the event key. | |||
| insert(E); // Insert the new record (if it's not there). | |||
| set<HistogramRecord>::iterator iE = // Find either the pre-existing or the new | |||
| find(E); // record for this key. | |||
| int* C; // Play naughty pointer games to access | |||
| C = const_cast<int*>(&((*iE).Count)); // the Count for this record inside the | |||
| (*C) += Adjustment; // set and add our Adjustment to it. | |||
| HitCount += Adjustment; // Accumulate the adjustments overall. | |||
| return(*C); // Return the count for this key. | |||
| } | |||
| int Hits() { return HitCount; } // Return the sum of hits so far. | |||
| void reset() { // Reset the histogram to zero. | |||
| HitCount = 0; // That means no counts, and | |||
| clear(); // an empty set of records. | |||
| } | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,630 @@ | |||
| // networking.cpp | |||
| // Copyright (C) 2006-2009 MicroNeil Research Corporation. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 | |||
| //============================================================================== | |||
| // See networking.hpp for notes. | |||
| // See networking.inline.hpp for inlined methods & functions. | |||
| #include "networking.hpp" | |||
| Networking Network; // Finally creating the Network instance. | |||
| //// Platform Specific Stuff /////////////////////////////////////////////////// | |||
| #if defined(WIN32) || defined(WIN64) | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //// Being Windows specific code | |||
| WSADATA WSSTartData; // Socket library data structure. | |||
| // Error description handling for humans. | |||
| string Networking::DescriptiveError(string Msg, int Errno) { // Form a descriptive error w/ errno. | |||
| char* s = 0; | |||
| switch(Errno) { | |||
| case WSA_INVALID_HANDLE: s = "WSA_INVALID_HANDLE"; break; | |||
| case WSA_NOT_ENOUGH_MEMORY: s = "WSA_NOT_ENOUGH_MEMORY"; break; | |||
| case WSA_INVALID_PARAMETER: s = "WSA_INVALID_PARAMETER"; break; | |||
| case WSA_OPERATION_ABORTED: s = "WSA_OPERATION_ABORTED"; break; | |||
| case WSA_IO_INCOMPLETE: s = "WSA_IO_INCOMPLETE"; break; | |||
| case WSA_IO_PENDING: s = "WSA_IO_PENDING"; break; | |||
| case WSAEINTR: s = "WSAEINTR"; break; | |||
| case WSAEBADF: s = "WSAEBADF"; break; | |||
| case WSAEACCES: s = "WSAEACCES"; break; | |||
| case WSAEFAULT: s = "WSAEFAULT"; break; | |||
| case WSAEINVAL: s = "WSAEINVAL"; break; | |||
| case WSAEMFILE: s = "WSAEMFILE"; break; | |||
| case WSAEWOULDBLOCK: s = "WSAEWOULDBLOCK"; break; | |||
| case WSAEINPROGRESS: s = "WSAEINPROGRESS"; break; | |||
| case WSAEALREADY: s = "WSAEALREADY"; break; | |||
| case WSAENOTSOCK: s = "WSAENOTSOCK"; break; | |||
| case WSAEDESTADDRREQ: s = "WSAEDESTADDRREQ"; break; | |||
| case WSAEMSGSIZE: s = "WSAEMSGSIZE"; break; | |||
| case WSAEPROTOTYPE: s = "WSAEPROTOTYPE"; break; | |||
| case WSAENOPROTOOPT: s = "WSAENOPROTOOPT"; break; | |||
| case WSAEPROTONOSUPPORT: s = "WSAEPROTONOSUPPORT"; break; | |||
| case WSAESOCKTNOSUPPORT: s = "WSAESOCKTNOSUPPORT"; break; | |||
| case WSAEOPNOTSUPP: s = "WSAEOPNOTSUPP"; break; | |||
| case WSAEPFNOSUPPORT: s = "WSAEPFNOSUPPORT"; break; | |||
| case WSAEAFNOSUPPORT: s = "WSAEAFNOSUPPORT"; break; | |||
| case WSAEADDRINUSE: s = "WSAEADDRINUSE"; break; | |||
| case WSAEADDRNOTAVAIL: s = "WSAEADDRNOTAVAIL"; break; | |||
| case WSAENETDOWN: s = "WSAENETDOWN"; break; | |||
| case WSAENETUNREACH: s = "WSAENETUNREACH"; break; | |||
| case WSAENETRESET: s = "WSAENETRESET"; break; | |||
| case WSAECONNABORTED: s = "WSAECONNABORTED"; break; | |||
| case WSAECONNRESET: s = "WSAECONNRESET"; break; | |||
| case WSAENOBUFS: s = "WSAENOBUFS"; break; | |||
| case WSAEISCONN: s = "WSAEISCONN"; break; | |||
| case WSAENOTCONN: s = "WSAENOTCONN"; break; | |||
| case WSAESHUTDOWN: s = "WSAESHUTDOWN"; break; | |||
| case WSAETOOMANYREFS: s = "WSAETOOMANYREFS"; break; | |||
| case WSAETIMEDOUT: s = "WSAETIMEDOUT"; break; | |||
| case WSAECONNREFUSED: s = "WSAECONNREFUSED"; break; | |||
| case WSAELOOP: s = "WSAELOOP"; break; | |||
| case WSAENAMETOOLONG: s = "WSAENAMETOOLONG"; break; | |||
| case WSAEHOSTDOWN: s = "WSAEHOSTDOWN"; break; | |||
| case WSAEHOSTUNREACH: s = "WSAEHOSTUNREACH"; break; | |||
| case WSAENOTEMPTY: s = "WSAENOTEMPTY"; break; | |||
| case WSAEPROCLIM: s = "WSAEPROCLIM"; break; | |||
| case WSAEUSERS: s = "WSAEUSERS"; break; | |||
| case WSAEDQUOT: s = "WSAEDQUOT"; break; | |||
| case WSAESTALE: s = "WSAESTALE"; break; | |||
| case WSAEREMOTE: s = "WSAEREMOTE"; break; | |||
| case WSASYSNOTREADY: s = "WSASYSNOTREADY"; break; | |||
| case WSAVERNOTSUPPORTED: s = "WSAVERNOTSUPPORTED"; break; | |||
| case WSANOTINITIALISED: s = "WSANOTINITIALISED"; break; | |||
| case WSAEDISCON: s = "WSAEDISCON"; break; | |||
| case WSAENOMORE: s = "WSAENOMORE"; break; | |||
| case WSAECANCELLED: s = "WSAECANCELLED"; break; | |||
| case WSAEINVALIDPROCTABLE: s = "WSAEINVALIDPROCTABLE"; break; | |||
| case WSAEINVALIDPROVIDER: s = "WSAEINVALIDPROVIDER"; break; | |||
| case WSAEPROVIDERFAILEDINIT: s = "WSAEPROVIDERFAILEDINIT"; break; | |||
| case WSASYSCALLFAILURE: s = "WSASYSCALLFAILURE"; break; | |||
| case WSASERVICE_NOT_FOUND: s = "WSASERVICE_NOT_FOUND"; break; | |||
| case WSATYPE_NOT_FOUND: s = "WSATYPE_NOT_FOUND"; break; | |||
| case WSA_E_NO_MORE: s = "WSA_E_NO_MORE"; break; | |||
| case WSA_E_CANCELLED: s = "WSA_E_CANCELLED"; break; | |||
| case WSAEREFUSED: s = "WSAEREFUSED"; break; | |||
| case WSAHOST_NOT_FOUND: s = "WSAHOST_NOT_FOUND"; break; | |||
| case WSATRY_AGAIN: s = "WSATRY_AGAIN"; break; | |||
| case WSANO_RECOVERY: s = "WSANO_RECOVERY"; break; | |||
| case WSANO_DATA: s = "WSANO_DATA"; break; | |||
| case WSA_QOS_RECEIVERS: s = "WSA_QOS_RECEIVERS"; break; | |||
| case WSA_QOS_SENDERS: s = "WSA_QOS_SENDERS"; break; | |||
| case WSA_QOS_NO_SENDERS: s = "WSA_QOS_NO_SENDERS"; break; | |||
| case WSA_QOS_NO_RECEIVERS: s = "WSA_QOS_NO_RECEIVERS"; break; | |||
| case WSA_QOS_REQUEST_CONFIRMED: s = "WSA_QOS_REQUEST_CONFIRMED"; break; | |||
| case WSA_QOS_ADMISSION_FAILURE: s = "WSA_QOS_ADMISSION_FAILURE"; break; | |||
| case WSA_QOS_POLICY_FAILURE: s = "WSA_QOS_POLICY_FAILURE"; break; | |||
| case WSA_QOS_BAD_STYLE: s = "WSA_QOS_BAD_STYLE"; break; | |||
| case WSA_QOS_BAD_OBJECT: s = "WSA_QOS_BAD_OBJECT"; break; | |||
| case WSA_QOS_TRAFFIC_CTRL_ERROR: s = "WSA_QOS_TRAFFIC_CTRL_ERROR"; break; | |||
| case WSA_QOS_GENERIC_ERROR: s = "WSA_QOS_GENERIC_ERROR"; break; | |||
| case WSA_QOS_ESERVICETYPE: s = "WSA_QOS_ESERVICETYPE"; break; | |||
| case WSA_QOS_EFLOWSPEC: s = "WSA_QOS_EFLOWSPEC"; break; | |||
| case WSA_QOS_EPROVSPECBUF: s = "WSA_QOS_EPROVSPECBUF"; break; | |||
| case WSA_QOS_EFILTERSTYLE: s = "WSA_QOS_EFILTERSTYLE"; break; | |||
| case WSA_QOS_EFILTERTYPE: s = "WSA_QOS_EFILTERTYPE"; break; | |||
| case WSA_QOS_EFILTERCOUNT: s = "WSA_QOS_EFILTERCOUNT"; break; | |||
| case WSA_QOS_EOBJLENGTH: s = "WSA_QOS_EOBJLENGTH"; break; | |||
| case WSA_QOS_EFLOWCOUNT: s = "WSA_QOS_EFLOWCOUNT"; break; | |||
| case WSA_QOS_EPOLICYOBJ: s = "WSA_QOS_EPOLICYOBJ"; break; | |||
| case WSA_QOS_EFLOWDESC: s = "WSA_QOS_EFLOWDESC"; break; | |||
| case WSA_QOS_EPSFLOWSPEC: s = "WSA_QOS_EPSFLOWSPEC"; break; | |||
| case WSA_QOS_EPSFILTERSPEC: s = "WSA_QOS_EPSFILTERSPEC"; break; | |||
| case WSA_QOS_ESDMODEOBJ: s = "WSA_QOS_ESDMODEOBJ"; break; | |||
| case WSA_QOS_ESHAPERATEOBJ: s = "WSA_QOS_ESHAPERATEOBJ"; break; | |||
| case WSA_QOS_RESERVED_PETYPE: s = "WSA_QOS_RESERVED_PETYPE"; break; | |||
| #ifdef WSA_QOS_EUNKOWNPSOBJ | |||
| case WSA_QOS_EUNKOWNPSOBJ: s = "WSA_QOS_EUNKOWNPSOBJ"; break; | |||
| #endif | |||
| } | |||
| Msg.append(" "); | |||
| if(s) { | |||
| Msg.append(s); | |||
| } | |||
| else { | |||
| ostringstream ErrNoMsg; | |||
| ErrNoMsg << " UNKNOWN ErrorNumber = " << Errno; | |||
| Msg.append(ErrNoMsg.str()); | |||
| } | |||
| return Msg; | |||
| }; | |||
| // Networking Constructor ////////////////////////////////////////////////////// | |||
| // Handles any necessary setup of Network Module resources. | |||
| Networking::Networking() { // Upon initialization, | |||
| if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData)) { // startup the Winsock2.0 DLL. | |||
| throw InitializationError( // If that fails then throw! | |||
| "Networking::Networking() if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData))" | |||
| ); | |||
| } | |||
| } | |||
| // Networking Destructor /////////////////////////////////////////////////////// | |||
| // Handles any necessary cleanup of Network Module resources. | |||
| Networking::~Networking() { // Upon shutdown, | |||
| WSACleanup(); // shutdown the Winsock DLL. | |||
| } | |||
| //// Emd Windows specific code | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| #else | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //// Begin GNU specific code | |||
| // Error description handling for humans. | |||
| string Networking::DescriptiveError(string Msg, int Errno) { // Form a descriptive error w/ errno. | |||
| Msg.append(" "); Msg.append(strerror(Errno)); | |||
| return Msg; | |||
| }; | |||
| // Networking Constructor ////////////////////////////////////////////////////// | |||
| // Handles any necessary setup of Network Module resources. | |||
| Networking::Networking() { // Upon initialization, | |||
| // Nothing So Far... // nothing special required. | |||
| } | |||
| // Networking Destructor /////////////////////////////////////////////////////// | |||
| // Handles any necessary cleanup of Network Module resources. | |||
| Networking::~Networking() { // GNU sockets cleanup, | |||
| // Nothing So Far... // nothing specail to required. | |||
| } | |||
| //// End GNU specific code | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| #endif | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //// Platform Agnostic Stuff | |||
| //// Useful Internal Bits & Pieces ///////////////////////////////////////////// | |||
| const int LowestOctetMask = 0x000000FF; // The bits to look at. | |||
| const int OneOctetInBits = 8; // The bits to shift. | |||
| void splitIP( // Split an IP into octets. | |||
| unsigned long A, // The address in host format. | |||
| int& a0, // Reference to the first octet. | |||
| int& a1, // Reference to the second octet. | |||
| int& a2, // Reference to the third octet. | |||
| int& a3 // Reference to the forth octet. | |||
| ){ | |||
| a3 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the lowest order octet & move. | |||
| a2 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the next lowest octet & move. | |||
| a1 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the next lowest octet & move. | |||
| a0 = A & LowestOctetMask; // Get the highest octet. That's IT! | |||
| } | |||
| //// IP4Address methods //////////////////////////////////////////////////////// | |||
| IP4Address::operator unsigned long int() const { // Assign to unsigned long int. | |||
| return IP; // Return it. | |||
| } | |||
| IP4Address::operator string() const { // Assign to a string. | |||
| char stringbfr[IPStringBufferSize]; // Grab a temporary buffer. | |||
| memset(stringbfr, 0, sizeof(stringbfr)); // Null out it's space. | |||
| int a0, a1, a2, a3; // Grab some integers. | |||
| splitIP(IP, a0, a1, a2, a3); // Split the IP in the IP4Address. | |||
| sprintf(stringbfr, "%d.%d.%d.%d", a0, a1, a2, a3); // Format the octets. | |||
| return string(stringbfr); // Return a string. | |||
| } | |||
| //// SocketAddress methods ///////////////////////////////////////////////////// | |||
| // getAddress(str, len) | |||
| const char* SocketAddress::getAddress(char* str) { // Get the IP address into a cstring. | |||
| if(NULL == str) { // If the caller did not provide a | |||
| str = IPStringBuffer; // buffer to use then we will use ours. | |||
| } | |||
| int a0, a1, a2, a3, i=0; // Grab a bunch of handy integers. | |||
| getAddress(a0, a1, a2, a3); // Get the address as octets. | |||
| sprintf(str, "%d.%d.%d.%d", a0, a1, a2, a3); // Format as dotted decimal notation. | |||
| return str; // Return the output buffer. | |||
| } | |||
| // getAddress(int& a0, int& a1, int& a2, int& a3) | |||
| void SocketAddress::getAddress(int& a0, int& a1, int& a2, int& a3) { // Get the IP address into 4 ints | |||
| unsigned long A = getAddress(); // Get the address. | |||
| splitIP(A, a0, a1, a2, a3); // Split it into octets. | |||
| } | |||
| //// TCPListener methods /////////////////////////////////////////////////////// | |||
| TCPListener::TCPListener(unsigned short Port) { // Set up localhost on this Port. | |||
| LocalAddress.setPort(Port); // Establish the port. | |||
| LocalAddress.setAddress(LOCALHOST); // Set the address to LOCALHOST. | |||
| MaxPending = DefaultMaxPending; // Use the default inbound queue size. | |||
| ReuseAddress = true; // ReuseAddress on by default. | |||
| OpenStage1Complete = false; // This stage of open() not yet done. | |||
| OpenStage2Complete = false; // This stage of open() not yet done. | |||
| // Create a socket... | |||
| LastError = 0; | |||
| Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket. | |||
| if(INVALID_SOCKET == Handle) { // If that operation failed then | |||
| LastError = Network.getLastError(); // grab the error code and | |||
| throw Networking::SocketCreationError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPListener::open().socket()", LastError)); | |||
| } | |||
| } | |||
| TCPListener::TCPListener(SocketAddress& WhereToBind) { // Set up specific "name" for listening. | |||
| LocalAddress = WhereToBind; // Make my Local address as provided. | |||
| MaxPending = DefaultMaxPending; // Use the default inbound queue size. | |||
| ReuseAddress = true; // ReuseAddress on by default. | |||
| OpenStage1Complete = false; // This stage of open() not yet done. | |||
| OpenStage2Complete = false; // This stage of open() not yet done. | |||
| // Create a socket... | |||
| LastError = 0; | |||
| Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket. | |||
| if(INVALID_SOCKET == Handle) { // If that operation failed then | |||
| LastError = Network.getLastError(); // grab the error code and | |||
| throw Networking::SocketCreationError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPListener::open().socket()", LastError)); | |||
| } | |||
| } | |||
| // open() | |||
| void TCPListener::open() { // Open when ready. | |||
| if(OpenSucceeded) return; // If open already, we're done. | |||
| LastError = 0; // Clear the last error. | |||
| bool SuccessFlag = true; // Start optimistically. | |||
| // Set SO_REUSEADDR if turned on | |||
| if(!OpenStage1Complete) { // Do this stage only once. | |||
| int ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag. | |||
| int result = // Set SO_REUSEADDR before bind(). | |||
| setsockopt( | |||
| Handle, | |||
| SOL_SOCKET, | |||
| SO_REUSEADDR, | |||
| (char*) &ReuseAddress_Flag, | |||
| sizeof(ReuseAddress_Flag)); | |||
| if(0 > result) { // If there was an error then | |||
| SuccessFlag = false; // we did not succeed. | |||
| LastError = Network.getLastError(); // Capture the error information and | |||
| throw Networking::SocketSetSockOptError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPListener::open().setsockopt()", LastError)); | |||
| } | |||
| OpenStage1Complete = true; // Stage 1 complete now. | |||
| } // End of open() stage 1 | |||
| // Next we bind it... | |||
| if(!OpenStage2Complete) { // Do this stage only once. | |||
| int result = // Bind our socket to the LocalAddress. | |||
| bind( | |||
| Handle, | |||
| LocalAddress.getPtr_sockaddr(), | |||
| LocalAddress.getAddressSize()); | |||
| if(0 > result) { // If there was an error then | |||
| SuccessFlag = false; // we did not succeed. | |||
| LastError = Network.getLastError(); // Capture the error information and | |||
| throw Networking::SocketBindError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPListener::open().bind()", LastError)); | |||
| } | |||
| OpenStage2Complete = true; // Stage 2 complete now. | |||
| } // End of open() stage 2 | |||
| // Then we put it in a listening state... | |||
| int result = listen(Handle, MaxPending); // Listen for up to MaxPending at once. | |||
| if(0 > result) { // If an error occurred then | |||
| SuccessFlag = false; // we did not succeed. | |||
| LastError = Network.getLastError(); // Capture the error information and | |||
| throw Networking::SocketListenError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPListener::open().listen()", LastError)); | |||
| } | |||
| OpenSucceeded = SuccessFlag; // So, did we succeed? | |||
| } | |||
| // acceptClient() | |||
| TCPClient* TCPListener::acceptClient() { // Accept a client connection. | |||
| LastError = 0; // Clear the last error. | |||
| socklen_t rsize = RemoteAddress.getAddressSize(); // Size as an int for accept(). | |||
| hSocket NewHandle = // Accept a new connection if available. | |||
| accept( | |||
| Handle, // use our handle, of course,... | |||
| RemoteAddress.getPtr_sockaddr(), // and store the remote hosts | |||
| &rsize); // address for us. | |||
| if(INVALID_SOCKET == NewHandle) { // If there was an error then | |||
| LastError = Network.getLastError(); // capture the error value. | |||
| if(!Network.WouldBlock(LastError)) { // If it's not a EWOULDBLOCK error | |||
| throw Networking::SocketAcceptError( // then we need to throw. | |||
| Network.DescriptiveError( | |||
| "TCPListener::acceptClient()", LastError)); | |||
| } else { // EWOULDBLOCK errors are normal in | |||
| return NULL; // non blocking mode so we return | |||
| } // NULL when we see them. | |||
| } | |||
| // If things have gone well we can do what we came for. | |||
| return new TCPClient(*this, NewHandle, RemoteAddress); // Create the new TCPClient object. | |||
| } | |||
| //// TCPClient methods ///////////////////////////////////////////////////////// | |||
| int TCPClient::transmit(const char* bfr, int size) { // How to send a buffer of data. | |||
| LastError = 0; // No errors yet. | |||
| if(0 == size) return 0; // Nothing to send, send nothing. | |||
| if(0 == bfr) // Watch out for null buffers. | |||
| throw Networking::SocketWriteError("TCPClient::transmit() NULL Bfr!"); | |||
| if(0 > size) // Watch out for bad sizes. | |||
| throw Networking::SocketWriteError("TCPClient::transmit() 0 > size!"); | |||
| int ByteCount = send(Handle, bfr, size, NOFLAGS); // Try to send and capture the count. | |||
| if(0 > ByteCount) ByteCount = 0; // Mask error results as 0 bytes sent. | |||
| if(size > ByteCount) { // If we didn't send it all check it out. | |||
| LastError = Network.getLastError(); // Grab the error code. | |||
| if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then | |||
| return ByteCount; // it was a partial send - return. | |||
| } else { // If this was a different kind of error | |||
| throw Networking::SocketWriteError( // then throw! | |||
| Network.DescriptiveError( | |||
| "TCPClient::transmit()", LastError)); | |||
| } | |||
| } | |||
| return ByteCount; // Ultimately return the byte count. | |||
| } | |||
| int TCPClient::receive(char* bfr, int size) { // How to receive a buffer of data. | |||
| if(ReadBufferIsEmpty()) { // If the read buffer is empty then | |||
| fillReadBuffer(); // fill it first. | |||
| } // Optimize our transfer to the smaller | |||
| if(DataLength < size) { // of what we have or the size of the | |||
| size = DataLength; // provided buffer. This way we ony check | |||
| } // one value in our copy loop ;-) | |||
| int RemainingDataLength = size; // Capture the length of data to xfer. | |||
| while(0 < RemainingDataLength) { // While we have work to do | |||
| *bfr = *ReadPointer; // copy each byte from our ReadBuffer, | |||
| bfr++; ReadPointer++; // move the pointers to the next byte, | |||
| DataLength--; // update our ReadBuffers's DataLength, | |||
| RemainingDataLength--; // and count down the bytes left to xfer. | |||
| } | |||
| return size; // When done, say how much we moved. | |||
| } | |||
| int TCPClient::delimited_receive(char* bfr, int size, char delimiter) { // How to receive delimited data. | |||
| if(ReadBufferIsEmpty()) { // If the read buffer is empty then | |||
| fillReadBuffer(); // fill it first. | |||
| } // Optimize our transfer to the smaller | |||
| if(DataLength < size) { // of what we have or the size of the | |||
| size = DataLength; // provided buffer. This way we ony check | |||
| } // one value in our copy loop ;-) | |||
| int Count = 0; // Keep our byte count in scope. | |||
| bool DelimiterNotReached = true; // Watching for our deliimiter. | |||
| while((Count < size) && DelimiterNotReached) { // While there is work to do... | |||
| *bfr = *ReadPointer; // copy each byte from our ReadBuffer, | |||
| DelimiterNotReached = (delimiter != (*bfr)); // check for the delimiter character, | |||
| bfr++; ReadPointer++; // move the pointers to the next byte, | |||
| DataLength--; // update our ReadBuffers's DataLength, | |||
| Count++; // and count up the bytes we have moved. | |||
| } | |||
| return Count; // When done, say how much we moved. | |||
| } | |||
| //// TCPHost methods /////////////////////////////////////////////////////////// | |||
| // Constructors... | |||
| TCPHost::TCPHost(unsigned short Port) { // Will connect to localhost on Port. | |||
| RemoteAddress.setPort(Port); // Connect to Port on | |||
| RemoteAddress.setAddress(LOCALHOST); // Localhost. | |||
| ReadPointer = ReadBuffer; // Set the read position to zero. | |||
| DataLength = 0; // There is no data yet. | |||
| ReuseAddress = false; // ReuseAddress off by default. | |||
| OpenStage1Complete = false; // Stage 1 of open() not done yet. | |||
| // Create a socket to use. | |||
| LastError = 0; // Clear our last error value. | |||
| Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket. | |||
| if(0 > Handle) { // If that operation failed then | |||
| LastError = Network.getLastError(); // grab the error code and | |||
| throw Networking::SocketCreationError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPHost::open().socket()", LastError)); | |||
| } | |||
| } | |||
| TCPHost::TCPHost(SocketAddress& Remote) { // Will connect to Remote address/port. | |||
| RemoteAddress = Remote; // Capture the provided address. | |||
| ReadPointer = ReadBuffer; // Set the read position to zero. | |||
| DataLength = 0; // There is no data yet. | |||
| ReuseAddress = false; // ReuseAddress off by default. | |||
| OpenStage1Complete = false; // Stage 1 of open() not done yet. | |||
| // Create a socket to use. | |||
| LastError = 0; // Clear our last error value. | |||
| Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket. | |||
| if(0 > Handle) { // If that operation failed then | |||
| LastError = Network.getLastError(); // grab the error code and | |||
| throw Networking::SocketCreationError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPHost::open().socket()", LastError)); | |||
| } | |||
| } | |||
| // Methods... | |||
| void TCPHost::open() { // We provide open(). | |||
| if(OpenSucceeded) return; // If open already, we're done. | |||
| LastError = 0; // Clear our LastError value. | |||
| bool SuccessFlag = true; // Begin optimistically. | |||
| // Set SO_REUSEADDR if turned on | |||
| if(!OpenStage1Complete) { // If we haven't done this yet: | |||
| int ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag. | |||
| int result = // Set SO_REUSEADDR before bind(). | |||
| setsockopt( | |||
| Handle, | |||
| SOL_SOCKET, | |||
| SO_REUSEADDR, | |||
| (char *)&ReuseAddress_Flag, | |||
| sizeof(ReuseAddress_Flag)); | |||
| if(0 > result) { // If there was an error then | |||
| SuccessFlag = false; // we did not succeed. | |||
| LastError = Network.getLastError(); // Capture the error information and | |||
| throw Networking::SocketSetSockOptError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPListener::open().setsockopt()", LastError)); | |||
| } | |||
| OpenStage1Complete = true; // Skip this section from now on. | |||
| } // Done with stage 1. | |||
| // Connect the socekt to the Host. | |||
| int result = // Connect to the remote host | |||
| connect( // using the socket we just | |||
| Handle, // stored in Handle and | |||
| RemoteAddress.getPtr_sockaddr(), // the Remote address. | |||
| RemoteAddress.getAddressSize()); | |||
| if(0 > result) { // If there was an error then | |||
| SuccessFlag = false; // we did not succeed. | |||
| LastError = Network.getLastError(); // Record the error data. | |||
| if(Network.IsConnected(LastError)) { // If we actually did succeed then | |||
| SuccessFlag = true; // say so. (Silly Winsock!) | |||
| } else // But if that's not the case check | |||
| if( // to see if something bad happened - | |||
| !Network.WouldBlock(LastError) && // not just would-block, or | |||
| !Network.InProgress(LastError) // in progress... | |||
| ) { // If it was something other than | |||
| throw Networking::SocketConnectError( // WouldBlock or InProgress then | |||
| Network.DescriptiveError( // throw! | |||
| "TCPHost::open().connect()", LastError)); | |||
| } // If it was WouldBlock then it's | |||
| } // considered to be ok. | |||
| OpenSucceeded = SuccessFlag; // So, are we open now? | |||
| } | |||
| int TCPHost::transmit(const char* bfr, int size) { // How to send a buffer of data. | |||
| LastError = 0; // No errors yet. | |||
| if(0 == size) return 0; // Nothing to send, send nothing. | |||
| if(0 == bfr) // Watch out for null buffers. | |||
| throw Networking::SocketWriteError("TCPHost::transmit() NULL Bfr!"); | |||
| if(0 > size) // Watch out for bad sizes. | |||
| throw Networking::SocketWriteError("TCPHost::transmit() 0 > size!"); | |||
| int ByteCount = send(Handle, bfr, size, NOFLAGS); // Try to send and capture the count. | |||
| if(0 > ByteCount) ByteCount = 0; // Mask error results as 0 bytes sent. | |||
| if(size > ByteCount) { // If we didn't send it all check it out. | |||
| LastError = Network.getLastError(); // Grab the error code. | |||
| if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then | |||
| return ByteCount; // it was a partial snd - return. | |||
| } else { // If this was a different kind of error | |||
| throw Networking::SocketWriteError( // then throw! | |||
| Network.DescriptiveError( | |||
| "TCPHost::transmit().send()", LastError)); | |||
| } | |||
| } | |||
| return ByteCount; // Ultimately return the byte count. | |||
| } | |||
| int TCPHost::receive(char* bfr, int size) { // How to receive a buffer of data. | |||
| if(ReadBufferIsEmpty()) { // If the read buffer is empty then | |||
| fillReadBuffer(); // fill it first. | |||
| } // Optimize our transfer to the smaller | |||
| if(DataLength < size) { // of what we have or the size of the | |||
| size = DataLength; // provided buffer. This way we ony check | |||
| } // one value in our copy loop ;-) | |||
| int RemainingDataLength = size; // Capture the length of data to xfer. | |||
| while(0 < RemainingDataLength) { // While we have work to do | |||
| *bfr = *ReadPointer; // copy each byte from our ReadBuffer, | |||
| bfr++; ReadPointer++; // move the pointers to the next byte, | |||
| DataLength--; // update our ReadBuffers's DataLength, | |||
| RemainingDataLength--; // and count down the bytes left to xfer. | |||
| } | |||
| return size; // When done, say how much we moved. | |||
| } | |||
| int TCPHost::delimited_receive(char* bfr, int size, char delimiter) { // How to receive delimited data. | |||
| if(ReadBufferIsEmpty()) { // If the read buffer is empty then | |||
| fillReadBuffer(); // fill it first. | |||
| } // Optimize our transfer to the smaller | |||
| if(DataLength < size) { // of what we have or the size of the | |||
| size = DataLength; // provided buffer. This way we ony check | |||
| } // one value in our copy loop ;-) | |||
| int Count = 0; // Keep our byte count in scope. | |||
| bool DelimiterNotReached = true; // Watching for our deliimiter. | |||
| while((Count < size) && DelimiterNotReached) { // While there is work to do... | |||
| *bfr = *ReadPointer; // copy each byte from our ReadBuffer, | |||
| DelimiterNotReached = (delimiter != (*bfr)); // check for the delimiter character, | |||
| bfr++; ReadPointer++; // move the pointers to the next byte, | |||
| DataLength--; // update our ReadBuffers's DataLength, | |||
| Count++; // and count up the bytes we have moved. | |||
| } | |||
| return Count; // When done, say how much we moved. | |||
| } | |||
| // End Platform Agnostic Stuff | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| @@ -0,0 +1,529 @@ | |||
| // networking.hpp | |||
| // Copyright (C) 2006-2009 MicroNeil Research Corporation. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 | |||
| //============================================================================== | |||
| // The networking module abstracts network communications and provides a set | |||
| // of objects for handling most tasks. | |||
| // 20080313 _M Refactored to throw proper runtime_error exceptions. | |||
| // Include only once... | |||
| #ifndef M_Networking | |||
| #define M_Networking | |||
| #include <stdexcept> | |||
| #include <iostream> | |||
| #include <string> | |||
| #include <sstream> | |||
| #include <cstring> | |||
| using namespace std; | |||
| //// Platform specific includes... | |||
| #if defined(WIN32) || defined(WIN64) | |||
| //// Windows headers... | |||
| #include <winsock2.h> | |||
| typedef int socklen_t; // Posix uses socklen_t so we mimic it. | |||
| typedef SOCKET hSocket; // Winx handles Socket is opaque. | |||
| #else | |||
| //// GNU Headers... | |||
| #include <netdb.h> | |||
| #include <sys/socket.h> | |||
| #include <netinet/in.h> | |||
| #include <sys/file.h> | |||
| #include <arpa/inet.h> | |||
| #include <unistd.h> | |||
| #include <fcntl.h> | |||
| #include <cstdlib> | |||
| #include <cstdio> | |||
| #include <cerrno> | |||
| typedef int hSocket; // *nix uses int to handle a Socket. | |||
| const hSocket INVALID_SOCKET = -1; // -1 is the invalid Socket. | |||
| #endif | |||
| //// Tuning and Constants ////////////////////////////////////////////////////// | |||
| const unsigned long LOCALHOST = 0x7F000001; // 127.0.0.1 as an integer. | |||
| const int DefaultMaxPending = 5; // Default connection queue size. | |||
| const int TCPClientBufferSize = 4096; // TCP Client buffer size. | |||
| const int TCPHostBufferSize = 4096; // TCP Host buffer size. | |||
| const int NOFLAGS = 0; // Magic number for no flags. | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // IP4address class | |||
| // | |||
| // The IP4address class makes it easy to manipulate IPs. | |||
| class IP4Address { // IP4Address manipulator. | |||
| private: | |||
| unsigned long int IP; // The actual data. | |||
| public: | |||
| IP4Address(); // Blank constructor IP = 0.0.0.0 | |||
| IP4Address(const unsigned long int newIP); // Constructor given unsigned long | |||
| IP4Address(const IP4Address&); // Constructor given an IP4Address | |||
| IP4Address(const char* newIP); // Construcing with a cstring. | |||
| IP4Address(const string& newIP); // Constructing with a cppstring. | |||
| IP4Address& operator=(const unsigned long int Right); // Convert from unsigned long int. | |||
| IP4Address& operator=(const char* Right); // Convert from c string. | |||
| IP4Address& operator=(const string& Right); // Convert from cpp string. | |||
| operator unsigned long int() const; | |||
| operator string() const; | |||
| bool operator<(const IP4Address Right) const; // < Comparison. | |||
| bool operator>(const IP4Address Right) const; // > Comparison. | |||
| bool operator==(const IP4Address Right) const; // == Comparison. | |||
| bool operator!=(const IP4Address Right) const; // != Comparison. | |||
| bool operator<=(const IP4Address Right) const; // <= Comparison. | |||
| bool operator>=(const IP4Address Right) const; // >= Comparison. | |||
| }; | |||
| /* static unsigned long int& | |||
| operator=(unsigned long int& Out, const IP4Address& In); // Assign to unsigned long | |||
| static string& | |||
| operator=(string& Out, const IP4Address& In); // Assign to cpp string | |||
| */ | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Network Core class | |||
| // | |||
| // The Networking class acts as a central point for setup, cleanup, and access | |||
| // to network services. For example, when using WinSock, the DLL initialization | |||
| // must occur once when the program starts up and the shutdown must occur once | |||
| // as the program shuts down. The constructor and destructor of the "Network" | |||
| // instances of this class handles that work. There should only be one instance | |||
| // of this class anywhere in the program and that instance is created when this | |||
| // module is included. DON'T MAKE MORE INSTANCES OF THIS :-) | |||
| // | |||
| // Part of the reason for this class is to handle all of the cross-platform | |||
| // weirdness involved in handling sockets and conversions. This way all of the | |||
| // ifdef switched code can be consolidated into this utility class and the | |||
| // code for the remaining classes can remain nice and clean by using this | |||
| // class to handle those tasks. | |||
| class Networking { | |||
| private: | |||
| public: | |||
| class NotSupportedError : public runtime_error { // Thrown when something can't be done. | |||
| public: NotSupportedError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class InitializationError : public runtime_error { // Thrown if initialization fails. | |||
| public: InitializationError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class ControlError : public runtime_error { // Thrown if control functions fail. | |||
| public: ControlError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketCreationError : public runtime_error { // Thrown if a call to socket() fails. | |||
| public: SocketCreationError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketSetSockOptError : public runtime_error { | |||
| public: SocketSetSockOptError(const string& w):runtime_error(w) {} // Thrown if a call to setsockopt() fails. | |||
| }; | |||
| class SocketBindError : public runtime_error { // Thrown if a call to bind() fails. | |||
| public: SocketBindError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketListenError : public runtime_error { // Thrown if a call to listen() fails. | |||
| public: SocketListenError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketConnectError : public runtime_error { // Thrown if a call to connect() fails. | |||
| public: SocketConnectError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketAcceptError : public runtime_error { // Thrown if a call to accept() fails. | |||
| public: SocketAcceptError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketReadError : public runtime_error { // Thrown if a socket read call fails. | |||
| public: SocketReadError(const string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketWriteError : public runtime_error { // Thrown if a socket write call fails. | |||
| public: SocketWriteError(const string& w):runtime_error(w) {} | |||
| }; | |||
| static string DescriptiveError(string Msg, int Errno); // Form a descriptive error w/ errno. | |||
| Networking(); | |||
| ~Networking(); | |||
| int getLastError(); // WSAGetLastError or errno | |||
| int setNonBlocking(hSocket socket); // Set socket to non-blocking. | |||
| int closeSocket(hSocket socket); // closesocket() or close() | |||
| bool WouldBlock(int ErrorCode); // ErrorCode matches [WSA]EWOULDBLOCK | |||
| bool InProgress(int ErrorCode); // ErrorCode matches [WSA]EINPROGRESS | |||
| bool IsConnected(int ErrorCode); // ErrorCode matches [WSA]EISCONN | |||
| }; | |||
| extern Networking Network; // There is ONE Network object ;-) | |||
| // End of Network Core Class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // SocketName class | |||
| // This class represents a communications end-point on a TCP/IP network. All | |||
| // conversions from/to strings and for byte orders are handled in this class | |||
| // as well as lookups for ports/services and IPaddresses/host-names. | |||
| // | |||
| // Note that the cstring conversions expect the buffer to be large enough. | |||
| const int IPStringBufferSize = 40; // Safe size for IP as text conversion. | |||
| const int PortStringBufferSize = 20; // Safe size for Port as text conversion. | |||
| class SocketAddress { | |||
| private: | |||
| struct sockaddr_in Address; // Socket address structure. | |||
| char IPStringBuffer[IPStringBufferSize]; // Handy conversion buffer. | |||
| char PortStringBuffer[PortStringBufferSize]; // Handy conversion buffer. | |||
| public: | |||
| SocketAddress(); // Constructor sets ANY address. | |||
| struct sockaddr_in* getPtr_sockaddr_in(); // Returns a pointer to sockaddr_in. | |||
| struct sockaddr* getPtr_sockaddr(); // Returns a pointer to sockaddr. | |||
| socklen_t getAddressSize(); // How big is that structure anyway? | |||
| void setAddress(unsigned long ipAddress); // Set the IP address from an unsigned int | |||
| void setAddress(char* ipString); // Set the IP address from a cstring | |||
| unsigned long getAddress(); // Get the IP address as an unsigned int | |||
| const char* getAddress(char* str); // Get the IP address into a cstring | |||
| void getAddress(int& a0, int& a1, int& a2, int& a3); // Get the IP address into 4 ints | |||
| void setPort(unsigned short port); // Set the port address from an int | |||
| void setPort(char* port); // Set the port address from a cstring | |||
| unsigned short getPort(); // Get the port address as an unsigned int | |||
| const char* getPort(char* str); // Get the port address into a cstring | |||
| void clear(); // Initialize the address. | |||
| }; | |||
| // End of SocketName class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Socket class | |||
| // This class abstracts the underlying socket and adds some functionality | |||
| // for this module. The derived class is expected to setup the socket before | |||
| // it can be opened. In fact, the derivative class must provide the open() | |||
| // function :-) Open is expected to call socket, bind it, and set the socket | |||
| // into the appropriate mode for it's use in the derived object. | |||
| class Socket { | |||
| protected: | |||
| hSocket Handle; // Our socket handle. | |||
| bool NonBlocking; // True if the socket is NonBlocking. | |||
| bool ReuseAddress; // True if SO_REUSEADDR should be used. | |||
| bool OpenSucceeded; // Successful open occurred. | |||
| int LastError; // Last error result for this socket. | |||
| SocketAddress LocalAddress; // Our local address data. | |||
| SocketAddress RemoteAddress; // Our remote address data. | |||
| public: | |||
| Socket(); // Constructor sets initial state. | |||
| ~Socket(); // Destructor closes Socket if open. | |||
| hSocket getHandle(); // Returns the current SocketId. | |||
| bool isNonBlocking(); // Returns true if socket is NonBlocking | |||
| void makeNonBlocking(); // Sets the socket to NonBlocking mode. | |||
| bool isReuseAddress(); // True if socket is set SO_REUSEADDR. | |||
| bool isReuseAddress(bool set); // Changes SO_REUSEADDR setting. | |||
| bool isOpen(); // True if the socket is open. | |||
| int getLastError(); // Returns the last error for this socket. | |||
| virtual void open() = 0; // Derived class specifies open(); | |||
| void close(); // Close politely. | |||
| }; | |||
| // End of Socket class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // MessagePort class | |||
| // Interface that Sends and Receives messages - possibly a bit at a time. This | |||
| // interface standardizes things so that multiple technologies can go beneith | |||
| // such as UNIX domain pipes or named pipes or sockets etc. There is also a | |||
| // special function to improve the efficiency of delimited transfers (such as | |||
| // email). The function checks for the delimited byte inside an optimized loop | |||
| // so that the port doesn't have to be read one byte at a time by the caller. | |||
| // In the case of non-blocking ports, these methods may return before all of | |||
| // the data has been transferred. In these cases the caller is expected to know | |||
| // if it's got the complete message and is expected to repeat it's call until | |||
| // it does. | |||
| class MessagePort { | |||
| public: | |||
| virtual bool isNonBlocking() = 0; // True if we should expect partial xfrs. | |||
| virtual int transmit(const char* bfr, int size) = 0; // How to send a buffer of data. | |||
| virtual int receive(char* bfr, int size) = 0; // How to receive a buffer of data. | |||
| virtual int delimited_receive(char* bfr, int size, char delimiter) = 0; // How to receive delimited data. | |||
| }; | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Message class | |||
| // This is a base class for representing messages that are sent to or received | |||
| // from MessagePorts. The basic Message has 3 modes. Unfixed width, fixed width, | |||
| // or delimeted. More complex messaging schemes can be built up from these | |||
| // basics. A message must know how to send and recieve itself using the | |||
| // MessagePort API and must be able to indicate if the latest transfer request | |||
| // is complete or needs to be continued. The MessagePort may be blocking or | |||
| // non-blocking. If it is blocking then a writeTo() or readFrom() operation | |||
| // should not return until the transfer is completed. If the MessagePort is in | |||
| // a non-blocking mode then writeTo() and readFrom() will do as much as they | |||
| // can before returning but if the transfer was not completed then the app | |||
| // lication may need to transferMore(). | |||
| class Message { | |||
| char* Data; // Pointer to message data. | |||
| int DataBufferSize; // Size of buffer to hold data. | |||
| int DataSize; // Size of Data. | |||
| char* RWPointer; // RW position in buffer. | |||
| bool TransferInProgress; // True if read or write is not complete. | |||
| bool Delimited; // Delimited Message Flag. | |||
| char Delimiter; // Delimiter character. | |||
| public: | |||
| /** All of this is yet to be built! **/ | |||
| Message(const Message& M); // Copy constructor. | |||
| Message(int Size); // Construct empty of Size. | |||
| Message(int Size, char Delimiter); // Construct empty with delimiter. | |||
| Message(char* NewData, int Size); // Construct non-delimited message. | |||
| Message(char* NewData, int Size, char Delimiter); // Construct delimited message. | |||
| void writeTo(MessagePort &P); // Initiate an outbound transfer. | |||
| void readFrom(MessagePort &P); // Initiate an inbound transfer. | |||
| bool isBusy(); // True if the transfer isn't complete. | |||
| void transferMore(); // Do more of the transfer. | |||
| void abortTransfer(); // Forget about the transfer. | |||
| bool isDelimited(); // True if the message is delimited. | |||
| char getDelimiter(); // Read the delimiter cahracter. | |||
| char* getData(); // Access the data buffer. | |||
| int getDataSize(); // How much data is there. | |||
| }; | |||
| // End of Message class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // TCPListener class | |||
| // This class represents a local socket used to listen for new connections. The | |||
| // application can poll this object for new inbound connections which are then | |||
| // delivered as TCPClient objects. | |||
| class TCPClient; // Hint about the coming client class. | |||
| class TCPListener : public Socket { | |||
| private: | |||
| bool OpenStage1Complete; // First stage of open() complete. | |||
| bool OpenStage2Complete; // Second stage of open() complete. | |||
| public: | |||
| TCPListener(unsigned short Port); // Set up localhost on this Port. | |||
| TCPListener(SocketAddress& WhereToBind); // Set up specific "name" for listening. | |||
| ~TCPListener(); // Close when destructing. | |||
| int MaxPending; // Maximum inbound connection queue. | |||
| virtual void open(); // Open when ready. | |||
| TCPClient* acceptClient(); // Accept a client connection. | |||
| }; | |||
| // End of TCPListener class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // TCPClient class | |||
| // This class represents a TCP network client connection. It is created by | |||
| // a TCPListener object if/when a TCP connection is made to the Listener and | |||
| // accepted by the application. | |||
| class TCPClient : public Socket, public MessagePort { | |||
| private: | |||
| TCPListener& MyListener; | |||
| char ReadBuffer[TCPClientBufferSize]; // Buffer for delimited reading. | |||
| //int ReadBufferSize; // Size of buffer. | |||
| char* ReadPointer; // Read position. | |||
| int DataLength; // Length of data in buffer. | |||
| bool ReadBufferIsEmpty(); // True if DataLength is zero. | |||
| void fillReadBuffer(); // Fill the ReadBuffer from the socket. | |||
| public: | |||
| TCPClient(TCPListener& L, hSocket H, SocketAddress& A); // How to create a TCPClient. | |||
| ~TCPClient(); // Destructor for cleanup. | |||
| TCPListener& getMyListener(); // Where did I come from? | |||
| bool isNonBlocking(); // Provided for MessagePort. | |||
| virtual int transmit(const char* bfr, int size); // How to send a buffer of data. | |||
| virtual int receive(char* bfr, int size); // How to receive a buffer of data. | |||
| virtual int delimited_receive(char* bfr, int size, char delimiter); // How to receive delimited data. | |||
| virtual void open(); // We provide open() as unsupported. | |||
| unsigned long getRemoteIP(); // Get remote IP as long. | |||
| const char* getRemoteIP(char* str); // Get IP as string. | |||
| unsigned short getRemotePort(); // Get remote Port as unsigned short. | |||
| const char* getRemotePort(char* str); // Get Port as string. | |||
| }; | |||
| // End of TCPClient class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // TCPHost class | |||
| // This class represents a TCP network server connection. A client application | |||
| // creates this object when it wants to connect to a given TCP service. | |||
| class TCPHost : public Socket, public MessagePort { | |||
| private: | |||
| char ReadBuffer[TCPHostBufferSize]; // Buffer for delimited reading. | |||
| char* ReadPointer; // Read position. | |||
| int DataLength; // Length of data in buffer. | |||
| bool ReadBufferIsEmpty(); // True if DataLength is zero. | |||
| void fillReadBuffer(); // Fill the ReadBuffer from the socket. | |||
| bool OpenStage1Complete; // Skip stage 1 of open() after done. | |||
| public: | |||
| TCPHost(unsigned short Port); // Will connect to localhost on Port. | |||
| TCPHost(SocketAddress& Remote); // Will connect to Remote address/port. | |||
| // TCPHost(SocketAddress& Local, SocketAddress& Remote); // Will connect to Remote from Local. | |||
| ~TCPHost(); // Clean up when we go away. | |||
| bool isNonBlocking(); // Provided for MessagePort. | |||
| virtual int transmit(const char* bfr, int size); // How to send a buffer of data. | |||
| virtual int receive(char* bfr, int size); // How to receive a buffer of data. | |||
| virtual int delimited_receive(char* bfr, int size, char delimiter); // How to receive delimited data. | |||
| virtual void open(); // We provide open(). | |||
| }; | |||
| // End of TCPHost class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // UDPListener class | |||
| // This class represents a local UPD port set up to listen for UDP requests. In | |||
| // this case, each UDP packet that arrives is assumed to be a single request so | |||
| // for each a UDPRequest object is created that links back to this Listener. | |||
| // The application can then use that UDPRequest to .respond() with a Message. | |||
| // the response is sent back to the original requester and the UDPRequest is | |||
| // considered satisfied. | |||
| class UDPListener : public Socket { | |||
| }; | |||
| // End of UDPListener class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // UDPRequest class | |||
| // This class is created by a UDPListener when a packet is received. The object | |||
| // contains all of the necessary information about the source for the request | |||
| // so that the application can .respond() to them through this object. The | |||
| // response UDP packtes are sent through the UDPListener socket. | |||
| class UDPRequest : public MessagePort { | |||
| }; | |||
| // End of UDPRequest class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // UDPHost class | |||
| // This class represents a server/host on the network that uses the UDP | |||
| // protocol. The application can use this object to send a .request() Message | |||
| // and getReply(). Each request becomes a UDP packet. Each received UDP packet | |||
| // from the specified UDPHost becomes a reply Message. (Connected UDP socket). | |||
| class UDPHost : public Socket, public MessagePort { | |||
| }; | |||
| // End of UDPHost class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // UDPReceiver class | |||
| // This class is used to receive UDP packets on a particular port, but does not | |||
| // create UDPRequest objects from them - they are considered to be simply | |||
| // Messages. A UDPReceiver is most likely to be used in ad-hoc networking and | |||
| // to receive advertisements and/or broadcasts from other peers. | |||
| class UDPReceiver : public Socket, public MessagePort { | |||
| }; | |||
| // End of UDPReceiver class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // UDPBroadcaster class | |||
| // This class is used to advertise / broadcast Messages using UDP. | |||
| class UDPBroadcaster : public Socket, public MessagePort { | |||
| }; | |||
| // End of UDPBroadcaster class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //// Include Inline methods and functions... | |||
| #include "networking.inline.hpp" | |||
| #endif | |||
| // End include Networking.hpp only once... | |||
| @@ -0,0 +1,368 @@ | |||
| // networking.inline.hpp | |||
| // Copyright (C) 2006-2009 MicroNeil Research Corporation. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 | |||
| //============================================================================== | |||
| // Inlined methods for Networking module. See networking.hpp for notes. | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Platform Specific | |||
| //// Windows platform | |||
| #if defined(WIN32) || (WIN64) | |||
| inline int Networking::getLastError() { // In windows you get the last error | |||
| return WSAGetLastError(); // from WSAGetLastError(); | |||
| } | |||
| inline int Networking::setNonBlocking(hSocket socket) { // Set a winsock to non-blocking | |||
| unsigned long nonblocking = 1; // Create a flag... | |||
| int result = 0; | |||
| if(0 != ioctlsocket(socket, FIONBIO, &nonblocking)) { // Set the state of the socket. | |||
| result = -1; // If that fails then return -1. | |||
| } | |||
| return result; // Show 'em my motto! | |||
| } | |||
| inline int Networking::closeSocket(hSocket socket) { // Close a socket in winsock | |||
| return closesocket(socket); // wraps closesocket(). | |||
| } | |||
| inline bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK. | |||
| return (WSAEWOULDBLOCK == ErrorCode); | |||
| } | |||
| inline bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS. | |||
| return( // [WSA]EALREADY also returns true. | |||
| WSAEINPROGRESS == ErrorCode || // In fact, on Win* platforms we could | |||
| WSAEALREADY == ErrorCode || // get any of these when retesting | |||
| WSAEWOULDBLOCK == ErrorCode || // open() for a connection. | |||
| WSAEINVAL == ErrorCode | |||
| ); | |||
| } | |||
| inline bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN. | |||
| return(WSAEISCONN == ErrorCode); | |||
| } | |||
| #else | |||
| //// GNU platform | |||
| inline int Networking::getLastError() { // In GNU you get the last error | |||
| return errno; // from errno; | |||
| } | |||
| inline int Networking::setNonBlocking(hSocket socket) { // Set a socket to non-blocking | |||
| int flags, result; // Grab a place to hold the flags. | |||
| flags = fcntl(socket, F_GETFL, 0); // Get the current flags. | |||
| result = fcntl(socket, F_SETFL, flags | O_NONBLOCK); // Set the NONBLOCK flag & return. | |||
| return result; // Return the result. | |||
| } | |||
| inline int Networking::closeSocket(hSocket socket) { // Close a socket in GNU | |||
| return close(socket); // wraps close(). | |||
| } | |||
| inline bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK. | |||
| return (EWOULDBLOCK == ErrorCode); | |||
| } | |||
| inline bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS. | |||
| return( // [WSA]EALREADY also returns true. | |||
| EINPROGRESS == ErrorCode || | |||
| EALREADY == ErrorCode | |||
| ); | |||
| } | |||
| inline bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN. | |||
| return(EISCONN == ErrorCode); | |||
| } | |||
| #endif | |||
| // End Platform Specific | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Begin Platform Agnostic | |||
| //// class IP4Address ////////////////////////////////////////////////////////// | |||
| inline IP4Address::IP4Address():IP(0){} // Blank constructor IP = 0.0.0.0 | |||
| inline IP4Address::IP4Address(const unsigned long int newIP):IP(newIP){} // Constructor given unsigned long | |||
| inline IP4Address::IP4Address(const IP4Address& newIP):IP(newIP.IP){} // Constructor given an IP4Address | |||
| inline IP4Address::IP4Address(const char* newIP) { (*this) = newIP; } // Construcing with a cstring. | |||
| inline IP4Address::IP4Address(const string& newIP) { (*this) = newIP; } // Constructing with a cppstring. | |||
| inline IP4Address& | |||
| IP4Address::operator=(const unsigned long int Right) { IP = Right; } // Convert from unsigned long int. | |||
| inline IP4Address& IP4Address::operator=(const char* Right) { // Convert from c string. | |||
| IP = ntohl(inet_addr(Right)); | |||
| } | |||
| inline IP4Address& IP4Address::operator=(const string& Right) { // Convert from cpp string. | |||
| IP = ntohl(inet_addr(Right.c_str())); | |||
| } | |||
| inline bool IP4Address::operator<(const IP4Address Right) const { // < Comparison. | |||
| return (IP < Right.IP); | |||
| } | |||
| inline bool IP4Address::operator>(const IP4Address Right) const { // > Comparison. | |||
| return (IP > Right.IP); | |||
| } | |||
| inline bool IP4Address::operator==(const IP4Address Right) const { // == Comparison. | |||
| return (IP == Right.IP); | |||
| } | |||
| inline bool IP4Address::operator!=(const IP4Address Right) const { // != Comparison. | |||
| return (IP != Right.IP); | |||
| } | |||
| inline bool IP4Address::operator<=(const IP4Address Right) const { // <= Comparison. | |||
| return (IP <= Right.IP); | |||
| } | |||
| inline bool IP4Address::operator>=(const IP4Address Right) const { // >= Comparison. | |||
| return (IP >= Right.IP); | |||
| } | |||
| //// class SocketAddress /////////////////////////////////////////////////////// | |||
| inline void SocketAddress::clear() { | |||
| memset(&Address, 0, sizeof(Address)); // Zero out the address strcuture | |||
| Address.sin_family = AF_INET; // Internet Address Family ip4 | |||
| Address.sin_addr.s_addr = htonl(INADDR_ANY); // Any IP address | |||
| Address.sin_port = 0; // Zero means any port. | |||
| } | |||
| inline SocketAddress::SocketAddress() { // Constructor sets up w/ wildcards | |||
| clear(); // Conveniently, we can use clear() :-) | |||
| } | |||
| inline struct sockaddr_in* SocketAddress::getPtr_sockaddr_in() { // Returns a pointer to sockaddr_in. | |||
| return &Address; // Simply return it's address. | |||
| } | |||
| inline struct sockaddr* SocketAddress::getPtr_sockaddr() { // Returns a pointer to sockaddr. | |||
| return (struct sockaddr*) &Address; | |||
| } | |||
| inline socklen_t SocketAddress::getAddressSize() { | |||
| return sizeof(Address); // Return the size of the structure. | |||
| } | |||
| inline void SocketAddress::setAddress(unsigned long ipAddress) { // Set the IP address from an unsigned int | |||
| Address.sin_addr.s_addr = htonl(ipAddress); // Convert to network order and assign. | |||
| } | |||
| inline void SocketAddress::setAddress(char* ipString) { // Set the IP address from a cstring | |||
| Address.sin_addr.s_addr = inet_addr(ipString); // Convert to number and assign. | |||
| } | |||
| inline unsigned long SocketAddress::getAddress() { // Get the IP address as an unsigned int | |||
| return ntohl(Address.sin_addr.s_addr); // Convert to host order and return. | |||
| } | |||
| inline void SocketAddress::setPort(unsigned short port) { // Set the port address from an int | |||
| Address.sin_port = htons(port); // Convert to network order and set. | |||
| } | |||
| inline void SocketAddress::setPort(char* port) { // Set the port address from a cstring | |||
| setPort(atoi(port)); // Convert to int and set. | |||
| } | |||
| inline unsigned short SocketAddress::getPort() { // Get the port address as an unsigned int | |||
| return ntohs(Address.sin_port); // Convert to host order and return. | |||
| } | |||
| inline const char* SocketAddress::getPort(char* str) { // Get the port address into a cstring. | |||
| if(NULL == str) { // If the caller did not provide a | |||
| str = PortStringBuffer; // buffer to use then we will use ours. | |||
| } | |||
| sprintf(str,"%d",getPort()); // Get the port and convert to cstring. | |||
| return str; // Return the string we got. | |||
| } | |||
| //// class Socket ////////////////////////////////////////////////////////////// | |||
| inline Socket::Socket() : // When starting up we are | |||
| Handle(INVALID_SOCKET), OpenSucceeded(false) { // not yet valid. | |||
| } | |||
| inline Socket::~Socket() { // When shutting down, be sure | |||
| close(); // any open socket is closed. | |||
| } | |||
| inline void Socket::close() { // When we close, | |||
| if(INVALID_SOCKET != Handle) { // If the handle is open then | |||
| if(Network.closeSocket(Handle)) { // close the handle and check for error. | |||
| LastError = Network.getLastError(); // If there was an error record it. | |||
| if(!Network.WouldBlock(LastError)) { // If the error was not WOULDBLOCK | |||
| throw Networking::ControlError( // then throw a ControlError exception. | |||
| Network.DescriptiveError( | |||
| "Socket::close()", LastError)); | |||
| } | |||
| } else { // If there was no error then | |||
| LastError = 0; // reset the LastError value. | |||
| } | |||
| Handle = INVALID_SOCKET; // and reset the handle to INVALID. | |||
| NonBlocking = false; // The default is Blocking. | |||
| OpenSucceeded = false; // After close, forget we opened. | |||
| } | |||
| } | |||
| inline hSocket Socket::getHandle() { // Returns the current Socket handle. | |||
| return Handle; | |||
| } | |||
| inline bool Socket::isNonBlocking() { // Returns true if socket is NonBlocking | |||
| return NonBlocking; | |||
| } | |||
| inline void Socket::makeNonBlocking() { // Sets the socket to NonBlocking mode. | |||
| if(0 > Network.setNonBlocking(Handle)) { // Feed the call through Network. | |||
| LastError = Network.getLastError(); // If it didn't work, go get the error. | |||
| NonBlocking = false; // We are NOT NonBlocking. | |||
| throw Networking::ControlError( // Throw a control error. | |||
| Network.DescriptiveError( | |||
| "Socket::makeNonBlocking()", LastError)); | |||
| } else { | |||
| NonBlocking = true; // If we didn't throw, we're ON. | |||
| } | |||
| } | |||
| inline bool Socket::isReuseAddress() { return ReuseAddress; } // True if socket is set SO_REUSEADDR. | |||
| inline bool Socket::isReuseAddress(bool set) { return (ReuseAddress = set); } // Changes SO_REUSEADDR setting. | |||
| inline bool Socket::isOpen() { // True if the socket is open. | |||
| return( | |||
| INVALID_SOCKET != Handle && // A valid handle and | |||
| true == OpenSucceeded // a successful open operation | |||
| ); // means we're open. | |||
| } | |||
| inline int Socket::getLastError() { // Returns the last error for this socket. | |||
| return LastError; | |||
| } | |||
| //// class TCPClient /////////////////////////////////////////////////////////// | |||
| inline TCPClient::TCPClient(TCPListener& L, hSocket H, SocketAddress& A) : // How to create a TCPClient. | |||
| MyListener(L) { // Capture our listener. | |||
| Handle = H; // Capture the new socket handle. | |||
| RemoteAddress = A; // Capture the client address. | |||
| ReadPointer = ReadBuffer; // Set the read position to zero. | |||
| DataLength = 0; // There is no data yet. | |||
| OpenSucceeded = true; // We're getting an open socket. | |||
| } | |||
| inline TCPClient::~TCPClient() { // When destroying a TCPClient | |||
| if(isOpen()) close(); // Close when being destroyed. | |||
| } | |||
| inline void TCPClient::open() { // We provide open() as unsupported. | |||
| throw Networking::NotSupportedError( // Throw an exception if this is called. | |||
| Network.DescriptiveError( | |||
| "TCPClient::open()", LastError)); | |||
| } | |||
| inline bool TCPClient::ReadBufferIsEmpty() { // True if the ReadBuffer is empty. | |||
| return (0 >= DataLength); // We can check that with DataLength. | |||
| } | |||
| inline void TCPClient::fillReadBuffer() { // Fills the buffer from the socket. | |||
| LastError = 0; // Clear the LastError value. | |||
| ReadPointer = ReadBuffer; // Reset the ReadPointer. | |||
| DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), NOFLAGS); // Try to read some data. | |||
| if(0 >= DataLength) { // If there was an error then | |||
| LastError = Network.getLastError(); // Grab the last error code. | |||
| DataLength = 0; // Correct the DataLength. | |||
| if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then | |||
| return; // simply return - it's ok. | |||
| } else { // If it was a different error | |||
| throw Networking::SocketReadError( // then throw a ReadError. | |||
| Network.DescriptiveError( | |||
| "TCPClient::fillReadBuffer()", LastError)); | |||
| } | |||
| } // If we succeeded then our ReadBuffer | |||
| } // assembly is in good shape. | |||
| inline bool TCPClient::isNonBlocking() { // Provided for MessagePort. | |||
| return Socket::isNonBlocking(); | |||
| } | |||
| inline unsigned long TCPClient::getRemoteIP() { // Get remote IP as long. | |||
| return RemoteAddress.getAddress(); | |||
| } | |||
| inline const char* TCPClient::getRemoteIP(char* str) { // Get IP as string. | |||
| return RemoteAddress.getAddress(str); | |||
| } | |||
| inline unsigned short TCPClient::getRemotePort() { // Get remote Port as unsigned short. | |||
| return RemoteAddress.getPort(); | |||
| } | |||
| inline const char* TCPClient::getRemotePort(char* str) { // Get Port as string. | |||
| return RemoteAddress.getPort(str); | |||
| } | |||
| //// class TCPHost ///////////////////////////////////////////////////////////// | |||
| inline TCPHost::~TCPHost() { // When destroying a TCPHost | |||
| if(isOpen()) close(); // Close when being destroyed. | |||
| } | |||
| inline bool TCPHost::ReadBufferIsEmpty() { // True if the ReadBuffer is empty. | |||
| return (0 >= DataLength); // We can check that with DataLength. | |||
| } | |||
| inline void TCPHost::fillReadBuffer() { // Fills the buffer from the socket. | |||
| LastError = 0; // Clear the LastError value. | |||
| ReadPointer = ReadBuffer; // Reset the ReadPointer. | |||
| DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), NOFLAGS); // Try to read some data. | |||
| if(0 >= DataLength) { // If there was an error then | |||
| LastError = Network.getLastError(); // Grab the last error code. | |||
| DataLength = 0; // Correct the DataLength. | |||
| if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then | |||
| return; // simply return - it's ok. | |||
| } else { // If it was a different error | |||
| throw Networking::SocketReadError( // then throw a ReadError. | |||
| Network.DescriptiveError( | |||
| "TCPHost::fillReadBuffer()", LastError)); | |||
| } | |||
| } // If we succeeded then our ReadBuffer | |||
| } // assembly is in good shape. | |||
| inline bool TCPHost::isNonBlocking() { // Provided for MessagePort. | |||
| return Socket::isNonBlocking(); | |||
| } | |||
| //// class TCPListener ///////////////////////////////////////////////////////// | |||
| inline TCPListener::~TCPListener() { // Close when deleting. | |||
| close(); | |||
| } | |||
| @@ -0,0 +1,434 @@ | |||
| // threading.cpp | |||
| // | |||
| // (C) 2006 - 2009 MicroNeil Research Corporation. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 | |||
| // For details on the Threading module and development history see threading.hpp | |||
| #include "threading.hpp" | |||
| using namespace std; // Introduce std namespace. | |||
| ThreadManager Threads; // Master thread manager. | |||
| void ThreadManager::rememberThread(Thread* T) { // Threads register themselves. | |||
| ScopeMutex ThereCanBeOnlyOne(MyMutex); // Protect the known pool. | |||
| KnownThreads.insert(T); // Add the new thread pointer. | |||
| } | |||
| void ThreadManager::forgetThread(Thread* T) { // Threads remove themselves. | |||
| ScopeMutex ThereCanBeOnlyOne(MyMutex); // Protect the known pool. | |||
| KnownThreads.erase(T); // Add the new thread pointer. | |||
| } | |||
| ThreadStatusReport ThreadManager::StatusReport() { // Get a status report, All Threads. | |||
| ScopeMutex ThereCanBeOnlyOne(MyMutex); // Protect our set -- a moment in time. | |||
| ThreadStatusReport Answer; // Create our vector to hold the report. | |||
| for( // Loop through all of the Threads. | |||
| set<Thread*>::iterator iT = KnownThreads.begin(); | |||
| iT != KnownThreads.end(); iT++ | |||
| ) { // Grab each Threads' report. | |||
| Thread& X = *(*iT); // Handy reference to the Thread. | |||
| Answer.push_back(X.StatusReport()); // Push back each Thread's report. | |||
| } | |||
| return Answer; // Return the finished report. | |||
| } | |||
| bool ThreadManager::lockExistingThread(Thread* T) { // Locks ThreadManager if T exists. | |||
| MyMutex.lock(); // Lock the mutex for everyone. | |||
| if(KnownThreads.end() == KnownThreads.find(T)) { // If we do not find T in our set | |||
| MyMutex.unlock(); // then unlock the mutex and return | |||
| return false; // false. | |||
| } // If we did find it then | |||
| LockedThread = T; // set our locked thread and | |||
| return true; // return true; | |||
| } | |||
| // We use assert() in the code below because if these conditions fail then there | |||
| // is something seriously wrong and potentially dangerous with the calling code. | |||
| void ThreadManager::unlockExistingThread(Thread* T) { // Unlocks ThreadManager if T locked. | |||
| assert(0 != LockedThread); // We had better have a locked thread. | |||
| assert(T == LockedThread); // The locked thread had better match. | |||
| LockedThread = 0; // Clear the locked thread. | |||
| MyMutex.unlock(); // Unlock the mutex. | |||
| } | |||
| //// Scope Thread Lock allows for a safe way to lock threads through the Threads | |||
| //// object for delivering short messages. Just like a ScopeMutex, when the object | |||
| //// goes away the lock is released. | |||
| ScopeThreadLock::ScopeThreadLock(Thread* T) : // Construct a scope lock on a Thread. | |||
| MyLockedThread(0) { // To star with we have no lock. | |||
| if(Threads.lockExistingThread(T)) { // If we achieve a lock then we | |||
| MyLockedThread = T; // remember it. Our destructor will | |||
| } // unlock it if we were successful. | |||
| } | |||
| ScopeThreadLock::~ScopeThreadLock() { // Destruct a scope lock on a Thread. | |||
| if(0 != MyLockedThread) { // If we were successfully constructed | |||
| Threads.unlockExistingThread(MyLockedThread); // we can unlock the thread and | |||
| MyLockedThread = 0; // forget about it before we go away. | |||
| } | |||
| } | |||
| bool ScopeThreadLock::isGood() { // If we have successfully locked T | |||
| return (0 != MyLockedThread) ? true:false; // it will NOT be 0, so return true. | |||
| } | |||
| bool ScopeThreadLock::isBad() { // If we did not successfully lock T | |||
| return (0 == MyLockedThread) ? false:true; // it will be 0, so return false. | |||
| } | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Thread | |||
| const ThreadType Thread::Type("Generic Thread"); | |||
| const ThreadState Thread::ThreadInitialized("Thread Initialized"); | |||
| const ThreadState Thread::ThreadStarted("Thread Started"); | |||
| const ThreadState Thread::ThreadFailed("Thread Failed"); | |||
| const ThreadState Thread::ThreadStopped("Thread Stopped"); | |||
| const ThreadState Thread::ThreadDestroyed("Thread Destroyed"); | |||
| bool Thread::isRunning() { return RunningFlag; } // Return RunningFlag state. | |||
| bool Thread::isBad() { return BadFlag; } // Return BadFlag state. | |||
| const string Thread::MyFault() { return BadWhat; } // Return exception Bad fault if any. | |||
| const string Thread::MyName() { return MyThreadName; } // Return the instance name if any. | |||
| const ThreadType& Thread::MyType() { return MyThreadType; } // Return the instance Thread Type. | |||
| const ThreadState& Thread::MyState() { return (*MyThreadState); } // Thread state for this instance. | |||
| void Thread::CurrentThreadState(const ThreadState& TS) { // Set Current Thread State. | |||
| MyThreadState = const_cast<ThreadState*>(&TS); | |||
| } | |||
| const ThreadState& Thread::CurrentThreadState() { return (*MyThreadState); } // Get Current Thread State. | |||
| ThreadStatusRecord Thread::StatusReport() { // Get a status report from this thread. | |||
| return | |||
| ThreadStatusRecord( // Status record. | |||
| this, | |||
| const_cast<ThreadType&>(MyThreadType), | |||
| *MyThreadState, | |||
| RunningFlag, | |||
| BadFlag, | |||
| BadWhat, | |||
| MyThreadName | |||
| ); | |||
| } | |||
| // launchTask() calls and monitors myTask for exceptions and set's the correct | |||
| // states for the isBad and isRunning flags. | |||
| void Thread::launchTask() { // Launch and watch myTask() | |||
| try { // Do this safely. | |||
| RunningFlag = true; // Now we are running. | |||
| CurrentThreadState(ThreadStarted); // Set the running state. | |||
| myTask(); // myTask() is called. | |||
| } // myTask() should handle exceptions. | |||
| catch(exception& e) { // Unhandled exceptions are informative: | |||
| BadFlag = true; // They mean the thread went bad but | |||
| BadWhat = e.what(); // we have an idea what went wrong. | |||
| } // We shouldn't get other kinds of | |||
| catch(...) { // exceptions because if things go | |||
| BadFlag = true; // wrong and one gets through this | |||
| BadWhat = "Unkown Exception(...)"; // is all we can say about it. | |||
| } | |||
| RunningFlag = false; // When we're done, we're done. | |||
| if(BadFlag) CurrentThreadState(ThreadFailed); // If we're bad we failed. | |||
| else CurrentThreadState(ThreadStopped); // If we're not bad we stopped. | |||
| } | |||
| // getMyThread() returns the local thread primative. | |||
| thread_primative Thread::getMyThread() { return MyThread; } // Return my thread primative. | |||
| // runThreadTask() is a helper function to start threads. It is the function | |||
| // that is acutally launched as a new thread. It's whole job is to call the | |||
| // myTask() method on the object passed to it as it is launched. | |||
| // The run() method creates a new thread with ThreadRunner() as the main | |||
| // function, having passed it's object. | |||
| // WIN32 and POSIX have different versions of both the main thread function | |||
| // and the way to launch it. | |||
| #ifdef WIN32 | |||
| Thread::Thread() : // When constructing a WIN32 thread | |||
| MyThreadType(Thread::Type), // Use generic Thread Type. | |||
| MyThreadName("UnNamed Thread"), // Use a generic Thread Name. | |||
| RunningFlag(false), // Couldn't be running yet. | |||
| BadFlag(false), // Couldn't be bad yet. | |||
| MyThread(NULL) { // Null the thread handle. | |||
| Threads.rememberThread(this); // Remember this thread. | |||
| CurrentThreadState(ThreadInitialized); // Set our initialized state. | |||
| } | |||
| Thread::Thread(const ThreadType& T, const string N) : // Construct with specific Type/Name | |||
| MyThreadType(T), // Use generic Thread Type. | |||
| MyThreadName(N), // Use a generic Thread Name. | |||
| RunningFlag(false), // Couldn't be running yet. | |||
| BadFlag(false), // Couldn't be bad yet. | |||
| MyThread(NULL) { // Null the thread handle. | |||
| Threads.rememberThread(this); // Remember this thread. | |||
| CurrentThreadState(ThreadInitialized); // Set our initialized state. | |||
| } | |||
| Thread::~Thread() { // In WIN32 land when we destroy the | |||
| if(NULL != MyThread) { // thread object check for a valid | |||
| CloseHandle(MyThread); // thread handle and destroy it if | |||
| } // it exists. | |||
| RunningFlag = false; // The thread is not running. | |||
| Threads.forgetThread(this); // Forget this thread. | |||
| CurrentThreadState(ThreadDestroyed); // The Thread has left the building. | |||
| } | |||
| unsigned __stdcall runThreadTask(void* thread_object) { // The WIN32 version has this form. | |||
| ((Thread*)thread_object)->launchTask(); | |||
| } | |||
| void Thread::run() { // Run a WIN32 thread... | |||
| unsigned tid; // Thread id to toss. Only need Handle. | |||
| MyThread = (HANDLE) _beginthreadex(NULL,0,runThreadTask,this,0,&tid); // Create a thread calling ThreadRunner | |||
| if(NULL == MyThread) BadFlag = true; // and test that the resutl was valid. | |||
| } | |||
| void Thread::join() { // To join in WIN32 | |||
| WaitForSingleObject(MyThread, INFINITE); // Wait for the thread by handle. | |||
| } | |||
| #else | |||
| Thread::Thread() : // POSIX Thread constructor. | |||
| MyThreadType(Thread::Type), // Use a generic Thread Type. | |||
| MyThreadName("UnNamed Thread"), // Use a generic Thread Name. | |||
| RunningFlag(false), // Can't be running yet. | |||
| BadFlag(false) { // Can't be bad yet. | |||
| Threads.rememberThread(this); // Remember this thread. | |||
| CurrentThreadState(ThreadInitialized); // Set our initialized state. | |||
| } | |||
| Thread::Thread(const ThreadType& T, const string N) : // POSIX Specific Thread Constructor. | |||
| MyThreadType(T), // Use a generic Thread Type. | |||
| MyThreadName(N), // Use a generic Thread Name. | |||
| RunningFlag(false), // Can't be running yet. | |||
| BadFlag(false) { // Can't be bad yet. | |||
| Threads.rememberThread(this); // Remember this thread. | |||
| CurrentThreadState(ThreadInitialized); // Set our initialized state. | |||
| } | |||
| Thread::~Thread() { // POSIX destructor. | |||
| RunningFlag = false; // Not running now for sure. | |||
| Threads.forgetThread(this); // Forget this thread. | |||
| CurrentThreadState(ThreadDestroyed); // The Thread has left the building. | |||
| } | |||
| void* runThreadTask(void* thread_object) { // The POSIX version has this form. | |||
| ((Thread*)thread_object)->launchTask(); | |||
| } | |||
| void Thread::run() { // Run a POSIX thread... | |||
| int result = pthread_create(&MyThread, NULL, runThreadTask, this); // Create a thread calling ThreadRunner | |||
| if(0 != result) BadFlag = true; // and test that there was no error. | |||
| } | |||
| void Thread::join() { // To join in POSIX | |||
| pthread_join(MyThread, NULL); // call pthread_join with MyThread. | |||
| } | |||
| #endif | |||
| // End Thread | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Mutex | |||
| #ifdef WIN32 | |||
| // WIN32 Mutex Implementation ////////////////////////////////////////////////// | |||
| // The original design of the WIN32 Mutex used critical sections. However after | |||
| // additional research it was determined that the use of a Semaphore with an | |||
| // initial count of 1 would work better overall on multiple Winx platforms - | |||
| // especially SMP systems. | |||
| Mutex::Mutex() : // Creating a WIN32 Mutex means | |||
| IAmLocked(false) { // Setting IAmLocked to false and | |||
| MyMutex = CreateSemaphore(NULL, 1, 1, NULL); // create a semaphore object with | |||
| assert(NULL != MyMutex); // a count of 1. | |||
| } | |||
| Mutex::~Mutex() { // Destroying a WIN32 Mutex means | |||
| assert(false == IAmLocked); // Make sure we're not in use and | |||
| CloseHandle(MyMutex); // destroy the semaphore object. | |||
| } | |||
| bool Mutex::tryLock() { // Trying to lock WIN32 Mutex means | |||
| bool DoIHaveIt = false; // Start with a pessimistic assumption | |||
| if( | |||
| false == IAmLocked && // If we have a shot at this and | |||
| WAIT_OBJECT_0 == WaitForSingleObject(MyMutex, 0) // we actually get hold of the semaphore | |||
| ) { // then we can set our flags... | |||
| IAmLocked = true; // Set IAmLocked, because we are and | |||
| DoIHaveIt = true; // set our result to true. | |||
| } | |||
| return DoIHaveIt; // Return true if we got it (see above). | |||
| } | |||
| void Mutex::lock() { // Locking the WIN32 Mutex means | |||
| assert(WAIT_OBJECT_0 == WaitForSingleObject(MyMutex, INFINITE)); // Wait on the semaphore - only 1 will | |||
| IAmLocked = true; // get through or we have a big problem. | |||
| } | |||
| void Mutex::unlock() { // Unlocking the WIN32 Mutex means | |||
| assert(true == IAmLocked); // making sure we're really locked then | |||
| IAmLocked = false; // reset the IAmLocked flag and | |||
| ReleaseSemaphore(MyMutex, 1, NULL); // release the semaphore. | |||
| } | |||
| bool Mutex::isLocked() { return IAmLocked; } // Return the IAmLocked flag. | |||
| #else | |||
| // POSIX Mutex Implementation ////////////////////////////////////////////////// | |||
| Mutex::Mutex() : // Constructing a POSIX mutex means | |||
| IAmLocked(false) { // setting the IAmLocked flag to false and | |||
| assert(0 == pthread_mutex_init(&MyMutex,NULL)); // initializing the mutex_t object. | |||
| } | |||
| Mutex::~Mutex() { // Before we destroy our mutex we check | |||
| assert(false == IAmLocked); // to see that it is not locked and | |||
| assert(0 == pthread_mutex_destroy(&MyMutex)); // destroy the primative. | |||
| } | |||
| void Mutex::lock() { // Locking a POSIX mutex means | |||
| assert(0 == pthread_mutex_lock(&MyMutex)); // asserting our lock was successful and | |||
| IAmLocked = true; // setting the IAmLocked flag. | |||
| } | |||
| void Mutex::unlock() { // Unlocking a POSIX mutex means | |||
| assert(true == IAmLocked); // asserting that we are locked, | |||
| IAmLocked = false; // clearing the IAmLocked flag. | |||
| assert(0 == pthread_mutex_unlock(&MyMutex)); // asserting our unlock was successful and | |||
| } | |||
| bool Mutex::tryLock() { // Trying to lock a POSIX mutex means | |||
| bool DoIHaveIt = false; // starting off pessimistically. | |||
| if(false == IAmLocked) { // If we are not locked yet then we | |||
| if(0 == pthread_mutex_trylock(&MyMutex)) { // try to lock the mutex. If we succeed | |||
| DoIHaveIt = IAmLocked = true; // we set our IAmLocked flag and our | |||
| } // DoIHaveIt flag to true; | |||
| } | |||
| return DoIHaveIt; // In any case we return the result. | |||
| } | |||
| bool Mutex::isLocked() { return IAmLocked; } // Return the IAmLocked flag. | |||
| #endif | |||
| // End Mutex | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // ScopeMutex | |||
| ScopeMutex::ScopeMutex(Mutex& M) : // When constructing a ScopeMutex, | |||
| MyMutex(M) { // Initialize MyMutex with what we are given | |||
| MyMutex.lock(); // and then immediately lock it. | |||
| } | |||
| ScopeMutex::~ScopeMutex() { // When a ScopeMutex is destroyed, | |||
| MyMutex.unlock(); // it first unlocks it's mutex. | |||
| } | |||
| // End ScopeMutex | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Production Gateway | |||
| #ifdef WIN32 | |||
| // Win32 Implementation //////////////////////////////////////////////////////// | |||
| ProductionGateway::ProductionGateway() { // Construct in Windows like this: | |||
| const int HUGENUMBER = 0x7fffffL; // Work without any real limits. | |||
| MySemaphore = CreateSemaphore(NULL, 0, HUGENUMBER, NULL); // Create a Semaphore for signalling. | |||
| assert(NULL != MySemaphore); // That should always work. | |||
| } | |||
| ProductionGateway::~ProductionGateway() { // Be sure to close it when we're done. | |||
| CloseHandle(MySemaphore); | |||
| } | |||
| void ProductionGateway::produce() { // To produce() in WIN32 we | |||
| ReleaseSemaphore(MySemaphore, 1, NULL); // release 1 count into the semaphore. | |||
| } | |||
| void ProductionGateway::consume() { // To consume() in WIN32 we | |||
| WaitForSingleObject(MySemaphore, INFINITE); // wait for a count in the semaphore. | |||
| } | |||
| #else | |||
| // POSIX Implementation //////////////////////////////////////////////////////// | |||
| ProductionGateway::ProductionGateway() : // Construct in POSIX like this: | |||
| Product(0), // All of our counts start at zero. | |||
| Waiting(0), | |||
| Signaled(0) { | |||
| assert(0 == pthread_mutex_init(&MyMutex, NULL)); // Initialize our mutex. | |||
| assert(0 == pthread_cond_init(&MyConditionVariable, NULL)); // Initialize our condition variable. | |||
| } | |||
| ProductionGateway::~ProductionGateway() { // When we're done we must destroy | |||
| assert(0 == pthread_mutex_destroy(&MyMutex)); // our local mutex and | |||
| assert(0 == pthread_cond_destroy(&MyConditionVariable)); // our condition variable. | |||
| } | |||
| void ProductionGateway::produce() { // To produce in POSIX | |||
| assert(0 == pthread_mutex_lock(&MyMutex)); // Lock our mutex. | |||
| ++Product; // Add an item to our product count. | |||
| if(Signaled < Waiting) { // If anybody is waiting that has not | |||
| assert(0 == pthread_cond_signal(&MyConditionVariable)); // yet been signaled then signal them | |||
| ++Signaled; // and keep track. They will count this | |||
| } // down as they awaken. | |||
| assert(0 == pthread_mutex_unlock(&MyMutex)); // At the end unlock our mutex so | |||
| } // waiting threads can fly free :-) | |||
| void ProductionGateway::consume() { // To consume in POSIX | |||
| assert(0 == pthread_mutex_lock(&MyMutex)); // Lock our mutex. | |||
| while(0 >= Product) { // Until we have something to consume, | |||
| ++Waiting; // wait for a signal from | |||
| assert(0 == pthread_cond_wait(&MyConditionVariable, &MyMutex)); // our producer. When we have a signal | |||
| --Waiting; // we are done waiting and we have | |||
| --Signaled; // been signaled. Of course, somebody | |||
| } // may have beaten us to it so check. | |||
| --Product; // If we have product then take it. | |||
| assert(0 == pthread_mutex_unlock(&MyMutex)); // At the end unlock our mutex so | |||
| } | |||
| #endif | |||
| // End Production Gateway | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| @@ -0,0 +1,440 @@ | |||
| // threading.hpp | |||
| // | |||
| // (C) 2006 - 2009 MicroNeil Research Corporation. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 | |||
| // The "Threading" module is a basic, cross-platform, multi-threading tool kit. | |||
| // The differences between posix compatible systems and win32 based systems are | |||
| // abstracted. On win32 systems, native win32 primatives are used to construct. | |||
| // efficient, lightweight objects. | |||
| // On others we assume we can use pthreads. In either case the objects we have | |||
| // here are designed to cover all of the basics efficiently while hiding the | |||
| // required under-cover work. | |||
| // A lot of this module is coded here in the header with the inline keyword | |||
| // because it is likely that the more basic objects can be efficiently compiled | |||
| // as inline abstractions to native calls. Really basic systems won't need | |||
| // anything beyond what is in this file. | |||
| // 20070202.1601 _M Further research has suggested that using a Semaphore in | |||
| // WIN32 environments in place of a CRITICAL_SECTION may provide the best | |||
| // performance and stability on all platforms. Specifically, SMP platforms may | |||
| // race and waste resources with CRITICAL_SECTIONs and in those cases it is | |||
| // recommended that the CRITICAL_SECTIONs may be "throttled" using Semaphores | |||
| // to limit the number of threads that may contend for a critical section. It | |||
| // is also suggested that if the Semaphore has an initialization value of 1 | |||
| // the CRITICAL_SECTION is redundant. So this code has been modified to do | |||
| // precisely that! | |||
| // | |||
| // This new version also includes a ProductionGateway object that simplifies | |||
| // the producer/consumer model. The object keeps track of the number of calls | |||
| // to produce() and consume() and ensures that threads will block on consume() | |||
| // until a sufficient number of calls to produce() are made. That is, for every | |||
| // one call to produce(), a call to consume() will be allowed to proceed. The | |||
| // object also allows for the potentially asynchronous nature of these calls. | |||
| // 20070530.1751 _M Added top level exception handling in threads along with | |||
| // isRunning() and isBad() methods. | |||
| // 20060528.1647 _M All of the basics are complete and tested on both WIN32 and | |||
| // RHEL4 single and multiple processors. | |||
| // Include MNR_threading Once Only ============================================= | |||
| #ifndef MNR_threading | |||
| #define MNR_threading | |||
| #include <cassert> | |||
| #include <set> | |||
| #include <vector> | |||
| #include <string> | |||
| using namespace std; | |||
| class ThreadManager; // ThreadManager does exist. | |||
| extern ThreadManager Threads; // Master thread manager. | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Thread Status & Type | |||
| // | |||
| // ThreadState objects are constant static objects defined for each Thread class | |||
| // so that the thread can update it's state by changing a pointer. The state | |||
| // can then be compared between threads of the same type and can be read-out | |||
| // as text for debugging purposes. | |||
| class ThreadState { // Thread State Object. | |||
| public: | |||
| const string Name; // Text name of thread descriptor. | |||
| ThreadState(string N) : Name(N) {} // Constructor requires text name. | |||
| }; | |||
| // ThreadType objects are constant static objects defined for each Thread class | |||
| // so that classes can be identified by type using a pointer to the constant. | |||
| class ThreadType { | |||
| public: | |||
| const string Name; | |||
| ThreadType(string N) : Name(N) {} | |||
| }; | |||
| class Thread; // There is such thing as a Thread. | |||
| class ThreadStatusRecord { // Describes a Thread's condition. | |||
| private: | |||
| Thread* Pointer; // A pointer to the thread. | |||
| ThreadType* Type; // A descriptor of it's type. | |||
| ThreadState* State; // A descriptor of it's state. | |||
| string Name; // Name of the thread if any. | |||
| bool isRunning; // True if the thread is running. | |||
| bool isBad; // True if the thread is bad. | |||
| string Fault; // Bad Thread's Fault if any. | |||
| public: | |||
| ThreadStatusRecord( // Initialize all items. | |||
| Thread* P, | |||
| ThreadType& T, | |||
| ThreadState& S, | |||
| bool R, | |||
| bool B, | |||
| string F, | |||
| string N | |||
| ) : | |||
| Pointer(P), | |||
| Type(&T), | |||
| State(&S), | |||
| isRunning(R), | |||
| isBad(B), | |||
| Fault(F), | |||
| Name(N) | |||
| {} | |||
| ThreadStatusRecord& operator=(const ThreadStatusRecord& Right) { // Minimal Assignment Operator | |||
| Pointer = Right.Pointer; | |||
| Type = Right.Type; | |||
| State = Right.State; | |||
| isRunning = Right.isRunning; | |||
| isBad = Right.isBad; | |||
| Fault = Right.Fault; | |||
| Name = Right.Name; | |||
| } | |||
| bool operator<(const ThreadStatusRecord& Right) { // Minimal Comparison Operator. | |||
| return (Pointer < Right.Pointer); | |||
| } | |||
| // How to get the details of the report. | |||
| const Thread* getPointer() { return Pointer; } | |||
| const ThreadType& getType() { return *Type; } | |||
| const ThreadState& getState() { return *State; } | |||
| bool getRunning() { return isRunning; } | |||
| bool getBad() { return isBad; } | |||
| string getFault() { return Fault; } | |||
| string getName() { return Name; } | |||
| }; | |||
| typedef vector<ThreadStatusRecord> ThreadStatusReport; // Status report type. | |||
| // End ThreadDescriptor | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Win32 / POSIX abstractions | |||
| #ifdef WIN32 | |||
| // When in WIN32 land... | |||
| // Remember to compile (on GNU anyway) with -mthreads | |||
| #include <windows.h> | |||
| #include <process.h> | |||
| typedef HANDLE thread_primative; // The WIN32 thread primative abstracts | |||
| // HANDLE | |||
| typedef HANDLE mutex_primative; // The WIN32 mutex primative abstracts | |||
| // a HANDLE to a Semaphore. | |||
| inline void threading_yield() { // When we want to yield time in WIN32 | |||
| SwitchToThread(); // we call SwitchToThread(); | |||
| } | |||
| #else | |||
| // When in POSIX land... | |||
| // Remember to compile (on GMU anyway) with -pthread | |||
| #include <pthread.h> | |||
| #include <sched.h> | |||
| typedef pthread_t thread_primative; // The POSIX thread primative abstracts | |||
| // pthread_t | |||
| typedef pthread_mutex_t mutex_primative; // The POSIX mutex primative abstracts | |||
| // pthread_mutex_t | |||
| inline void threading_yield() { // When we want to yield time in POSIX | |||
| sched_yield(); // we call sched_yield(); | |||
| } | |||
| #endif | |||
| // End Win32 / POSIX abstractions | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // The Thread class gets extended to do any specific work. The pure virtual | |||
| // function MyTask is overloaded by the derived class to define that work. It | |||
| // is expected that the class will be initialized with any parameters that | |||
| // will be used by the thread and that the thread will make available any | |||
| // results through public interfaces either during and/or after the thread | |||
| // has finished running. | |||
| class Thread { | |||
| private: | |||
| ThreadState* MyThreadState; // Track current thread state. | |||
| protected: | |||
| const ThreadType& MyThreadType; // Identify thread type. | |||
| const string MyThreadName; // Name string of this instance. | |||
| thread_primative MyThread; // Abstracted thread. | |||
| bool RunningFlag; // True when thread is in myTask() | |||
| bool BadFlag; // True when myTask() throws! | |||
| string BadWhat; // Bad exception what() if any. | |||
| void CurrentThreadState(const ThreadState& TS); // Set thread state. | |||
| public: | |||
| Thread(); // Constructor (just in case) | |||
| Thread(const ThreadType& T, string N); // Construct with specific Type/Name | |||
| ~Thread(); // Destructor (just in case) | |||
| void run(); // Method to launch this thread. | |||
| void join(); // Method to Join this thread. | |||
| void launchTask(); // Launch and watch myTask(). | |||
| virtual void myTask() = 0; // The actual task must be overloaded. | |||
| thread_primative getMyThread(); // Inspect my thread primative. | |||
| bool isRunning(); // Return the Running flag state. | |||
| bool isBad(); // Return the Bad flag state. | |||
| const string MyFault(); // Return exception Bad fault if any. | |||
| const string MyName(); // The thread's name. | |||
| const ThreadType& MyType(); // Thread type for this thread. | |||
| const ThreadState& MyState(); // Returns the current thread state. | |||
| const ThreadState& CurrentThreadState(); // Returns the current thread state. | |||
| ThreadStatusRecord StatusReport(); // Return's the thread's status reprt. | |||
| // Constants for Thread... | |||
| const static ThreadType Type; // The thread's type. | |||
| const static ThreadState ThreadInitialized; // Constructed successfully. | |||
| const static ThreadState ThreadStarted; // Started. | |||
| const static ThreadState ThreadFailed; // Failed by unhandled exception. | |||
| const static ThreadState ThreadStopped; // Stopped normally. | |||
| const static ThreadState ThreadDestroyed; // Safety value for destructed Threads. | |||
| }; | |||
| // End Thread | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // The Mutex class abstracts a lightweight, very basic mutex object. | |||
| // As with the Thread object, more ellaborate forms can be built up from | |||
| // this basic mechanism. An important design constraint for this basic | |||
| // mutex object is that it work even if the thread that's running was not | |||
| // created with the Thread object... that ensures that it can be used in | |||
| // code that is destined to function in other applications. | |||
| class Mutex { | |||
| private: | |||
| mutex_primative MyMutex; // Here is our primative mutex. | |||
| volatile bool IAmLocked; // Here is our Lock Count. | |||
| public: | |||
| Mutex(); // Construct the mutex. | |||
| ~Mutex(); // Destroy the mutex. | |||
| void lock(); // Lock it. | |||
| void unlock(); // Unlock it. | |||
| bool tryLock(); // Try to lock it. | |||
| bool isLocked(); // Check to see if it's locked. | |||
| }; | |||
| // End of Mutex | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // ScopeMutex | |||
| // A ScopeMutex is a nifty trick for locking a mutex during some segment of | |||
| // code. On construction, it locks the Mutex that it is given and keeps it | |||
| // locked until it is destroyed. Of course this also means that it will unlock | |||
| // the mutex when it goes out of scope - which is precisely the point :-) | |||
| // | |||
| // The right way to use a ScopeMutex is to create it just before you need to | |||
| // have control and then forget about it. From a design perspective, you might | |||
| // want to make sure that whatever happens after the ScopeMutex has been | |||
| // created is as short as possible and if it is not then you may want to | |||
| // use the Mutex directly. | |||
| // | |||
| // The best place to use a ScopeMutex is where you might leave the controling | |||
| // bit of code through a number of logical paths such as a logic tree or even | |||
| // due to some exceptions. In this context it saves you having to track down | |||
| // all of the possible cases and unlock the mutex in each of them. | |||
| class ScopeMutex { | |||
| private: | |||
| Mutex& MyMutex; // ScopeMutex has an ordinary Mutex to use. | |||
| public: | |||
| ScopeMutex(Mutex& M); // Constructing a ScopeMutex requires a Mutex | |||
| ~ScopeMutex(); // We do have special code for descrution. | |||
| }; | |||
| // End ScopeMutex | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // ProductionGateway | |||
| // A ProductionGateway encapsulates the thread synchronization required for a | |||
| // producer / consumer relationship. For each call to the produce() method one | |||
| // call to the consume() method can proceed. The object takes into account that | |||
| // these methods may be called out of sequence and that, for example, produce() | |||
| // might be called several times before any calls to consume. | |||
| #ifdef WIN32 | |||
| // Win32 Implementation //////////////////////////////////////////////////////// | |||
| class ProductionGateway { | |||
| private: | |||
| HANDLE MySemaphore; // WIN32 makes this one easy w/ a 0 semi. | |||
| public: | |||
| ProductionGateway(); // The constructor and destructor handle | |||
| ~ProductionGateway(); // creating and destroying the semi. | |||
| void produce(); // Produce "releases" the semi. | |||
| void consume(); // Consume "waits" if needed. | |||
| }; | |||
| #else | |||
| // POSIX Implementation //////////////////////////////////////////////////////// | |||
| class ProductionGateway { // Posix needs a few pieces for this. | |||
| private: | |||
| mutex_primative MyMutex; // Mutex to protect the data. | |||
| pthread_cond_t MyConditionVariable; // A condition variable for signaling. | |||
| int Product; // A count of unused calls to produce() | |||
| int Waiting; // A count of waiting threads. | |||
| int Signaled; // A count of signaled threads. | |||
| public: | |||
| ProductionGateway(); // The constructor and destructor handle | |||
| ~ProductionGateway(); // creating and destroying the semi. | |||
| void produce(); // Produce "releases" the semi. | |||
| void consume(); // Consume "waits" if needed. | |||
| }; | |||
| #endif | |||
| // End ProductionGateway | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // The ThreadManager class provides a global thread management tool. All Thread | |||
| // objects register themselves with the Threads object upon construction and | |||
| // remove themselves from the registry upon destruction. The Threads object can | |||
| // produce a status report for all of the known threads on the system and can | |||
| // temporarily lock the existing thread so that it can be contacted reliably. | |||
| // locking and unlocking the ThreadManager is intended only for short messages | |||
| // that set flags in the thread or pass some small data packet. The lock only | |||
| // prevents the thread from being destroyed before the message can be sent so | |||
| // that the thread that owns the threadlock will not make any calls to a dead | |||
| // pointer. Most apps should be designed so that the threadlock mechanism is | |||
| // not required. | |||
| class ThreadManager { // Central manager for threads. | |||
| friend class Thread; // Threads are friends. | |||
| private: | |||
| Mutex MyMutex; // Protect our data with this. | |||
| set<Thread*> KnownThreads; // Keep track of all threads. | |||
| void rememberThread(Thread* T); // Threads register themselves. | |||
| void forgetThread(Thread* T); // Threads remove themselves. | |||
| Thread* LockedThread; // Pointer to locked thread if any. | |||
| public: | |||
| ThreadManager():LockedThread(0){} // Initialize nice and clean. | |||
| ThreadStatusReport StatusReport(); // Get a status report. | |||
| bool lockExistingThread(Thread* T); // Locks ThreadManager if T exists. | |||
| void unlockExistingThread(Thread* T); // Unlocks ThreadManager if T locked. | |||
| }; | |||
| class ScopeThreadLock { // This is like a ScopeMutex for | |||
| private: // the ThreadManager. | |||
| Thread* MyLockedThread; // It needs to know it's Thread. | |||
| public: | |||
| ScopeThreadLock(Thread* T); // Locks T in ThreadManager if it can. | |||
| ~ScopeThreadLock(); // Unlocks T in ThreadManager if locked. | |||
| bool isGood(); // True if T was locked. | |||
| bool isBad(); // False if T was not locked. | |||
| }; | |||
| // End Thread Manager | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| #endif | |||
| // End Of Include MNR_threading Once Only ====================================== | |||
| @@ -0,0 +1,325 @@ | |||
| // timing.cpp | |||
| // | |||
| // Copyright (C) 2006 - 2009 MicroNeil Research Corporation. | |||
| // | |||
| // See the corresponding .hpp file for descriptions and history. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 <ctime> | |||
| #include <sys/time.h> | |||
| #include <cerrno> | |||
| // Platform Specific Includes ////////////////////////////////////////////////// | |||
| #ifdef WIN32 | |||
| #include <windows.h> | |||
| #endif | |||
| #include "timing.hpp" | |||
| // Introduce the standard namespace //////////////////////////////////////////// | |||
| using namespace std; | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // class Sleeper - An object that remembers how long it is supposed to sleep. | |||
| // This allows an application to create "standard" sleep timers. This also | |||
| // helps keep sleeper values within range to avoid weird timing problems. | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // Abstracted doRawSleep() function //////////////////////////////////////////// | |||
| #ifdef WIN32 | |||
| // In a WIN32 environment Sleep() is defined and it works in milliseconds so | |||
| // we will use that for doRawSleep(). It's important to note that under normal | |||
| // circumstances win32 Sleep() may be off by quite a bit (15ms or so) due to | |||
| // how timing is done in the OS. There are ways around this, but they are | |||
| // sometimes complex - so here I've left things basic. If more precise win32 | |||
| // timing is needed then this method can be recoded using a workaround that is | |||
| // appropriate to the application. | |||
| void Sleeper::doRawSleep(int x) { | |||
| Sleep(x); // Use windows Sleep() | |||
| } | |||
| #else | |||
| // If we are not in a win32 environment then we're likely on a posix/unix system | |||
| // or at least we have the standard posix/unix time functions so we'll redefine | |||
| // absSleep to use nanosleep(); | |||
| void Sleeper::doRawSleep(int x) { | |||
| struct timespec sleeptime; // How much sleeping to do. | |||
| struct timespec remaining; // How much sleeping remains. | |||
| int result; // The latest result. | |||
| remaining.tv_sec = x/1000; // Divide ms by 1000 to get secs. | |||
| remaining.tv_nsec = (x%1000)*1000000; // Multiply the remaining msecs to get nsecs. | |||
| do { // Just in case we get interruped... | |||
| sleeptime.tv_sec = remaining.tv_sec; // Get our sleep time from the | |||
| sleeptime.tv_nsec = remaining.tv_nsec; // remaining time. | |||
| result = nanosleep(&sleeptime,&remaining); // Call nanosleep and get the remaining time. | |||
| } while(0>result && EINTR==errno); // If we were interrupted sleep some more. | |||
| } | |||
| #endif | |||
| Sleeper::Sleeper() // Constructed empty we set our | |||
| :MillisecondsToSleep(0) { // sleep time to zero. | |||
| } | |||
| Sleeper::Sleeper(int x) { // Constructed with a value we | |||
| setMillisecondsToSleep(x); // set the sleep time or throw. | |||
| } | |||
| int Sleeper::setMillisecondsToSleep(int x) { // Safe way to set the vlaue. | |||
| if(x < MinimumSleeperTime || | |||
| x > MaximumSleeperTime) // If it's not a good time value | |||
| throw BadSleeperValue(); // then throw the exception. | |||
| MillisecondsToSleep = x; // If it is good - set it. | |||
| } | |||
| int Sleeper::getMillisecondsToSleep() { // Safe way to get the value. | |||
| return MillisecondsToSleep; // Send back the value. | |||
| } | |||
| void Sleeper::sleep() { // Here's where we snooze. | |||
| if(MillisecondsToSleep > 0) { // If we have a good snooze | |||
| doRawSleep(MillisecondsToSleep); // value then go to Sleep(). | |||
| } else { // If the value is not good | |||
| throw BadSleeperValue(); // throw an exception. | |||
| } | |||
| } | |||
| void Sleeper::sleep(int x) { // Reset the sleep time then sleep. | |||
| setMillisecondsToSleep(x); // Set the sleep time. | |||
| sleep(); // Sleep. | |||
| } | |||
| void Sleeper::operator()() { // Syntactic sugar - operator() on | |||
| sleep(); // a sleeper calls sleep(). | |||
| } | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // class PollTimer - An object to pause during polling processes where the | |||
| // time between polls is expanded according to a Fibonacci sequence. This | |||
| // allows self organizing automata to relax a bit when a particular process | |||
| // is taking a long time so that the resources used in the polling process are | |||
| // reduced if the system is under load - The idea is to prevent the polling | |||
| // process from loading the system when there are many nodes poling, yet to | |||
| // allow for a rapid response when there are few or when the answer we're | |||
| // waiting for is ready quickly. We use a Fibonacci expansion because it is | |||
| // a natural spiral. | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| PollTimer::PollTimer(int Nom, int Max) { // Construction requires a | |||
| setNominalPollTime(Nom); // nominal delay to use and | |||
| setMaximumPollTime(Max); // a maximum delay to allow. | |||
| } | |||
| int PollTimer::setNominalPollTime(int Nom) { // Set the Nominal Poll Time. | |||
| if(Nom < MinimumSleeperTime || // Check the low and high | |||
| Nom > MaximumSleeperTime) // limits and throw an | |||
| throw BadPollTimerValue(); // exception if we need to. | |||
| // If the value is good then | |||
| NominalPollTime = Nom; // remember it. | |||
| if(MaximumPollTime < NominalPollTime) // Make sure the Maximum poll | |||
| MaximumPollTime = NominalPollTime; // time is > the Nominal time. | |||
| reset(); // Reset due to the change. | |||
| return NominalPollTime; // Return the new value. | |||
| } | |||
| int PollTimer::setMaximumPollTime(int Max) { // Set the Maximum Poll Time. | |||
| if(Max < MinimumSleeperTime || // Check the low and high | |||
| Max > MaximumSleeperTime) // limits and throw an | |||
| throw BadPollTimerValue(); // exception if we need to. | |||
| // If the value is good then | |||
| MaximumPollTime = Max; // remember it. | |||
| if(MaximumPollTime < NominalPollTime) // Make sure the Maximum poll | |||
| MaximumPollTime = NominalPollTime; // time is >= the Nominal time. | |||
| reset(); // Reset due to the change. | |||
| return MaximumPollTime; // Return the new value. | |||
| } | |||
| void PollTimer::reset() { // Reset the spiral. | |||
| FibA = NominalPollTime; // Assume our starting event. | |||
| FibB = 0; // Assume no other events. | |||
| LimitReached=false; // Reset our limit watcher. | |||
| } | |||
| int PollTimer::pause() { // Pause between polls. | |||
| int SleepThisTime = MaximumPollTime; // Assume we're at out limit for now. | |||
| if(LimitReached) { // If actually are at our limit then | |||
| mySleeper.sleep(SleepThisTime); // use the current value. | |||
| } else { // If we are still expanding then | |||
| SleepThisTime = FibA+FibB; // Calculate the time to use and | |||
| if(SleepThisTime >= MaximumPollTime) { // check it against the limit. If | |||
| SleepThisTime = MaximumPollTime; // we reached the limit, us that value | |||
| LimitReached = true; // and set the flag. | |||
| } else { // If we haven't reached the limit yet | |||
| FibB=FibA; // then shift our events and remember | |||
| FibA=SleepThisTime; // this one to build our spiral. | |||
| } | |||
| mySleeper.sleep(SleepThisTime); // Take a nap. | |||
| } // Then FIRE THE MISSILES! | |||
| return SleepThisTime; // Tell the caller how long we slept. | |||
| } | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // class Timer - This one acts much like a stop watch with millisecond | |||
| // resolution. The time is based on wall-clock time using gettimeofday(). | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| #ifdef WIN32 | |||
| // Here is the win32 version of getLocalRawClock() | |||
| #define TimerIsUnixBased (false) | |||
| msclock Timer::getLocalRawClock() const { | |||
| FILETIME t; // We need a FILETIME structure. | |||
| msclock c; // We need a place to calculate our value. | |||
| GetSystemTimeAsFileTime(&t); // Grab the system time. | |||
| c = (unsigned long long int) t.dwHighDateTime << 32LL; // Put full seconds into the high order bits. | |||
| c |= t.dwLowDateTime; // Put 100ns ticks into the low order bits. | |||
| c /= 10000; // Divide 100ns ticks by 10K to get ms. | |||
| c -= EPOCH_DELTA_IN_MSEC; // Correct for the epoch difference. | |||
| return c; // Return the result. | |||
| } | |||
| #else | |||
| // Here is the unix/posix version of getLocalRawClock() | |||
| #define TimerIsUnixBased (true) | |||
| msclock Timer::getLocalRawClock() const { | |||
| struct timeval t; // We need a timval structure. | |||
| msclock c; // We need a place to calculate our value. | |||
| gettimeofday(&t,NULL); // Grab the system time. | |||
| c = t.tv_sec * 1000; // Put the full seconds in as milliseconds. | |||
| c += t.tv_usec / 1000; // Add the microseconds as milliseconds. | |||
| return c; // Return the milliseconds. | |||
| } | |||
| #endif | |||
| Timer::Timer() { // Construct by resetting the | |||
| start(); // clocks by using start(); | |||
| } | |||
| Timer::Timer(msclock startt): // Construct a timer from a specific time. | |||
| RunningFlag(true), // Set the running flag, | |||
| StartTime(startt), // the start time and | |||
| StopTime(startt) { // the stop time clock to startt. | |||
| } | |||
| void Timer::clear() { // Stop, zero elapsed, now. | |||
| StartTime = StopTime = getLocalRawClock(); // Set the start and stop time | |||
| RunningFlag = false; // to now. We are NOT running. | |||
| } | |||
| msclock Timer::start() { // (re) Start the timer at this moment. | |||
| return start(getLocalRawClock()); // start() using the current raw clock. | |||
| } | |||
| msclock Timer::start(msclock startt) { // (re) Start a timer at startt. | |||
| StartTime = StopTime = startt; // Set the start and end clocks. | |||
| RunningFlag = true; // Set the running flag to true. | |||
| return StartTime; // Return the start clock. | |||
| } | |||
| msclock Timer::getStartClock() { return StartTime; } // Return the start clock value. | |||
| bool Timer::isRunning() { return RunningFlag; } // Return the running state. | |||
| msclock Timer::getElapsedTime() const { // Return the elapsed timeofday - | |||
| msclock AssumedStopTime; // We need to use a StopTime simulation. | |||
| if(RunningFlag) { // If we are running we must get | |||
| AssumedStopTime = getLocalRawClock(); // the current time (as if it were stop). | |||
| } else { // If we are not running we use | |||
| AssumedStopTime = StopTime; // the actual stop time. | |||
| } | |||
| msclock delta = AssumedStopTime - StartTime; // Calculate the difference. | |||
| return delta; // That's our result. | |||
| } | |||
| msclock Timer::stop() { // Stop the timer. | |||
| StopTime = getLocalRawClock(); // Grab the time and then stop | |||
| RunningFlag=false; // the clock. | |||
| return StopTime; // Return the time we stopped. | |||
| } | |||
| msclock Timer::getStopClock() { return StopTime; } // Return the stop clock value. | |||
| double Timer::getElapsedSeconds() const { // Calculate the elapsed seconds. | |||
| msclock e = getElapsedTime(); // Get the elapsed time in msecs. | |||
| double secs = (double) e / 1000.0; // Calculate seconds from msecs. | |||
| return secs; | |||
| } | |||
| bool Timer::isUnixBased() { return TimerIsUnixBased; } // Is this timer unix based? | |||
| msclock Timer::toWindowsEpoch(msclock unixt) { // Convert a unix based msclock to win32 based. | |||
| return (unixt + EPOCH_DELTA_IN_MSEC); // Going this way we add the epoch delta. | |||
| } | |||
| msclock Timer::toUnixEpoch(msclock win32t) { // Convert a win32 based msclock to a unix based. | |||
| return (win32t - EPOCH_DELTA_IN_MSEC); // Going this way we subtract the epoch delta. | |||
| } | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // class Timeout - This one uses a Timer to establish a timeout value. | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| Timeout::Timeout(msclock duration):myDuration(duration) { } // Create, set the duration, start. | |||
| msclock Timeout::setDuration(msclock duration) { // Set/Change the duration in milliseconds. | |||
| myDuration = duration; // (re) Set the duration. | |||
| return myDuration; // Return the current (new) duration. | |||
| } | |||
| msclock Timeout::getDuration() { // Return the current duration. | |||
| return myDuration; | |||
| } | |||
| msclock Timeout::restart() { // Restart the timeout timer. | |||
| return myTimer.start(); // Restart the clock and return the time. | |||
| } | |||
| msclock Timeout::getElapsedTime() { // Get elapsed milliseconds. | |||
| return myTimer.getElapsedTime(); // Return the elapsed time. | |||
| } | |||
| msclock Timeout::getRemainingTime() { // Get remaining milliseconds. | |||
| msclock remaining = 0ULL; // Assume we're expired to start. | |||
| msclock elapsed = myTimer.getElapsedTime(); // Get the elapsed time. | |||
| if(elapsed < myDuration) { // If there is still time then | |||
| remaining = myDuration - elapsed; // calculate what is left. | |||
| } | |||
| return remaining; // Return what we found. | |||
| } | |||
| bool Timeout::isExpired() { // Return true if time is up. | |||
| return (!(myTimer.getElapsedTime() < myDuration)); // Check the elapsed time against myDuration. | |||
| } | |||
| @@ -0,0 +1,360 @@ | |||
| // timing.hpp | |||
| // | |||
| // Copyright (C) 2004-2009 MicroNeil Research Corporation. | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // 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 | |||
| // The purpose of this module is to abstract timing functions for | |||
| // cross platform C++ development usning GNU compilers in *nix and | |||
| // win32 environments (minGW). Timing resolution is in milliseconds | |||
| // throughout to provide consistency and reasonable expectations. | |||
| // 20060404 _M Added Timer::start(msclock startt) for chaining. | |||
| // 20060403 _M This "timing" module has been completed and tested on | |||
| // win32 (compiled using CodeBlocks and minGW) and on RHES3 (g++). | |||
| // | |||
| // The bottom line is that this code is perfect for most applications that | |||
| // don't need real-time interaction on the win32 platform. That is, for | |||
| // any application that can accept 15ms or so of "wiggle" in their timing | |||
| // functions. On linux I was able to observe very consistent results with | |||
| // variations measured in 1-2ms. | |||
| // | |||
| // Aynone seeking real-time accuracy on the win32 platform will need to contend | |||
| // with all of the landmines in place against that and will need to write more | |||
| // ellaborate versions of Timer::getLocalRawClock() and Sleeper::doRawSleep() | |||
| // aa appropriate for their application. The existing code should work fine for | |||
| // almost all other applications. | |||
| // | |||
| // This code was written with that in mind to some extent. That is why all of | |||
| // the timing functions are measured in milliseconds rather than microseconds | |||
| // or something smaller. Milliseconds are convenient for polling delays, | |||
| // communications timeouts, measuring database application performance, and | |||
| // other similar tasks. For that purpose - this timing module is just fine :-) | |||
| // 20060323 _M Rewrote this module from a combination of previous | |||
| // bits and pieces. This module will provide classes that abstract | |||
| // timing functions for use in GNU projects on *nix and win32 systems. | |||
| #ifndef MNR_timing | |||
| #define MNR_timing | |||
| // Introduce the standard namespace /////////////////////////////////////////// | |||
| using namespace std; | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // class Sleeper - An object that remembers how long it is supposed to sleep. | |||
| // This allows an application to create "standard" sleep timers that can be | |||
| // established at the top of the code (easy to find) and reused. | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| static const int MinimumSleeperTime = 1; // Minimum msec allowed. | |||
| static const int MaximumSleeperTime = 2000000000; // Maximum msec allowed. | |||
| class Sleeper { | |||
| private: | |||
| int MillisecondsToSleep; // How long to sleep. | |||
| void doRawSleep(int x); // Abstracted local sleep function. | |||
| public: | |||
| class BadSleeperValue {}; // Exception for bad values. | |||
| Sleeper(); // Constructed empty - set to zero. | |||
| Sleeper(int x); // Constructed with a value. | |||
| int setMillisecondsToSleep(int x); // Safe way to set the vlaue. | |||
| int getMillisecondsToSleep(); // Safe way to get the value. | |||
| void sleep(); // Here's where we snooze if we can. | |||
| void sleep(int x); // Shortcut - set the time and then sleep. | |||
| void operator()(); | |||
| }; | |||
| /* Sleeper Documentation... | |||
| ** | |||
| ** Sleeper.Sleeper() | |||
| ** Constructs a Sleeper with a zero value. | |||
| ** | |||
| ** Sleeper.Sleeper(int x) | |||
| ** Constructs a Sleeper to "snooze" for x milliseconds. | |||
| ** | |||
| ** Sleeper.setMillisecondsToSleep(int x) | |||
| ** Sets the sleep time for the Sleeper and returns the time set. | |||
| ** If the value is out of range then the Sleeper::BadSleeperValue will be thrown. | |||
| ** | |||
| ** Sleeper.getMillisecondsToSleep() | |||
| ** Returns the current MillisecondsToSleep. | |||
| ** | |||
| ** Sleeper.sleep() | |||
| ** Goes to sleep for MillisecondsToSleep. If MillisecondsToSleep has not been set | |||
| ** then the function throws Sleeper::BadSleeperVlaue. | |||
| ** | |||
| ** Sleeper.sleep(int x) | |||
| ** First sets MillisecondsToSleep, then goes to sleep. If x is too big or too small | |||
| ** then the method throws Sleeper::BadSleeperValue. | |||
| */ | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // class PollTimer - An object to pause during polling processes where the | |||
| // time between polls is expanded according to a Fibonacci sequence. This | |||
| // allows self organizing automata to relax a bit when a particular process | |||
| // is taking a long time so that the resources used in the polling process are | |||
| // reduced if the system is under load - The idea is to prevent the polling | |||
| // process from loading the system when there are many nodes poling, yet to | |||
| // allow for a rapid response when there are few or when the answer we're | |||
| // waiting for is ready quickly. We use a Fibonacci expansion because it is | |||
| // a natural spiral. | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| class PollTimer { | |||
| private: | |||
| Sleeper mySleeper; // We need a sleeper to do this. | |||
| int NominalPollTime; // Normal poll delay in msec. | |||
| int MaximumPollTime; // Maximum poll delay in msec. | |||
| bool LimitReached; | |||
| // Why not use unsigned int everywhere? Because sometimes libraries use | |||
| // int for their Sleep() functions... so we calculate with unsigned ints, | |||
| // but we use ints for inputs to keep things sane. Wierd bugs show up if | |||
| // signed ints overflow in clock_t values -- this learned by experience. | |||
| unsigned int FibA; // One poll delay ago. | |||
| unsigned int FibB; // Two poll delays ago. | |||
| // FibA and FibB are used to generate the fibonacci expansion. The current | |||
| // delay will always be the sum of the previous two delays assuming that | |||
| // there was always a first delay of 1 x Nominal Poll time. This results | |||
| // in an expansion like this: 1,2,3,5,8,13,21,34,... | |||
| public: | |||
| class BadPollTimerValue {}; // Exception for bad values. | |||
| PollTimer(int Nom, int Max); // Construct with nominal and max delays. | |||
| int setNominalPollTime(int Nom); // Set the Nominal Poll Time. | |||
| int setMaximumPollTime(int Max); // Set the Maximum Poll Time. | |||
| void reset(); // Reset the spiral. | |||
| int pause(); // Pause between polls. | |||
| }; | |||
| /* PollTimer Documentation... | |||
| ** | |||
| ** PollTimer(nominal_delay, maximum_delay) | |||
| ** Constructs a PollTimer and sets it's basic parameters. If the parameters are | |||
| ** out of range then BadPollTimerValue will be thrown. | |||
| ** | |||
| ** setNiminalPollTime(Nom) | |||
| ** Sets the nominal (base unit) poll delay time. Throws BadPollTimerValue if | |||
| ** the value is out of range. | |||
| ** | |||
| ** setMaximumPollTime(Max) | |||
| ** Sets the maximum (upper limit) poll delay. If the value is out of range then | |||
| ** BadPollTimerValue is thrown. | |||
| ** | |||
| ** reset() | |||
| ** Resets the current poll delay to the nominal delay. The next call to pause() | |||
| ** will sleep for the nominal delay. This method would normally be called when | |||
| ** a poll cycle turns up some work to do so that subsequent poll delays will be | |||
| ** short - leading to a responsive system. | |||
| ** | |||
| ** pause() | |||
| ** Calling this method will cause the current thread to sleep for the current | |||
| ** poll delay time. Subsquent calls to pause will cause longer sleep times | |||
| ** according to a natural spiral. An intervening call to reset() will shorten | |||
| ** the delay times again. This method returns the number of milliseconds | |||
| ** paused on this pass. | |||
| */ | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // class Timer - This one acts much like a stop watch with millisecond | |||
| // resolution. The time is based on wall-clock time using gettimeofday() or | |||
| // GetSystemTimeAsFileTime depending on the OS. | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| typedef unsigned long long int msclock; // 64 bit int used for measuring time in ms. | |||
| static msclock EPOCH_DELTA_IN_USEC = 11644473600000000ULL; // Microseconds difference between epochs. | |||
| static msclock EPOCH_DELTA_IN_MSEC = EPOCH_DELTA_IN_USEC / 1000; // Milliseconds difference between epochs. | |||
| class Timer { | |||
| private: | |||
| msclock StartTime; // TimeOfDay at start. | |||
| msclock StopTime; // TimeOfDay at stop or check. | |||
| bool RunningFlag; // True if clock is running. | |||
| msclock getLocalRawClock() const; // Derives unix epoch ms from local clock. | |||
| public: | |||
| Timer(); // Construct and start the Timer. | |||
| Timer(msclock startt); // Constructs and starts from a specific moment. | |||
| void clear(); // Stop and set elapsed time to zero at now. | |||
| msclock start(); // Re(set) the Start time to this moment. | |||
| msclock start(msclock startt); // Re(set) the Start time to startt. | |||
| msclock getStartClock(); // Return the unix epoch start clock. | |||
| bool isRunning(); // Return true if the clock is running. | |||
| msclock getElapsedTime() const; // get milliseconds since Timer start. | |||
| msclock stop(); // Stop the Timer. | |||
| msclock getStopClock(); // Return the unix epoch stop clock. | |||
| double getElapsedSeconds()const; // Get floating point elapsed seconds. | |||
| bool isUnixBased(); // True if base clock is unix/posix. | |||
| msclock toWindowsEpoch(msclock unixt); // Converts unix t to windows t. | |||
| msclock toUnixEpoch(msclock win32t); // Converts windows t to unix t. | |||
| }; | |||
| /* Timer Documentation... | |||
| ** | |||
| ** All raw clock values are returned as 64 bit unsigned integers using a special | |||
| ** type - msclock. Conversions are done using microsecond accuracy. | |||
| ** | |||
| ** Timer() | |||
| ** Creates a new timer and starts the clock at this moment. | |||
| ** | |||
| ** Timer(msclock startt) | |||
| ** Creates a new timer and starts the clock at a specific moment. This can be | |||
| ** used to start one clock precisely when another one ends as in: | |||
| ** new Timer B(A.stop()); | |||
| ** | |||
| ** getLocalRawClock() | |||
| ** This method uses slightly different code depending upon whether the system | |||
| ** is a unix box or win32. In both cases the function determines the local time | |||
| ** value as a 64bit integer with millisecond resolution using the unix epoch of | |||
| ** Jan 1, 1970. | |||
| ** | |||
| ** start() | |||
| ** This method starts or restarts the Timer's clock at this moment. | |||
| ** The msclock value for the start clock is returned. | |||
| ** | |||
| ** start(msclock startt) | |||
| ** This method starts or restarts the Timer's clock at the time specified | |||
| ** int startt. This is used for chaining operations such as B.start(A.stop()) | |||
| ** | |||
| ** getStartClock() | |||
| ** This method returns the start clock value. | |||
| ** | |||
| ** isRunning() | |||
| ** Returns true if the clock is running. | |||
| ** | |||
| ** getElapsedTime() | |||
| ** This method returns the elapsed time in milliseconds. | |||
| ** If the clock is running this value will be different each time it is called. | |||
| ** | |||
| ** stop() | |||
| ** This method stops the clock and returns the stop clock value. If this method | |||
| ** is called more than once then the stop clock is reset to the current time and | |||
| ** that time is returned. | |||
| ** | |||
| ** getStopClock() | |||
| ** This method returns the stop clock value. If the Timer is still running | |||
| ** then the result is the same as calling getElapsedTime(). If the clock is | |||
| ** not running then the time the clock was last stopped is returned. | |||
| ** | |||
| ** getElapsedSeconds() | |||
| ** Returns the elapsed time as a floating point number with millisecond | |||
| ** resolution. | |||
| ** | |||
| ** isUnixBased() | |||
| ** Returns true if the raw clock values are being derived from a unix/posix OS. | |||
| ** | |||
| ** toWindowsEpoch(msclock unixt) | |||
| ** Converts unixt to a windows value by adding the epoch delta. | |||
| ** | |||
| ** toUnixEpoch(msclock win32t) | |||
| ** Converts win32t to a unix value by subtracting the epoch delta. | |||
| */ | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // class ScopeTimer - Runs a Timer while ScopeTimer is in scope. | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| class ScopeTimer { // Runs a timer when in scope. | |||
| private: | |||
| Timer& myTimer; // This is the timer to run. | |||
| public: | |||
| ScopeTimer(Timer& T) : myTimer(T) { myTimer.start(); } // The Timer starts at construction. | |||
| ~ScopeTimer() { myTimer.stop(); } // The Timer stops at destruction. | |||
| }; | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| // class Timeout - This one uses a Timer to establish a timeout value. | |||
| /////////////////////////////////////////////////////////////////////////////// | |||
| class Timeout { | |||
| private: | |||
| Timer myTimer; // We need a timer to do this. | |||
| msclock myDuration; // Milliseconds before timout expires. | |||
| public: | |||
| class BadTimeoutValue {}; // If the value is bad throw this. | |||
| Timeout(msclock duration); // Create and set the duration. | |||
| msclock setDuration(msclock duration); // Set/Change the duration in milliseconds. | |||
| msclock getDuration(); // Return the current duration in milliseconds. | |||
| msclock restart(); // Restart the timeout timer. | |||
| msclock getElapsedTime(); // Get elapsed milliseconds. | |||
| msclock getRemainingTime(); // Get remaining milliseconds. | |||
| bool isExpired(); // Return true if time is up. | |||
| }; | |||
| /* Timeout Documentation... | |||
| ** | |||
| ** Timeout(int duration) | |||
| ** Creates a Timout timer and sets the duration in milliseconds. | |||
| ** | |||
| ** setDuration(int duration) | |||
| ** Sets or changes the duration of the timeout timer. | |||
| ** The Timout is NOT reset by this method. This allows you to change | |||
| ** the timeout on the fly. | |||
| ** | |||
| ** restart() | |||
| ** Restarts the timeout timer. | |||
| ** | |||
| ** getElapsedTime() | |||
| ** Returns the number of milliseconds elapsed since the Timout was created | |||
| ** or reset. | |||
| ** | |||
| ** getRemainingTime() | |||
| ** Returns the number of milliseconds remaining before time is up. | |||
| ** | |||
| ** isExpired() | |||
| ** Returns true if time is up. | |||
| */ | |||
| #endif // End MNR_timing once-only switch. | |||