text: #ifndef __STATS_STRUCT_H_INCLUDED__
  #define __STATS_STRUCT_H_INCLUDED__
  
  // define the following to allow tracking of stats within UP Upper in tunnels
  //#define IP_UPPER_STATS
  
  //#include HASH_FUN_H            // defined in config.h
  #include <stdint.h>
  //#include <google/type_traits.h>
  //#include <google/dense_hash_map>
  //#include <google/sparsehash/densehashtable.h>
  #include <map>
  #include <functional>
  #include <iostream>
  // Prior to including RtsdSupport.h, define the windows integer types, and afterward undefine them.
  #include "defwintypes.h"
  #include "RtsdSupport.h"
  #include "undefwintypes.h"
  #include "string.h"
  
  #include "logger.h"
  
  // SAB NOTE - the following will enable protocol and port tracking.
  // This informaiton does not get sent to SART, so it is off for now.
  // #define IP_CONN_TRACK_IP_PROTO
  
  class StatStruct
  {
  public:
      StatStruct()
      {
          uBytesSrcToDest = 0;
          uBytesDestToSrc = 0;
          uBytesSrcToDestTunnel = 0;
          uBytesDestToSrcTunnel = 0;
          uFramesSrcToDest = 0;
          uFramesDestToSrc = 0;
      }
      StatStruct(const StatStruct &to_copy) :
          uBytesSrcToDest(to_copy.uBytesSrcToDest), uBytesDestToSrc(to_copy.uBytesDestToSrc),
          uBytesSrcToDestTunnel(to_copy.uBytesSrcToDestTunnel),
          uBytesDestToSrcTunnel(to_copy.uBytesDestToSrcTunnel),
          uFramesSrcToDest(to_copy.uFramesSrcToDest),
          uFramesDestToSrc(to_copy.uFramesDestToSrc) {}
  
      uint64_t uBytesSrcToDest;
      uint64_t uBytesDestToSrc;
      uint64_t uBytesSrcToDestTunnel;
      uint64_t uBytesDestToSrcTunnel;
      uint64_t uFramesSrcToDest;
      uint64_t uFramesDestToSrc;
  };
  
  class StatStructTunnel
  {
  public:
      StatStructTunnel()
      {
          uLinkBytes = 0;
          uTransportBytes = 0;
          uTunnelBytes = 0;
          uFrames = 0;
      }
      StatStructTunnel(const StatStructTunnel &to_copy) :
          uLinkBytes(to_copy.uLinkBytes), uTransportBytes(to_copy.uTransportBytes),
          uTunnelBytes(to_copy.uTunnelBytes), uFrames(to_copy.uFrames) {}
  
      uint64_t uLinkBytes;
      uint64_t uTransportBytes;
      uint64_t uTunnelBytes;
      uint64_t uFrames;
  };
  
  uint32_t hash_mem32(uint8_t const *buf, uint16_t len, uint32_t hash);
  uint8_t hash_mem8(uint8_t const *buf, uint16_t len, uint8_t hash);
  
  class IPv4ConnKey
  {
  public:
      // TODO: Evaluate performance penalties for using the full hash calculation
      //uint32_t hash;
      uint8_t  nPort;
      uint8_t  nVLAN;
      uint8_t  nMPLS;
      uint32_t aVLANMPLSIds[RTSD_MAX_VLANMPLS_IDS];
  
      uint32_t ipSrc;
      uint32_t ipDest;
      IPv4ConnKey() {}
      IPv4ConnKey(const IPv4ConnKey &to_copy) :
          nPort(to_copy.nPort),
          nVLAN(to_copy.nVLAN), nMPLS(to_copy.nMPLS),
          ipSrc(to_copy.ipSrc), ipDest(to_copy.ipDest)
      {
          memcpy(aVLANMPLSIds, to_copy.aVLANMPLSIds, sizeof(uint32_t)*RTSD_MAX_VLANMPLS_IDS);
      }
  
      // For HASH function
      bool operator==(const IPv4ConnKey &key) const
      {
          if(key.nPort != nPort)
              return false;
          if(!(((key.ipSrc == ipSrc) && (key.ipDest == ipDest)) ||
                  ((key.ipDest == ipSrc) && (key.ipSrc == ipDest))))
              return false;
          if((key.nVLAN != nVLAN) || (key.nMPLS != nMPLS))
              return false;
          for(int i=0; i<nVLAN+nMPLS; i++)
          {
              if(key.aVLANMPLSIds[i] != aVLANMPLSIds[i])
                  return false;
          }
          return true;
      }
  
      // hash function for concurrent_map
      operator uint32_t() const
      {
          return hash();
      }
  
      uint32_t hash() const
      {
          //printf("IPv4ConnKey::hash()\n");
          // SAB NOTE: has must be symmetric with respect to ip src and dest:
          //    hash(src,dest) == hash(dest,src)
          // Here we calculate the hash using the least significant bits of the
          // IP src and dest.  However they are byte swapped, so we use the
          // upper 16 bits
          uint32_t val;
          if(ipSrc <= ipDest)
              val = (ipDest & 0xFFFF0000) | (ipSrc >> 16 );
          else
              val = (ipSrc & 0xFFFF0000) | (ipDest >> 16 );
          //LOG_INFO(LOG_ID("upstats"), "hash(%08X,%08X) returns %08X\n", ipSrc, ipDest, val);
          return val;
      }
  
      bool operator<(IPv4ConnKey const &rhs) const
      {
          return hash() < rhs.hash();
      }
  };
  
  // STL hash
  typedef struct
  {
      long operator() (const IPv4ConnKey &k) const
      {
          return k.hash();
      }
  } IPv4ConnKeyHash;
  
  // STL equality compare
  typedef struct
  {
      long operator() (const IPv4ConnKey &k1, const IPv4ConnKey &k2) const
      {
          return k1 == k2;
      }
  } IPv4ConnKeyEq;
  
  // IPProtInfoKey -
  // key for IP protocol and port number information.  Used for map for flows within an IP connection
  class IPProtInfoKey
  {
  public:
      uint8_t  uProtocol;          // IP Next protocol field
      uint16_t uSrcPort;             // TCP/UDP Source Port
      uint16_t uDestPort;            // TCP/UDP Destination Port
  
      IPProtInfoKey() {}
      IPProtInfoKey(const IPProtInfoKey &to_copy) :
          uProtocol(to_copy.uProtocol), uSrcPort(to_copy.uSrcPort), uDestPort(to_copy.uDestPort) {}
      // For HASH function
      bool operator==(const IPProtInfoKey &key)
      {
          //return ((key.uSrcPort == uSrcPort) &&
          //        (key.uDestPort == uDestPort) &&
          //        (key.uProtocol == uProtocol) );
          return hash() == key.hash();
      }
  
      uint32_t hash() const
      {
          //return (uint32_t) (uProtocol ^ (uSrcPort | (uDestPort << 16)));
          return hash_mem32(&uProtocol, sizeof(uint16_t)*2+sizeof(uint8_t), 5381);
      }
  
      bool operator<(const IPProtInfoKey & rhs) const
      {
          return hash() < rhs.hash();
      }
  };
  
  typedef std::map< IPProtInfoKey, StatStruct, std::less<IPProtInfoKey> > IPProtInfoMap;
  
  class StatStructConn : public StatStruct
  {
  public:
      StatStructConn() {}
      StatStructConn(const StatStructConn &to_copy) : StatStruct(to_copy) {}
  
  
  // map of IP Protocol/port information for each flow
  #ifdef IP_CONN_TRACK_IP_PROTO
      IPProtInfoMap m_mapIPProtInfo;
  #endif
  };
  
  // StatStructIpFlow - used for ALU RTP flow tracking
  class StatStructIpFlow : public StatStruct
  {
  public:
      StatStructIpFlow()
      {
          uSeqErr = 0;
          uLastSeq = 0;
          uRtpPayloadType = 0;
          uCreateTimeSeconds = 0;
          uTouchTimeSeconds = 0;
          uTOS = 0;
          nPort = 0;
          uPortSub = 0;
          uFirstSeqErrTime = 0;
          uLastSeqErrTime = 0;
      }
      StatStructIpFlow(const StatStructIpFlow &to_copy) : StatStruct(to_copy)
      {
          uSeqErr = to_copy.uSeqErr;
          uLastSeq = to_copy.uLastSeq;
          uRtpPayloadType = to_copy.uRtpPayloadType;
          uCreateTimeSeconds = to_copy.uCreateTimeSeconds;
          uTouchTimeSeconds = to_copy.uTouchTimeSeconds;
          uTOS = to_copy.uTOS;
          nPort = to_copy.nPort;
          uPortSub = to_copy.uPortSub;
          uFirstSeqErrTime = to_copy.uFirstSeqErrTime;
          uLastSeqErrTime = to_copy.uLastSeqErrTime;
      }
      uint16_t uSeqErr;
      uint16_t uLastSeq;
      uint8_t  uRtpPayloadType;
      uint32_t uCreateTimeSeconds;
      uint32_t uTouchTimeSeconds;
      uint8_t  uTOS;
      uint16_t nPort;   // Mask of the port the packet came on the hardware
      uint16_t uPortSub;
      uint32_t uFirstSeqErrTime;
      uint32_t uLastSeqErrTime;
  };
  
  
  class StatStructIpTwoTuple : public StatStruct
  {
  public:
      StatStructIpTwoTuple()
      {
          Reset();
      }
  
      void addSrcToDstFrameBin(uint32_t frame_size)
      {
          uSrcToDstBins[get_bin(frame_size)]++;
      }
      void addDstToSrcFrameBin(uint32_t frame_size)
      {
          uDstToSrcBins[get_bin(frame_size)]++;
      }
  
      void Reset()
      {
          uStartTime = 0;
  
          // Start these at opposite ends of their max
          uSrcMinPacketLen = 65535;
          uSrcMaxPacketLen = 0;
          uDstMinPacketLen = 65535;
          uDstMaxPacketLen = 0;
  
          memset(&uSrcToDstBins[0], 0, sizeof(uint32_t)*10);
          memset(&uDstToSrcBins[0], 0, sizeof(uint32_t)*10);
      }
  
      uint32_t uStartTime;
      void set_time(uint32_t time)
      {
          if (uStartTime == 0)
              uStartTime = time;
      }
  
      uint16_t uSrcMinPacketLen;
      uint16_t uSrcMaxPacketLen;
      uint16_t uDstMinPacketLen;
      uint16_t uDstMaxPacketLen;
  
      uint32_t uSrcToDstBins[10];
      uint32_t uDstToSrcBins[10];
  
  private:
      // Packet bin sizes
      int get_bin(uint32_t frame_size)
      {
          if (frame_size < 64)
              return 0;
          if (frame_size < 128)
              return 1;
          if (frame_size < 256)
              return 2;
          if (frame_size < 512)
              return 3;
          if (frame_size < 1024)
              return 4;
          if (frame_size < 1519)
              return 5;
          if (frame_size < 2058)
              return 6;
          if (frame_size < 4096)
              return 7;
          if (frame_size < 8192)
              return 8;
          return 9;
      }
  };
  
  /////////////////////////////////////////////////////////////////////////////
  // IP FLOW Key classes
  /////////////////////////////////////////////////////////////////////////////
  
  class IPv4FlowKey
  {
  public:
      uint32_t ipSrc;       // IP Source Address
      uint32_t ipDest;      // IP Destination Address
      uint16_t uSrcPort;    // TCP/UDP Src Port
      uint16_t uDestPort;   // TCP/UDP Dest Port
      uint8_t  uProtocol;   // IP protocol
      uint16_t uVlan;       // first VLAN layer
  
      IPv4FlowKey() {}
      IPv4FlowKey(IPv4FlowKey const &to_copy) :
          ipSrc(to_copy.ipSrc), ipDest(to_copy.ipDest),
          uSrcPort(to_copy.uSrcPort), uDestPort(to_copy.uDestPort),
          uProtocol(to_copy.uProtocol),
          uVlan(to_copy.uVlan) {}
  
      // For HASH function
      bool operator==(const IPv4FlowKey &key) const
      {
          return ((key.ipSrc == ipSrc) &&
                  (key.ipDest == ipDest) &&
                  (key.uSrcPort == uSrcPort) &&
                  (key.uDestPort == uDestPort) &&
                  (key.uProtocol == uProtocol) &&
                  (key.uVlan == uVlan) );
      }
  
      uint32_t hash() const
      {
          //return hash_mem32((uint8_t*)&ipSrc, sizeof(uint32_t)*2+sizeof(uint16_t)*2+sizeof(uint8_t), 5381);
          return (uint32_t) (ipSrc ^ ipDest ^ uProtocol ^ (uSrcPort | (uDestPort << 16)) ^ (uVlan<<16));
      }
  
      bool operator<(const IPv4FlowKey &rhs) const
      {
          return hash() < rhs.hash();
      }
  };
  
  // STL hash
  typedef struct
  {
      long operator() (const IPv4FlowKey &k) const
      {
          return k.hash();
      }
  } IPv4FlowKeyHash;
  
  // STL equality compare
  typedef struct
  {
      long operator() (const IPv4FlowKey &k1, const IPv4FlowKey &k2) const
      {
          return k1 == k2;
      }
  } IPv4FlowKeyEq;
  
  class IPv6FlowKey
  {
  public:
      uint8_t  ipSrc[16]; // 16 IP Source Address
      uint8_t  ipDest[16];// 16 IP Destination Address
      uint16_t uSrcPort;    // 2 TCP/UDP Src Port
      uint16_t uDestPort;   // 2 TCP/UDP Dest Port
      uint16_t uVlan;       // first VLAN layer
      uint8_t  uProtocol;   // 1 IP protocol
  
      IPv6FlowKey() {}
      IPv6FlowKey(IPv6FlowKey const &to_copy)
      {
          memcpy(&(ipSrc[0]), &(to_copy.ipSrc[0]), sizeofthis());
      }
  
      // For HASH function
      bool operator==(const IPv6FlowKey &key) const
      {
          if(hash() != key.hash())
              return false;
          if(key.uSrcPort != key.uSrcPort)
              return false;
          if(key.uDestPort != key.uDestPort)
              return false;
          if(key.uProtocol != key.uProtocol)
              return false;
          if(memcmp(key.ipSrc,ipSrc,16) != 0)
              return false;
          if(memcmp(key.ipDest,ipDest,16) != 0)
              return false;
          if(key.uVlan != key.uVlan)
              return false;
          return true;
      }
      static uint32_t sizeofthis()
      {
          return sizeof(uint16_t)*3+sizeof(uint8_t)*33;
      }
      uint32_t hash() const
      {
          return (uint32_t) ( *((uint32_t*)&ipSrc)  ^ *(((uint32_t*)&ipSrc)+3) ^
                              *((uint32_t*)&ipDest) ^ *(((uint32_t*)&ipDest)+3) ^
                              (uSrcPort | (uDestPort << 16)) ^ uProtocol ^ (uVlan<<16));
      }
      bool operator<(const IPv6FlowKey &rhs) const
      {
          return hash() < rhs.hash();
      }
  };
  
  // STL hash
  typedef struct
  {
      long operator() (const IPv6FlowKey &k) const
      {
          return k.hash();
      }
  } IPv6FlowKeyHash;
  
  // STL equality compare
  typedef struct
  {
      long operator() (const IPv6FlowKey &k1, const IPv6FlowKey &k2) const
      {
          return k1 == k2;
      }
  } IPv6FlowKeyEq;
  
  class IPProtoInfo;
  class classify_info_t;
  
  class IpFlowKey
  {
  private:
      IPv4FlowKey m_ipv4Key;
      IPv6FlowKey m_ipv6Key;
      bool m_isIPv4;
      bool m_swapped;
  public:
      IpFlowKey() : m_isIPv4(true), m_swapped(false) {};
      IpFlowKey(uint8_t const *pData, const IPProtoInfo *pIPLayer, const classify_info_t *pClassifyInfo);
      void Init(uint8_t const *pData, const IPProtoInfo *pIPLayer, const classify_info_t *pClassifyInfo);
  
      static void aggregatePorts(uint16_t &srcPort, uint16_t &dstPort);
      uint8_t uProtocol(void) const;
      uint16_t uSrcPort(void) const;
      uint16_t uDestPort(void) const;
      uint16_t uVlan(void) const;
      IPv4FlowKey const &ipv4Key(void) const
      {
          return m_ipv4Key;
      }
      IPv6FlowKey const &ipv6Key(void) const
      {
          return m_ipv6Key;
      }
      bool isIPv4(void) const
      {
          return m_isIPv4;
      }
      bool isSwapped(void) const
      {
          return m_swapped;
      }
  };
  
  /////////////////////////////////////////////////////////////////////////////
  // IP Two Tuple Key
  /////////////////////////////////////////////////////////////////////////////
  
  class IPv4TwoTupleKey
  {
  public:
      uint32_t ipSrc;       // IP Source Address
      uint32_t ipDest;      // IP Destination Address
  
      IPv4TwoTupleKey() {}
      IPv4TwoTupleKey(IPv4TwoTupleKey const &to_copy) :
          ipSrc(to_copy.ipSrc),
          ipDest(to_copy.ipDest)
          {}
  
      // For HASH function
      bool operator==(const IPv4TwoTupleKey &key) const
      {
          return ((key.ipSrc == ipSrc) &&
                  (key.ipDest == ipDest));
      }
  
      uint32_t hash() const
      {
          return (uint32_t) (ipSrc ^ ipDest);
      }
  
      bool operator<(const IPv4TwoTupleKey &rhs) const
      {
          return ipSrc < ipDest;
      }
  };
  
  // STL hash
  typedef struct
  {
      long operator() (const IPv4TwoTupleKey &k) const
      {
          return k.hash();
      }
  } IPv4TwoTupleKeyHash;
  
  
  class IPv6TwoTupleKey
  {
  public:
      uint8_t  ipSrc[16]; // 16 IP Source Address
      uint8_t  ipDest[16];// 16 IP Destination Address
  
      IPv6TwoTupleKey() {}
  
      // For HASH function
      bool operator==(const IPv6TwoTupleKey &key) const
      {
          if(memcmp(key.ipSrc,ipSrc,16) != 0)
              return false;
          if(memcmp(key.ipDest,ipDest,16) != 0)
              return false;
          return true;
      }
  
      uint32_t hash() const
      {
          return (uint32_t) ( *((uint32_t*)&ipSrc)  ^ *(((uint32_t*)&ipSrc)+3) ^
                              *((uint32_t*)&ipDest) ^ *(((uint32_t*)&ipDest)+3) );
      }
  
      bool operator<(const IPv6TwoTupleKey &rhs) const
      {
          int src_comp = memcmp(ipSrc, rhs.ipSrc,16);
          if (src_comp < 0)
              return true;
          else if (src_comp > 0)
              return false;
  
          int dst_comp = memcmp(ipDest, rhs.ipDest,16);
          if (dst_comp < 0)
              return true;
          else if (dst_comp > 0)
              return false;
  
          return true;
      }
  };
  
  // STL hash
  typedef struct
  {
      long operator() (const IPv6TwoTupleKey &k) const
      {
          return k.hash();
      }
  } IPv6TwoTupleKeyHash;
  
  class IpTwoTupleKey
  {
  private:
      IPv4TwoTupleKey m_ipv4Key;
      IPv6TwoTupleKey m_ipv6Key;
      bool m_isIPv4;
      bool m_swapped;
  public:
      IpTwoTupleKey() : m_isIPv4(true), m_swapped(false) {};
      IpTwoTupleKey(uint8_t const *pData, const IPProtoInfo *pIPLayer, const classify_info_t *pClassifyInfo);
      void Init(uint8_t const *pData, const IPProtoInfo *pIPLayer, const classify_info_t *pClassifyInfo);
  
      IPv4TwoTupleKey const &ipv4Key(void) const
      {
          return m_ipv4Key;
      }
      IPv6TwoTupleKey const &ipv6Key(void) const
      {
          return m_ipv6Key;
      }
      bool isIPv4(void) const
      {
          return m_isIPv4;
      }
      bool isSwapped(void) const
      {
          return m_swapped;
      }
  };
  
  
  /////////////////////////////////////////////////////////////////////////////
  class IPv4ConnProtoKey
  {
  public:
      uint32_t ipSrc;       // IP Source Address
      uint32_t ipDest;      // IP Destination Address
      uint16_t uSrcPort;    // TCP/UDP Src Port
      uint16_t uDestPort;   // TCP/UDP Dest Port
      uint8_t  uProtocol;   // IP protocol
  
      IPv4ConnProtoKey() {}
      IPv4ConnProtoKey(IPv4ConnProtoKey const &to_copy) :
          ipSrc(to_copy.ipSrc), ipDest(to_copy.ipDest),
          uSrcPort(to_copy.uSrcPort), uDestPort(to_copy.uDestPort),
          uProtocol(to_copy.uProtocol) {}
  
      // For HASH function
      bool operator==(const IPv4ConnProtoKey &key) const
      {
          return ((key.ipSrc == ipSrc) &&
                  (key.ipDest == ipDest) &&
                  (key.uSrcPort == uSrcPort) &&
                  (key.uDestPort == uDestPort) &&
                  (key.uProtocol == uProtocol) );
      }
  
      uint32_t hash() const
      {
          //return hash_mem32((uint8_t*)&ipSrc, sizeof(uint32_t)*2+sizeof(uint16_t)*2+sizeof(uint8_t), 5381);
          return (uint32_t) (ipSrc ^ ipDest ^ uProtocol ^ (uSrcPort | (uDestPort << 16)));
      }
  
      bool operator<(const IPv4ConnProtoKey &rhs) const
      {
          return hash() < rhs.hash();
      }
  };
  
  // STL hash
  typedef struct
  {
      long operator() (const IPv4ConnProtoKey &k) const
      {
          return k.hash();
      }
  } IPv4ConnProtoKeyHash;
  
  // STL equality compare
  typedef struct
  {
      long operator() (const IPv4ConnProtoKey &k1, const IPv4ConnProtoKey &k2) const
      {
          return k1 == k2;
      }
  } IPv4ConnProtoKeyEq;
  
  class IPv6ConnProtoKey
  {
  public:
      uint8_t  ipSrc[16]; // 16 IP Source Address
      uint8_t  ipDest[16];// 16 IP Destination Address
      uint16_t uSrcPort;    // 2 TCP/UDP Src Port
      uint16_t uDestPort;   // 2 TCP/UDP Dest Port
      uint8_t  uProtocol;   // 1 IP protocol
  
      IPv6ConnProtoKey() {}
      IPv6ConnProtoKey(IPv6ConnProtoKey const &to_copy)
      {
          memcpy(&(ipSrc[0]), &(to_copy.ipSrc[0]), sizeofthis());
      }
  
      // For HASH function
      bool operator==(const IPv6ConnProtoKey &key) const
      {
          if(hash() != key.hash())
              return false;
          if(key.uSrcPort != key.uSrcPort)
              return false;
          if(key.uDestPort != key.uDestPort)
              return false;
          if(key.uProtocol != key.uProtocol)
              return false;
          if(memcmp(key.ipSrc,ipSrc,16) != 0)
              return false;
          if(memcmp(key.ipDest,ipDest,16) != 0)
              return false;
          return true;
      }
      static uint32_t sizeofthis()
      {
          return sizeof(uint16_t)*2+sizeof(uint8_t)*34;
      }
      uint32_t hash() const
      {
          return (uint32_t) ( *((uint32_t*)&ipSrc)  ^ *(((uint32_t*)&ipSrc)+3) ^
                              *((uint32_t*)&ipDest) ^ *(((uint32_t*)&ipDest)+3) ^
                              (uSrcPort | (uDestPort << 16)) ^ uProtocol);
      }
      bool operator<(const IPv6ConnProtoKey &rhs) const
      {
          return hash() < rhs.hash();
      }
  };
  
  // STL hash
  typedef struct
  {
      long operator() (const IPv6ConnProtoKey &k) const
      {
          return k.hash();
      }
  } IPv6ConnProtoKeyHash;
  
  // STL equality compare
  typedef struct
  {
      long operator() (const IPv6ConnProtoKey &k1, const IPv6ConnProtoKey &k2) const
      {
          return k1 == k2;
      }
  } IPv6ConnProtoKeyEq;
  
  
  class IPv4TunnelKey
  {
  public:
      // TODO: Evaluate performance loss due to recalculating the hash all the time.
      //uint32_t hash;
      uint8_t  nPort;
      uint32_t aVLANMPLSIds[RTSD_MAX_VLANMPLS_IDS];
      uint8_t  nVLAN;
      uint8_t  nMPLS;
      uint32_t ipSrc;
      uint32_t ipDest;
      uint32_t TEI;
  
      IPv4TunnelKey() {}
      IPv4TunnelKey(IPv4TunnelKey const &to_copy)
          : nPort(to_copy.nPort)
      {
          memcpy(&(aVLANMPLSIds[0]), &(to_copy.aVLANMPLSIds[0]), sizeofthis());
      }
  
      // For HASH function
      bool operator==(const IPv4TunnelKey &key) const
      {
          if(key.nPort != nPort)
              return false;
          if((key.TEI != TEI) || (key.ipSrc != ipSrc) || (key.ipDest != ipDest) ||
                  (key.nVLAN != nVLAN) || (key.nMPLS != nMPLS))
              return false;
          for(int i=0; i<nVLAN+nMPLS; i++)
          {
              if(key.aVLANMPLSIds[i] != aVLANMPLSIds[i])
                  return false;
          }
          return true;
      }
  
      static uint32_t sizeofthis()
      {
          return sizeof(uint8_t)*2+2+sizeof(uint32_t)*(3+RTSD_MAX_VLANMPLS_IDS);
      }
      uint32_t hash() const
      {
          return ipSrc ^ ipDest ^ TEI;
      }
      // concurrent_hash_map helpers
      operator uint32_t() const
      {
          return hash();
      }
  };
  
  typedef struct
  {
      long operator() (const IPv4TunnelKey &k) const
      {
          return k.hash();
      }
  } IPv4TunnelKeyHash;
  
  typedef struct
  {
      long operator() (const IPv4TunnelKey &k1, const IPv4TunnelKey &k2) const
      {
          return k1 == k2;
      }
  } IPv4TunnelKeyEq;
  
  typedef std::map<IPv4ConnProtoKey, StatStructTunnel, std::less<IPv4ConnProtoKey> > IPv4UpperConnMap;
  typedef std::map<IPv6ConnProtoKey, StatStructTunnel, std::less<IPv6ConnProtoKey> > IPv6UpperConnMap;
  
  class StatStructTunnelLower : public StatStructTunnel
  {
  public:
      StatStructTunnelLower() {}
      StatStructTunnelLower(StatStructTunnelLower const &to_copy)
          : StatStructTunnel(to_copy) {}
  
  #ifdef IP_UPPER_STATS
      IPv4UpperConnMap m_mapIPv4UpperConns;
      IPv6UpperConnMap m_mapIPv6UpperConns;
  #endif
  };
  
  #endif // __STATS_STRUCT_H_INCLUDED__
  