Przeglądaj źródła

setup

git-svn-id: https://svn.microneil.com/svn/CodeDweller/trunk@1 d34b734f-a00e-4b39-a726-e4eeb87269ab
wx
madscientist 16 lat temu
commit
1ea8084744
14 zmienionych plików z 6057 dodań i 0 usunięć
  1. 34
    0
      Makefile.am
  2. 276
    0
      base64codec.cpp
  3. 49
    0
      base64codec.hpp
  4. 1242
    0
      configuration.cpp
  5. 734
    0
      configuration.hpp
  6. 576
    0
      configuration.inline.hpp
  7. 60
    0
      histogram.hpp
  8. 630
    0
      networking.cpp
  9. 529
    0
      networking.hpp
  10. 368
    0
      networking.inline.hpp
  11. 434
    0
      threading.cpp
  12. 440
    0
      threading.hpp
  13. 325
    0
      timing.cpp
  14. 360
    0
      timing.hpp

+ 34
- 0
Makefile.am Wyświetl plik

@@ -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 *~

+ 276
- 0
base64codec.cpp Wyświetl plik

@@ -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;
}


+ 49
- 0
base64codec.hpp Wyświetl plik

@@ -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

+ 1242
- 0
configuration.cpp
Plik diff jest za duży
Wyświetl plik


+ 734
- 0
configuration.hpp Wyświetl plik

@@ -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


+ 576
- 0
configuration.inline.hpp Wyświetl plik

@@ -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;
}

+ 60
- 0
histogram.hpp Wyświetl plik

@@ -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


+ 630
- 0
networking.cpp Wyświetl plik

@@ -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
////////////////////////////////////////////////////////////////////////////////

+ 529
- 0
networking.hpp Wyświetl plik

@@ -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...

+ 368
- 0
networking.inline.hpp Wyświetl plik

@@ -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();
}

+ 434
- 0
threading.cpp Wyświetl plik

@@ -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
////////////////////////////////////////////////////////////////////////////////

+ 440
- 0
threading.hpp Wyświetl plik

@@ -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 ======================================

+ 325
- 0
timing.cpp Wyświetl plik

@@ -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.
}

+ 360
- 0
timing.hpp Wyświetl plik

@@ -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.

Ładowanie…
Anuluj
Zapisz