continuation of last commit
This commit is contained in:
386
pvDataApp/misc/bitSet.cpp
Normal file
386
pvDataApp/misc/bitSet.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
/* bitSet.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include "string.h"
|
||||
#include "stdio.h"
|
||||
#include "bitSet.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
BitSet::BitSet() : words(0), wordsLength(0), wordsInUse(0) {
|
||||
initWords(BITS_PER_WORD);
|
||||
}
|
||||
|
||||
BitSet::BitSet(uint32 nbits) : words(0), wordsLength(0), wordsInUse(0) {
|
||||
initWords(nbits);
|
||||
}
|
||||
|
||||
BitSet::~BitSet() {
|
||||
delete words;
|
||||
}
|
||||
|
||||
void BitSet::initWords(uint32 nbits) {
|
||||
uint32 length = (nbits <= 0) ? 1 : wordIndex(nbits-1) + 1;
|
||||
if (words) delete words;
|
||||
words = new uint64[length];
|
||||
bzero(words, sizeof(uint64)*length);
|
||||
wordsLength = length;
|
||||
}
|
||||
|
||||
void BitSet::recalculateWordsInUse() {
|
||||
// wordsInUse is unsigned
|
||||
if (wordsInUse == 0)
|
||||
return;
|
||||
|
||||
// Traverse the bitset until a used word is found
|
||||
uint32 i;
|
||||
for (i = wordsInUse-1; i >= 0; i--)
|
||||
if (words[i] != 0)
|
||||
break;
|
||||
|
||||
wordsInUse = i+1; // The new logical size
|
||||
}
|
||||
|
||||
void BitSet::ensureCapacity(uint32 wordsRequired) {
|
||||
if (wordsLength < wordsRequired) {
|
||||
|
||||
// create and copy
|
||||
uint64* newwords = new uint64[wordsRequired];
|
||||
bzero(newwords, sizeof(uint64)*wordsRequired);
|
||||
memcpy(newwords, words, sizeof(uint64)*wordsLength);
|
||||
if (words) delete words;
|
||||
words = newwords;
|
||||
wordsLength = wordsRequired;
|
||||
}
|
||||
}
|
||||
|
||||
void BitSet::expandTo(uint32 wordIndex) {
|
||||
uint32 wordsRequired = wordIndex+1;
|
||||
if (wordsInUse < wordsRequired) {
|
||||
ensureCapacity(wordsRequired);
|
||||
wordsInUse = wordsRequired;
|
||||
}
|
||||
}
|
||||
|
||||
void BitSet::flip(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] ^= (((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
|
||||
void BitSet::set(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
expandTo(wordIdx);
|
||||
|
||||
words[wordIdx] |= (((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
}
|
||||
|
||||
void BitSet::clear(uint32 bitIndex) {
|
||||
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
if (wordIdx >= wordsInUse)
|
||||
return;
|
||||
|
||||
words[wordIdx] &= ~(((uint64)1) << (bitIndex % BITS_PER_WORD));
|
||||
|
||||
recalculateWordsInUse();
|
||||
}
|
||||
|
||||
void BitSet::set(uint32 bitIndex, bool value) {
|
||||
if (value)
|
||||
set(bitIndex);
|
||||
else
|
||||
clear(bitIndex);
|
||||
}
|
||||
|
||||
bool BitSet::get(uint32 bitIndex) const {
|
||||
uint32 wordIdx = wordIndex(bitIndex);
|
||||
return ((wordIdx < wordsInUse)
|
||||
&& ((words[wordIdx] & (((uint64)1) << (bitIndex % BITS_PER_WORD))) != 0));
|
||||
}
|
||||
|
||||
void BitSet::clear() {
|
||||
while (wordsInUse > 0)
|
||||
words[--wordsInUse] = 0;
|
||||
}
|
||||
|
||||
uint32 BitSet::numberOfTrailingZeros(uint64 i) {
|
||||
// HD, Figure 5-14
|
||||
uint32 x, y;
|
||||
if (i == 0) return 64;
|
||||
uint32 n = 63;
|
||||
y = (uint32)i; if (y != 0) { n = n -32; x = y; } else x = (uint32)(i>>32);
|
||||
y = x <<16; if (y != 0) { n = n -16; x = y; }
|
||||
y = x << 8; if (y != 0) { n = n - 8; x = y; }
|
||||
y = x << 4; if (y != 0) { n = n - 4; x = y; }
|
||||
y = x << 2; if (y != 0) { n = n - 2; x = y; }
|
||||
return n - ((x << 1) >> 31);
|
||||
}
|
||||
|
||||
uint32 BitSet::bitCount(uint64 i) {
|
||||
// HD, Figure 5-14
|
||||
i = i - ((i >> 1) & 0x5555555555555555LL);
|
||||
i = (i & 0x3333333333333333LL) + ((i >> 2) & 0x3333333333333333LL);
|
||||
i = (i + (i >> 4)) & 0x0f0f0f0f0f0f0f0fLL;
|
||||
i = i + (i >> 8);
|
||||
i = i + (i >> 16);
|
||||
i = i + (i >> 32);
|
||||
return (uint32)(i & 0x7f);
|
||||
}
|
||||
|
||||
int32 BitSet::nextSetBit(uint32 fromIndex) const {
|
||||
|
||||
uint32 u = wordIndex(fromIndex);
|
||||
if (u >= wordsInUse)
|
||||
return -1;
|
||||
|
||||
uint64 word = words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD));
|
||||
|
||||
while (true) {
|
||||
if (word != 0)
|
||||
return (u * BITS_PER_WORD) + numberOfTrailingZeros(word);
|
||||
if (++u == wordsInUse)
|
||||
return -1;
|
||||
word = words[u];
|
||||
}
|
||||
}
|
||||
|
||||
int32 BitSet::nextClearBit(uint32 fromIndex) const {
|
||||
// Neither spec nor implementation handle bitsets of maximal length.
|
||||
|
||||
uint32 u = wordIndex(fromIndex);
|
||||
if (u >= wordsInUse)
|
||||
return fromIndex;
|
||||
|
||||
uint64 word = ~words[u] & (WORD_MASK << (fromIndex % BITS_PER_WORD));
|
||||
|
||||
while (true) {
|
||||
if (word != 0)
|
||||
return (u * BITS_PER_WORD) + numberOfTrailingZeros(word);
|
||||
if (++u == wordsInUse)
|
||||
return wordsInUse * BITS_PER_WORD;
|
||||
word = ~words[u];
|
||||
}
|
||||
}
|
||||
|
||||
bool BitSet::isEmpty() const {
|
||||
return (wordsInUse == 0);
|
||||
}
|
||||
|
||||
uint32 BitSet::cardinality() const {
|
||||
uint32 sum = 0;
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
sum += bitCount(words[i]);
|
||||
return sum;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator&=(const BitSet& set) {
|
||||
|
||||
while (wordsInUse > set.wordsInUse)
|
||||
words[--wordsInUse] = 0;
|
||||
|
||||
// Perform logical AND on words in common
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
words[i] &= set.words[i];
|
||||
|
||||
recalculateWordsInUse();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator|=(const BitSet& set) {
|
||||
|
||||
uint32 wordsInCommon;
|
||||
if (wordsInUse < set.wordsInUse) {
|
||||
wordsInCommon = wordsInUse;
|
||||
//ensureCapacity(set.wordsInUse);
|
||||
//wordsInUse = set.wordsInUse;
|
||||
}
|
||||
else
|
||||
wordsInCommon = set.wordsInUse;
|
||||
|
||||
// Perform logical OR on words in common
|
||||
uint32 i = 0;
|
||||
for (; i < wordsInCommon; i++)
|
||||
words[i] |= set.words[i];
|
||||
|
||||
// TODO what to do if BitSets are not the same size !!!
|
||||
|
||||
// recalculateWordsInUse() is not needed
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator^=(const BitSet& set) {
|
||||
|
||||
uint32 wordsInCommon;
|
||||
if (wordsInUse < set.wordsInUse) {
|
||||
wordsInCommon = wordsInUse;
|
||||
//ensureCapacity(set.wordsInUse);
|
||||
//wordsInUse = set.wordsInUse;
|
||||
}
|
||||
else
|
||||
wordsInCommon = set.wordsInUse;
|
||||
|
||||
// Perform logical XOR on words in common
|
||||
uint32 i = 0;
|
||||
for (; i < wordsInCommon; i++)
|
||||
words[i] ^= set.words[i];
|
||||
|
||||
// TODO what to do if BitSets are not the same size !!!
|
||||
|
||||
recalculateWordsInUse();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator-=(const BitSet& set) {
|
||||
|
||||
uint32 wordsInCommon;
|
||||
if (wordsInUse < set.wordsInUse) {
|
||||
wordsInCommon = wordsInUse;
|
||||
//ensureCapacity(set.wordsInUse);
|
||||
//wordsInUse = set.wordsInUse;
|
||||
}
|
||||
else
|
||||
wordsInCommon = set.wordsInUse;
|
||||
|
||||
// Perform logical (a & !b) on words in common
|
||||
uint32 i = 0;
|
||||
for (; i < wordsInCommon; i++)
|
||||
words[i] &= ~set.words[i];
|
||||
|
||||
recalculateWordsInUse();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet& BitSet::operator=(const BitSet &set) {
|
||||
// Check for self-assignment!
|
||||
if (this == &set)
|
||||
return *this;
|
||||
|
||||
// we ensure that words array size is adequate (and not wordsInUse to ensure capacity to the future)
|
||||
if (wordsLength < set.wordsLength)
|
||||
{
|
||||
if (words) delete words;
|
||||
words = new uint64[set.wordsLength];
|
||||
wordsLength = set.wordsLength;
|
||||
}
|
||||
memcpy(words, set.words, sizeof(uint64)*set.wordsInUse);
|
||||
wordsInUse = set.wordsInUse;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BitSet::or_and(const BitSet& set1, const BitSet& set2) {
|
||||
uint32 inUse = (set1.wordsInUse < set2.wordsInUse) ? set1.wordsInUse : set2.wordsInUse;
|
||||
|
||||
ensureCapacity(inUse);
|
||||
wordsInUse = inUse;
|
||||
|
||||
// Perform logical AND on words in common
|
||||
for (uint32 i = 0; i < inUse; i++)
|
||||
words[i] |= (set1.words[i] & set2.words[i]);
|
||||
|
||||
// recalculateWordsInUse()...
|
||||
}
|
||||
|
||||
bool BitSet::operator==(const BitSet &set) const
|
||||
{
|
||||
if (this == &set)
|
||||
return true;
|
||||
|
||||
if (wordsInUse != set.wordsInUse)
|
||||
return false;
|
||||
|
||||
// Check words in use by both BitSets
|
||||
for (uint32 i = 0; i < wordsInUse; i++)
|
||||
if (words[i] != set.words[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BitSet::operator!=(const BitSet &set) const
|
||||
{
|
||||
return !(*this == set);
|
||||
}
|
||||
|
||||
void BitSet::toString(StringBuilder buffer) { toString(buffer, 0); }
|
||||
|
||||
void BitSet::toString(StringBuilder buffer, int indentLevel) const
|
||||
{
|
||||
*buffer += '{';
|
||||
int32 i = nextSetBit(0);
|
||||
char tmp[30];
|
||||
if (i != -1) {
|
||||
sprintf(tmp,"%d",i); *buffer += tmp;
|
||||
for (i = nextSetBit(i+1); i >= 0; i = nextSetBit(i+1)) {
|
||||
int32 endOfRun = nextClearBit(i);
|
||||
do { *buffer += ", "; sprintf(tmp,"%d",i); *buffer += tmp; } while (++i < endOfRun);
|
||||
}
|
||||
}
|
||||
*buffer += '}';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
void serialize(ByteBuffer buffer, SerializableControl flusher) {
|
||||
|
||||
final int n = wordsInUse;
|
||||
if (n == 0) {
|
||||
SerializeHelper.writeSize(0, buffer, flusher);
|
||||
return;
|
||||
}
|
||||
int len = 8 * (n-1);
|
||||
for (long x = words[n - 1]; x != 0; x >>>= 8)
|
||||
len++;
|
||||
|
||||
SerializeHelper.writeSize(len, buffer, flusher);
|
||||
flusher.ensureBuffer(len);
|
||||
|
||||
for (int i = 0; i < n - 1; i++)
|
||||
buffer.putLong(words[i]);
|
||||
|
||||
for (long x = words[n - 1]; x != 0; x >>>= 8)
|
||||
buffer.put((byte) (x & 0xff));
|
||||
}
|
||||
|
||||
public void deserialize(ByteBuffer buffer, DeserializableControl control) {
|
||||
|
||||
final int bytes = SerializeHelper.readSize(buffer, control); // in bytes
|
||||
|
||||
wordsInUse = (bytes + 7) / 8;
|
||||
if (wordsInUse > words.length)
|
||||
words = new long[wordsInUse];
|
||||
|
||||
if (wordsInUse == 0)
|
||||
return;
|
||||
|
||||
control.ensureData(bytes);
|
||||
|
||||
int i = 0;
|
||||
final int longs = bytes / 8;
|
||||
while (i < longs)
|
||||
words[i++] = buffer.getLong();
|
||||
|
||||
for (int j = i; j < wordsInUse; j++)
|
||||
words[j] = 0;
|
||||
|
||||
for (int remaining = (bytes - longs * 8), j = 0; j < remaining; j++)
|
||||
words[i] |= (buffer.get() & 0xffL) << (8 * j);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
}};
|
||||
310
pvDataApp/misc/bitSet.h
Normal file
310
pvDataApp/misc/bitSet.h
Normal file
@@ -0,0 +1,310 @@
|
||||
/* bitSet.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef BITSET_H
|
||||
#define BITSET_H
|
||||
#include <stdexcept>
|
||||
#include <pvType.h>
|
||||
//#include "byteBuffer.h"
|
||||
//#include "serialize.h"
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* This class implements a vector of bits that grows as needed. Each
|
||||
* component of the bit set has a {@code bool} value. The
|
||||
* bits of a {@code BitSet} are indexed by nonnegative integers.
|
||||
* Individual indexed bits can be examined, set, or cleared. One
|
||||
* {@code BitSet} may be used to modify the contents of another
|
||||
* {@code BitSet} through logical AND, logical inclusive OR, and
|
||||
* logical exclusive OR operations.
|
||||
*
|
||||
* <p>By default, all bits in the set initially have the value
|
||||
* {@code false}.
|
||||
*
|
||||
* <p>Every bit set has a current size, which is the number of bits
|
||||
* of space currently in use by the bit set. Note that the size is
|
||||
* related to the implementation of a bit set, so it may change with
|
||||
* implementation. The length of a bit set relates to logical length
|
||||
* of a bit set and is defined independently of implementation.
|
||||
*
|
||||
* <p>A {@code BitSet} is not safe for multithreaded use without
|
||||
* external synchronization.
|
||||
*
|
||||
* Based on Java implementation.
|
||||
*/
|
||||
class BitSet /*: public Serializable*/ {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates a new bit set. All bits are initially {@code false}.
|
||||
*/
|
||||
BitSet();
|
||||
|
||||
/**
|
||||
* Creates a bit set whose initial size is large enough to explicitly
|
||||
* represent bits with indices in the range {@code 0} through
|
||||
* {@code nbits-1}. All bits are initially {@code false}.
|
||||
*
|
||||
* @param nbits the initial size of the bit set
|
||||
*/
|
||||
BitSet(uint32 nbits);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~BitSet();
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to the complement of its
|
||||
* current value.
|
||||
*
|
||||
* @param bitIndex the index of the bit to flip
|
||||
*/
|
||||
void flip(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to {@code true}.
|
||||
*
|
||||
* @param bitIndex a bit index
|
||||
*/
|
||||
void set(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit specified by the index to {@code false}.
|
||||
*
|
||||
* @param bitIndex the index of the bit to be cleared
|
||||
*/
|
||||
void clear(uint32 bitIndex);
|
||||
|
||||
/**
|
||||
* Sets the bit at the specified index to the specified value.
|
||||
*
|
||||
* @param bitIndex a bit index
|
||||
* @param value a boolean value to set
|
||||
*/
|
||||
void set(uint32 bitIndex, bool value);
|
||||
|
||||
/**
|
||||
* Returns the value of the bit with the specified index. The value
|
||||
* is {@code true} if the bit with the index {@code bitIndex}
|
||||
* is currently set in this {@code BitSet}; otherwise, the result
|
||||
* is {@code false}.
|
||||
*
|
||||
* @param bitIndex the bit index
|
||||
* @return the value of the bit with the specified index
|
||||
*/
|
||||
bool get(uint32 bitIndex) const;
|
||||
|
||||
/**
|
||||
* Sets all of the bits in this BitSet to {@code false}.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Returns the index of the first bit that is set to {@code true}
|
||||
* that occurs on or after the specified starting index. If no such
|
||||
* bit exists then {@code -1} is returned.
|
||||
*
|
||||
* <p>To iterate over the {@code true} bits in a {@code BitSet},
|
||||
* use the following loop:
|
||||
*
|
||||
* <pre> {@code
|
||||
* for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
|
||||
* // operate on index i here
|
||||
* }}</pre>
|
||||
*
|
||||
* @param fromIndex the index to start checking from (inclusive)
|
||||
* @return the index of the next set bit, or {@code -1} if there
|
||||
* is no such bit
|
||||
*/
|
||||
int32 nextSetBit(uint32 fromIndex) const;
|
||||
|
||||
/**
|
||||
* Returns the index of the first bit that is set to {@code false}
|
||||
* that occurs on or after the specified starting index.
|
||||
*
|
||||
* @param fromIndex the index to start checking from (inclusive)
|
||||
* @return the index of the next clear bit
|
||||
*/
|
||||
int32 nextClearBit(uint32 fromIndex) const;
|
||||
|
||||
/**
|
||||
* Returns true if this {@code BitSet} contains no bits that are set
|
||||
* to {@code true}.
|
||||
*
|
||||
* @return indicating whether this {@code BitSet} is empty
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
|
||||
/**
|
||||
* Returns the number of bits set to {@code true} in this {@code BitSet}.
|
||||
*
|
||||
* @return the number of bits set to {@code true} in this {@code BitSet}
|
||||
*/
|
||||
uint32 cardinality() const;
|
||||
|
||||
/**
|
||||
* Performs a logical <b>AND</b> of this target bit set with the
|
||||
* argument bit set. This bit set is modified so that each bit in it
|
||||
* has the value {@code true} if and only if it both initially
|
||||
* had the value {@code true} and the corresponding bit in the
|
||||
* bit set argument also had the value {@code true}.
|
||||
*
|
||||
* @param set a bit set
|
||||
*/
|
||||
BitSet& operator&=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Performs a logical <b>OR</b> of this bit set with the bit set
|
||||
* argument. This bit set is modified so that a bit in it has the
|
||||
* value {@code true} if and only if it either already had the
|
||||
* value {@code true} or the corresponding bit in the bit set
|
||||
* argument has the value {@code true}.
|
||||
*
|
||||
* @param set a bit set
|
||||
*/
|
||||
BitSet& operator|=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Performs a logical <b>XOR</b> of this bit set with the bit set
|
||||
* argument. This bit set is modified so that a bit in it has the
|
||||
* value {@code true} if and only if one of the following
|
||||
* statements holds:
|
||||
* <ul>
|
||||
* <li>The bit initially has the value {@code true}, and the
|
||||
* corresponding bit in the argument has the value {@code false}.
|
||||
* <li>The bit initially has the value {@code false}, and the
|
||||
* corresponding bit in the argument has the value {@code true}.
|
||||
* </ul>
|
||||
*
|
||||
* @param set a bit set
|
||||
*/
|
||||
BitSet& operator^=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Clears all of the bits in this {@code BitSet} whose corresponding
|
||||
* bit is set in the specified {@code BitSet}.
|
||||
*
|
||||
* @param set the {@code BitSet} with which to mask this
|
||||
* {@code BitSet}
|
||||
*/
|
||||
BitSet& operator-=(const BitSet& set);
|
||||
|
||||
/**
|
||||
* Assigment operator.
|
||||
*/
|
||||
BitSet& operator=(const BitSet &set);
|
||||
|
||||
/**
|
||||
* Perform AND operation on <code>set1</code> and <code>set2</code>,
|
||||
* and OR on result and this instance.
|
||||
* @param set1
|
||||
* @param set2
|
||||
*/
|
||||
void or_and(const BitSet& set1, const BitSet& set2);
|
||||
|
||||
/**
|
||||
* Comparison operator.
|
||||
*/
|
||||
bool operator==(const BitSet &set) const;
|
||||
|
||||
bool operator!=(const BitSet &set) const;
|
||||
|
||||
void toString(StringBuilder buffer);
|
||||
|
||||
void toString(StringBuilder buffer, int indentLevel) const;
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* BitSets are packed into arrays of "words." Currently a word is
|
||||
* a long, which consists of 64 bits, requiring 6 address bits.
|
||||
* The choice of word size is determined purely by performance concerns.
|
||||
*/
|
||||
static const uint32 ADDRESS_BITS_PER_WORD = 6;
|
||||
static const uint32 BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;
|
||||
static const uint32 BIT_INDEX_MASK = BITS_PER_WORD - 1;
|
||||
|
||||
/** Used to shift left or right for a partial word mask */
|
||||
static const uint64 WORD_MASK = ~((uint64)0);
|
||||
|
||||
/** The internal field corresponding to the serialField "bits". */
|
||||
uint64* words;
|
||||
|
||||
/** The internal field corresponding to the size of words[] array. */
|
||||
uint32 wordsLength;
|
||||
|
||||
/** The number of words in the logical size of this BitSet. */
|
||||
uint32 wordsInUse;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Given a bit index, return word index containing it.
|
||||
*/
|
||||
static inline uint32 wordIndex(uint32 bitIndex) {
|
||||
return bitIndex >> ADDRESS_BITS_PER_WORD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new word array.
|
||||
*/
|
||||
void initWords(uint32 nbits);
|
||||
|
||||
/**
|
||||
* Sets the field wordsInUse to the logical size in words of the bit set.
|
||||
* WARNING: This method assumes that the number of words actually in use is
|
||||
* less than or equal to the current value of wordsInUse!
|
||||
*/
|
||||
void recalculateWordsInUse();
|
||||
|
||||
/**
|
||||
* Ensures that the BitSet can hold enough words.
|
||||
* @param wordsRequired the minimum acceptable number of words.
|
||||
*/
|
||||
void ensureCapacity(uint32 wordsRequired);
|
||||
|
||||
/**
|
||||
* Ensures that the BitSet can accommodate a given wordIndex,
|
||||
* temporarily violating the invariants. The caller must
|
||||
* restore the invariants before returning to the user,
|
||||
* possibly using recalculateWordsInUse().
|
||||
* @param wordIndex the index to be accommodated.
|
||||
*/
|
||||
void expandTo(uint32 wordIndex);
|
||||
|
||||
/**
|
||||
* Returns the number of zero bits following the lowest-order ("rightmost")
|
||||
* one-bit in the two's complement binary representation of the specified
|
||||
* <tt>long</tt> value. Returns 64 if the specified value has no
|
||||
* one-bits in its two's complement representation, in other words if it is
|
||||
* equal to zero.
|
||||
*
|
||||
* @return the number of zero bits following the lowest-order ("rightmost")
|
||||
* one-bit in the two's complement binary representation of the
|
||||
* specified <tt>long</tt> value, or 64 if the value is equal
|
||||
* to zero.
|
||||
*/
|
||||
static uint32 numberOfTrailingZeros(uint64 i);
|
||||
|
||||
/**
|
||||
* Returns the number of one-bits in the two's complement binary
|
||||
* representation of the specified <tt>long</tt> value. This function is
|
||||
* sometimes referred to as the <i>population count</i>.
|
||||
*
|
||||
* @return the number of one-bits in the two's complement binary
|
||||
* representation of the specified <tt>long</tt> value.
|
||||
*/
|
||||
static uint32 bitCount(uint64 i);
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* BITSET_H */
|
||||
|
||||
|
||||
|
||||
198
pvDataApp/misc/byteBuffer.cpp
Normal file
198
pvDataApp/misc/byteBuffer.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/*
|
||||
* byteBuffer.cpp
|
||||
*
|
||||
* Created on: Oct 18, 2010
|
||||
* Author: Miha Vitorovic
|
||||
*/
|
||||
#include <cstring>
|
||||
#include "byteBuffer.h"
|
||||
#include "epicsException.h"
|
||||
|
||||
namespace epics {
|
||||
namespace pvData {
|
||||
|
||||
using std::stringstream;
|
||||
|
||||
ByteBuffer::ByteBuffer(int size, int byteOrder) :
|
||||
_bufferByteOrder(byteOrder), _size(size), _position(0),
|
||||
_limit(size), _buffer(0) {
|
||||
|
||||
if (size < 0)
|
||||
throw EpicsException("negative size");
|
||||
|
||||
if (byteOrder!=EPICS_ENDIAN_BIG && byteOrder!=EPICS_ENDIAN_LITTLE)
|
||||
throw EpicsException("invalid endian");
|
||||
|
||||
_buffer = new char[_size];
|
||||
}
|
||||
|
||||
ByteBuffer::~ByteBuffer() {
|
||||
if (_buffer) delete _buffer;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::clear() {
|
||||
_position = 0;
|
||||
_limit = _size;
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::flip() {
|
||||
_limit = _position;
|
||||
_position = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::rewind() {
|
||||
_position = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
bool ByteBuffer::getBoolean() {
|
||||
if(_position<_limit)
|
||||
return _buffer[_position++]==0 ? false : true;
|
||||
else
|
||||
throw EpicsException("buffer underflow");
|
||||
}
|
||||
|
||||
int8 ByteBuffer::getByte() {
|
||||
if(_position<_limit)
|
||||
return (int8)_buffer[_position++];
|
||||
else
|
||||
throw EpicsException("buffer underflow");
|
||||
}
|
||||
|
||||
int16 ByteBuffer::getShort() {
|
||||
if(_limit-_position<(int)sizeof(int16))
|
||||
throw EpicsException("buffer underflow");
|
||||
int16 val;
|
||||
getWithEndianness((char*)&val, sizeof(int16)); // store short into val
|
||||
return val;
|
||||
}
|
||||
|
||||
int32 ByteBuffer::getInt() {
|
||||
if(_limit-_position<(int)sizeof(int32))
|
||||
throw EpicsException("buffer underflow");
|
||||
int32 val;
|
||||
getWithEndianness((char*)&val, sizeof(int32)); // store int into val
|
||||
return val;
|
||||
}
|
||||
|
||||
int64 ByteBuffer::getLong() {
|
||||
if(_limit-_position<(int)sizeof(int64))
|
||||
throw EpicsException("buffer underflow");
|
||||
int64 val;
|
||||
getWithEndianness((char*)&val, sizeof(int64)); // store long into val
|
||||
return val;
|
||||
}
|
||||
|
||||
float ByteBuffer::getFloat() {
|
||||
if(_limit-_position<(int)sizeof(float))
|
||||
throw EpicsException("buffer underflow");
|
||||
float val;
|
||||
getWithEndianness((char*)&val, sizeof(float)); // store float into val
|
||||
return val;
|
||||
}
|
||||
|
||||
double ByteBuffer::getDouble() {
|
||||
if(_limit-_position<(int)sizeof(double))
|
||||
throw EpicsException("buffer underflow");
|
||||
double val;
|
||||
getWithEndianness((char*)&val, sizeof(double)); // store double into val
|
||||
return val;
|
||||
}
|
||||
|
||||
void ByteBuffer::get(char* dst, int offset, int count) {
|
||||
if(count>getRemaining()) throw EpicsException("buffer underflow");
|
||||
for(int i = 0; i<count; i++)
|
||||
dst[offset+i] = _buffer[_position++];
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::put(const char* src, int offset, int count) {
|
||||
if(count>getRemaining()) throw EpicsException("buffer overflow");
|
||||
for(int i = 0; i<count; i++)
|
||||
_buffer[_position++] = src[offset+i];
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::putBoolean(bool value) {
|
||||
if(_position<_limit)
|
||||
_buffer[_position++] = value ? 1 : 0;
|
||||
else
|
||||
throw EpicsException("buffer overflow");
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::putByte(int8 value) {
|
||||
if(_position<_limit)
|
||||
_buffer[_position++] = (char)value;
|
||||
else
|
||||
throw EpicsException("buffer overflow");
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::putShort(int16 value) {
|
||||
if(_limit-_position<(int)sizeof(int16))
|
||||
throw EpicsException("buffer overflow");
|
||||
putWithEndianness((char*)&value, sizeof(int16)); // store short into buffer
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::putInt(int32 value) {
|
||||
if(_limit-_position<(int)sizeof(int32))
|
||||
throw EpicsException("buffer overflow");
|
||||
putWithEndianness((char*)&value, sizeof(int32)); // store int into buffer
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::putLong(int64 value) {
|
||||
if(_limit-_position<(int)sizeof(int64))
|
||||
throw EpicsException("buffer overflow");
|
||||
putWithEndianness((char*)&value, sizeof(int64)); // store long into buffer
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::putFloat(float value) {
|
||||
if(_limit-_position<(int)sizeof(float))
|
||||
throw EpicsException("buffer overflow");
|
||||
putWithEndianness((char*)&value, sizeof(float)); // store float into buffer
|
||||
return this;
|
||||
}
|
||||
|
||||
ByteBuffer* ByteBuffer::putDouble(double value) {
|
||||
if(_limit-_position<(int)sizeof(double))
|
||||
throw EpicsException("buffer overflow");
|
||||
putWithEndianness((char*)&value, sizeof(double)); // store double into buffer
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer underflow or overflow checks are performed in the caller.
|
||||
*/
|
||||
void ByteBuffer::getWithEndianness(char* dest, size_t size) {
|
||||
if(_bufferByteOrder==EPICS_BYTE_ORDER)
|
||||
for(size_t i = 0; i<size; i++)
|
||||
dest[i] = _buffer[_position++];
|
||||
else
|
||||
for(int i = (int)size-1; i>=0; i--)
|
||||
dest[i] = _buffer[_position++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer underflow or overflow checks are performed in the caller.
|
||||
*/
|
||||
void ByteBuffer::putWithEndianness(char* src, size_t size) {
|
||||
if(_bufferByteOrder==EPICS_BYTE_ORDER)
|
||||
for(size_t i = 0; i<size; i++)
|
||||
_buffer[_position++] = src[i];
|
||||
else
|
||||
for(int i = (int)size-1; i>=0; i--)
|
||||
_buffer[_position++] = src[i];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
330
pvDataApp/misc/byteBuffer.h
Normal file
330
pvDataApp/misc/byteBuffer.h
Normal file
@@ -0,0 +1,330 @@
|
||||
/* byteBuffer.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef BYTEBUFFER_H
|
||||
#define BYTEBUFFER_H
|
||||
|
||||
#include <string>
|
||||
#include <pvType.h>
|
||||
#include <epicsEndian.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvData {
|
||||
|
||||
|
||||
/** @brief A buffer of bytes.
|
||||
*
|
||||
* A buffer of bytes, which has
|
||||
* - capacity (size)
|
||||
* - limit
|
||||
* - position
|
||||
* The capacity is the maximum capacity of the buffer, the buffer's
|
||||
* limit is the index of the first element that should not be read or
|
||||
* written, and its position is the index of the next element to be
|
||||
* written.
|
||||
*
|
||||
* The buffer also ha a byte order specified which can be little or
|
||||
* big endian.
|
||||
*/
|
||||
class ByteBuffer {
|
||||
public:
|
||||
/** @brief ByteBuffer constructor.
|
||||
*
|
||||
* Creates a buffer of a given size, and with a specified byteOrder.
|
||||
*
|
||||
* @param size Specify the capacity of the buffer. Default is 32.
|
||||
* @param byteOrder the byte order of the buffer. Can be either
|
||||
* EPICS_ENDIAN_LITTLE or EPICS_ENDIAN_BIG.
|
||||
*/
|
||||
ByteBuffer(int size = 32, int byteOrder = EPICS_BYTE_ORDER);
|
||||
|
||||
/**
|
||||
* @brief ByteBuffer destructor.
|
||||
*/
|
||||
~ByteBuffer();
|
||||
|
||||
/**
|
||||
* Clears the buffer. Sets the {@code position} to 0 and {@code limit}
|
||||
* to {@code capacity}.
|
||||
*/
|
||||
ByteBuffer* clear();
|
||||
|
||||
/**
|
||||
* Flips the buffer. The {@code limit} is set to the current {@code position}
|
||||
* and the {@code position} is set to {@code 0}. This prepares the
|
||||
* buffer for a series of relative @em get operations after a series
|
||||
* of relative @em put operations.
|
||||
*/
|
||||
ByteBuffer* flip();
|
||||
|
||||
/**
|
||||
* Rewinds the buffer. This sets {@code position} to {@code 0}
|
||||
* without affecting the {@code limit}.
|
||||
*/
|
||||
ByteBuffer* rewind();
|
||||
|
||||
/**
|
||||
* Relative boolean read, {@code position} is incremented by
|
||||
* {@code 1}.
|
||||
*
|
||||
* @returns The next byte in the buffer as a boolean.
|
||||
* @throws EpicsException - Buffer underflow if there are no bytes
|
||||
* remaining in the buffer.
|
||||
*/
|
||||
bool getBoolean();
|
||||
|
||||
/**
|
||||
* Relative Int8 read, {@code position} is incremented by
|
||||
* {@code 1}.
|
||||
*
|
||||
* @returns The next byte in the buffer as a signed Int8.
|
||||
* @throws EpicsException - Buffer underflow if there are no bytes
|
||||
* remaining in the buffer.
|
||||
*/
|
||||
int8 getByte();
|
||||
|
||||
/**
|
||||
* Relative Int16 read, {@code position} is incremented by
|
||||
* {@code 2}.
|
||||
*
|
||||
* @returns The next Int16 value in the buffer.
|
||||
* @throws EpicsException - Buffer underflow if there are less than
|
||||
* 2 bytes remaining in the buffer.
|
||||
*/
|
||||
int16 getShort();
|
||||
|
||||
/**
|
||||
* Relative Int32 read, {@code position} is incremented by
|
||||
* {@code 4}.
|
||||
*
|
||||
* @returns The next Int32 value in the buffer.
|
||||
* @throws EpicsException - Buffer underflow if there are less than
|
||||
* 4 bytes remaining in the buffer.
|
||||
*/
|
||||
int32 getInt();
|
||||
|
||||
/**
|
||||
* Relative Int64 read, {@code position} is incremented by
|
||||
* {@code 8}.
|
||||
*
|
||||
* @returns The next Int64 value in the buffer.
|
||||
* @throws EpicsException - Buffer underflow if there are less than
|
||||
* 8 bytes remaining in the buffer.
|
||||
*/
|
||||
int64 getLong();
|
||||
|
||||
/**
|
||||
* Relative float read, {@code position} is incremented by
|
||||
* {@code 4}.
|
||||
*
|
||||
* @returns The next float value in the buffer.
|
||||
* @throws EpicsException - Buffer underflow if there are less than
|
||||
* 4 bytes remaining in the buffer.
|
||||
*/
|
||||
float getFloat();
|
||||
|
||||
/**
|
||||
* Relative double read, {@code position} is incremented by
|
||||
* {@code 8}.
|
||||
*
|
||||
* @returns The next double value in the buffer.
|
||||
* @throws EpicsException - Buffer underflow if there are less than
|
||||
* 8 bytes remaining in the buffer.
|
||||
*/
|
||||
double getDouble();
|
||||
|
||||
/**
|
||||
* Relative bulk @em get method. It transfers {@code count} bytes
|
||||
* from the buffer into the {@code dst}.
|
||||
*
|
||||
* @param[out] dst Destination buffer
|
||||
* @param[in] offset Offset in the destination buffer
|
||||
* @param[in] count The number of bytes to copy
|
||||
* @throws EpicsException - Buffer underflow if there are fewer
|
||||
* than count bytes remaining in the buffer.
|
||||
*/
|
||||
void get(char* dst, int offset, int count);
|
||||
|
||||
//virtual String getString() = 0; // TODO
|
||||
|
||||
|
||||
/**
|
||||
* Relative bulk @em put method. It transfers {@code count} bytes
|
||||
* from the {@code src} into the the buffer.
|
||||
*
|
||||
* @param[in] src Source buffer
|
||||
* @param[in] offset Offset in the source buffer
|
||||
* @param[in] count The number of bytes to copy
|
||||
* @returns Pointer to this ByteBuffer instance.
|
||||
* @throws EpicsException - Buffer overflow if there are not
|
||||
* enough bytes remaining in the buffer.
|
||||
*/
|
||||
ByteBuffer* put(const char* src, int offset, int count);
|
||||
|
||||
/**
|
||||
* Relative boolean write, {@code position} is incremented by
|
||||
* {@code 1}.
|
||||
*
|
||||
* @param[in] value The boolean value to write.
|
||||
* @throws EpicsException - Buffer overflow if there are no
|
||||
* bytes remaining in the buffer.
|
||||
*/
|
||||
ByteBuffer* putBoolean(bool value);
|
||||
|
||||
|
||||
/**
|
||||
* Relative Int8 write, {@code position} is incremented by
|
||||
* {@code 1}.
|
||||
*
|
||||
* @param[in] value The Int8 value to write.
|
||||
* @throws EpicsException - Buffer overflow if there are no
|
||||
* bytes remaining in the buffer.
|
||||
*/
|
||||
ByteBuffer* putByte(int8 value);
|
||||
|
||||
/**
|
||||
* Relative Int16 write, {@code position} is incremented by
|
||||
* {@code 2}.
|
||||
*
|
||||
* @param[in] value The Int16 value to write.
|
||||
* @throws EpicsException - Buffer overflow if there are less than
|
||||
* 2 bytes remaining in the buffer.
|
||||
*/
|
||||
ByteBuffer* putShort(int16 value);
|
||||
|
||||
/**
|
||||
* Relative Int32 write, {@code position} is incremented by
|
||||
* {@code 4}.
|
||||
*
|
||||
* @param[in] value The Int32 value to write.
|
||||
* @throws EpicsException - Buffer overflow if there are less than
|
||||
* 4 bytes remaining in the buffer.
|
||||
*/
|
||||
ByteBuffer* putInt(int32 value);
|
||||
|
||||
/**
|
||||
* Relative Int64 write, {@code position} is incremented by
|
||||
* {@code 8}.
|
||||
*
|
||||
* @param[in] value The Int64 value to write.
|
||||
* @throws EpicsException - Buffer overflow if there are less than
|
||||
* 8 bytes remaining in the buffer.
|
||||
*/
|
||||
ByteBuffer* putLong(int64 value);
|
||||
|
||||
/**
|
||||
* Relative float write, {@code position} is incremented by
|
||||
* {@code 4}.
|
||||
*
|
||||
* @param[in] value The float value to write.
|
||||
* @throws EpicsException - Buffer overflow if there are less than
|
||||
* 4 bytes remaining in the buffer.
|
||||
*/
|
||||
ByteBuffer* putFloat(float value);
|
||||
|
||||
/**
|
||||
* Relative float write, {@code position} is incremented by
|
||||
* {@code 8}.
|
||||
*
|
||||
* @param[in] value The double value to write.
|
||||
* @throws EpicsException - Buffer overflow if there are less than
|
||||
* 8 bytes remaining in the buffer.
|
||||
*/
|
||||
ByteBuffer* putDouble(double value);
|
||||
|
||||
//virtual ByteBuffer *putString(String value) = 0; // TODO
|
||||
|
||||
/**
|
||||
* The capacity (size) of the buffer in bytes.
|
||||
*
|
||||
* @returns The capacity of the buffer in bytes.
|
||||
*/
|
||||
inline int getSize() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset from the start of the buffer in bytes. Currently this
|
||||
* method always returns {@code 0}.
|
||||
*
|
||||
* @returns The offset from the start of the buffer in bytes
|
||||
*/
|
||||
inline int getArrayOffset() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The byte index of the next element to @em get or @em put.
|
||||
*
|
||||
* @returns The byte index of the next element.
|
||||
*/
|
||||
inline int getPosition() const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
/**
|
||||
* The byte index of the {@code limit}. {@code limit} is always less
|
||||
* or equal to the buffer capacity. No @em put or @em get operation
|
||||
* can be performed pass the {@code limit}.
|
||||
*
|
||||
* @returns The byte index of the {@code limit}.
|
||||
*/
|
||||
inline int getLimit() const {
|
||||
return _limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of the bytes that can be successfully read or written.
|
||||
*
|
||||
* @returns The number of the bytes that can be successfully read or
|
||||
* written.
|
||||
*/
|
||||
inline int getRemaining() const {
|
||||
return _limit-_position;
|
||||
}
|
||||
|
||||
/**
|
||||
* The byte order of the buffer.
|
||||
*
|
||||
* @returns Either {@code EPICS_ENDIAN_LITTLE} or
|
||||
* {@code EPICS_ENDIAN_BIG}.
|
||||
*/
|
||||
inline int getByteOrder() const {
|
||||
return _bufferByteOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives a read-only access to the buffer.
|
||||
*
|
||||
* @returns A const (read-only) pointer to the actual buffer.
|
||||
*/
|
||||
inline const char* getArray() const {
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
// TODO must define arrays
|
||||
|
||||
private:
|
||||
int _bufferByteOrder, _size, _position, _limit;
|
||||
char* _buffer;
|
||||
|
||||
/**
|
||||
* Reads the next <pre>size</pre> bytes from the buffer and stores them
|
||||
* into the destination taking into account endianness of the buffer
|
||||
* and the current hardware.
|
||||
*/
|
||||
void getWithEndianness(char* dest, size_t size);
|
||||
/**
|
||||
* Puts the next <pre>size</pre> bytes into the buffer reading them
|
||||
* from source taking into account endianness of the buffer
|
||||
* and the current hardware.
|
||||
*/
|
||||
void putWithEndianness(char* src, size_t size);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif /* BYTEBUFFER_H */
|
||||
231
pvDataApp/misc/epicsException.h
Normal file
231
pvDataApp/misc/epicsException.h
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/*
|
||||
* epicsException.hpp
|
||||
*
|
||||
* Created on: Oct 20, 2010
|
||||
* Author: Matej Sekoranja
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#ifndef EPICSEXCEPTION_H_
|
||||
#define EPICSEXCEPTION_H_
|
||||
|
||||
// TODO to be redefined!!!!!!
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <execinfo.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
class BaseException :
|
||||
public std::exception {
|
||||
public:
|
||||
BaseException(const char* message, const char* file, int line, std::exception* cause = 0)
|
||||
: m_msg(message), m_file(file), m_line(line), m_cause(cause)
|
||||
{
|
||||
getStackTrace(&m_stackTrace);
|
||||
}
|
||||
|
||||
virtual ~BaseException() throw()
|
||||
{
|
||||
if (m_cause) delete m_cause;
|
||||
}
|
||||
|
||||
virtual const char* what() const throw() { return m_msg.c_str(); }
|
||||
|
||||
const char* getFile() const { return m_file.c_str(); }
|
||||
int getLine() const { return m_line; }
|
||||
|
||||
void toString(std::string& str, unsigned int depth = 0) {
|
||||
str.append("BaseException: ");
|
||||
str.append(m_msg);
|
||||
str.append("\n\tat ");
|
||||
str.append(m_file);
|
||||
str.append(":");
|
||||
char sline[10];
|
||||
snprintf(sline, 10, "%d", m_line);
|
||||
str.append(sline);
|
||||
str.append("\n");
|
||||
str.append(m_stackTrace);
|
||||
if (m_cause)
|
||||
{
|
||||
str.append("caused by: ");
|
||||
BaseException *be = dynamic_cast<BaseException*>(m_cause);
|
||||
if (be)
|
||||
be->toString(str, depth+1);
|
||||
else
|
||||
str.append(m_cause->what());
|
||||
}
|
||||
}
|
||||
|
||||
/** Get stack trace, i.e. demangled backtrace of the caller. */
|
||||
static inline void getStackTrace(std::string* trace, unsigned int skip_frames = 0, unsigned int max_frames = 63)
|
||||
{
|
||||
#ifdef DISABLE_STACK_TRACE
|
||||
trace += "(stack trace disabled)";
|
||||
#else
|
||||
// storage array for stack trace address data
|
||||
void* addrlist[max_frames+1];
|
||||
|
||||
// retrieve current stack addresses
|
||||
int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));
|
||||
|
||||
if (addrlen == 0) {
|
||||
trace->append("(stack trace not available)");
|
||||
return;
|
||||
}
|
||||
|
||||
// resolve addresses into strings containing "filename(function+address)",
|
||||
// this array must be free()-ed
|
||||
char** symbollist = backtrace_symbols(addrlist, addrlen);
|
||||
|
||||
// allocate string which will be filled with the demangled function name
|
||||
size_t funcnamesize = 256;
|
||||
char* funcname = (char*)malloc(funcnamesize);
|
||||
|
||||
// iterate over the returned symbol lines. skip the first, it is the
|
||||
// address of this function.
|
||||
for (int i = (1 + skip_frames); i < addrlen; i++)
|
||||
{
|
||||
char *module = 0, *fname = 0, *offset = 0;
|
||||
#ifdef __APPLE__
|
||||
int stage = 0;
|
||||
for (char *p = symbollist[i]; *p; ++p)
|
||||
{
|
||||
// find spaces and separate
|
||||
// 0 a.out 0x0000000100000bbc _Z11print_tracev + 22
|
||||
switch (stage)
|
||||
{
|
||||
case 0: // skip frame index
|
||||
if (*p == ' ') stage++;
|
||||
break;
|
||||
case 1: // skip spaces
|
||||
if (*p != ' ') { module = p; stage++; }
|
||||
break;
|
||||
case 2: // module name
|
||||
if (*p == ' ') { *p = '\0'; stage++; }
|
||||
break;
|
||||
case 3: // skip spaces
|
||||
if (*p != ' ') stage++;
|
||||
break;
|
||||
case 4: // address
|
||||
if (*p == ' ') { fname = p+1; stage++; }
|
||||
break;
|
||||
case 5: // function
|
||||
if (*p == ' ') { *p = '\0'; stage++; }
|
||||
break;
|
||||
case 6: // "+ "
|
||||
if (*p == '+') { p++; offset = p+1; };
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// find parentheses and +address offset surrounding the mangled name:
|
||||
// ./module(function+0x15c) [0x8048a6d]
|
||||
module = symbollist[i];
|
||||
for (char *p = symbollist[i]; *p; ++p)
|
||||
{
|
||||
if (*p == '(') {
|
||||
// terminate module
|
||||
*p = '\0';
|
||||
fname = p+1;
|
||||
}
|
||||
else if (*p == '+') {
|
||||
// terminate fname
|
||||
*p = '\0';
|
||||
offset = p+1;
|
||||
}
|
||||
else if (*p == ')' && offset) {
|
||||
// terminate offset
|
||||
*p = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fname && offset && offset && fname < offset)
|
||||
{
|
||||
|
||||
// mangled name is now in [begin_name, begin_offset) and caller
|
||||
// offset in [begin_offset, end_offset). now apply
|
||||
// __cxa_demangle():
|
||||
|
||||
int status;
|
||||
char* ret = abi::__cxa_demangle(fname,
|
||||
funcname, &funcnamesize, &status);
|
||||
if (status == 0) {
|
||||
trace->append("\t ");
|
||||
*trace += module;
|
||||
trace->append(": ");
|
||||
*trace += ret; // use possibly realloc()-ed string
|
||||
trace->append("+");
|
||||
*trace += offset;
|
||||
trace->append("\n");
|
||||
}
|
||||
else {
|
||||
// demangling failed. Output function name as a C function with
|
||||
// no arguments.
|
||||
trace->append("\t ");
|
||||
*trace += module;
|
||||
trace->append(": ");
|
||||
*trace += fname;
|
||||
*trace += "()+";
|
||||
*trace += offset;
|
||||
trace->append("\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// couldn't parse the line? print the whole line.
|
||||
trace->append("\t ");
|
||||
*trace += symbollist[i];
|
||||
trace->append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
free(funcname);
|
||||
free(symbollist);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_msg;
|
||||
std::string m_file;
|
||||
int m_line;
|
||||
std::exception* m_cause;
|
||||
std::string m_stackTrace;
|
||||
};
|
||||
|
||||
|
||||
#define THROW_BASE_EXCEPTION(msg) throw new BaseException(msg, __FILE__, __LINE__)
|
||||
#define THROW_BASE_EXCEPTION_CAUSE(msg, cause) throw new BaseException(msg, __FILE__, __LINE__, cause)
|
||||
|
||||
/*
|
||||
/// Construct with file, line info and printf-type arguments.
|
||||
GenericException(const char *sourcefile, size_t line, const char *format, ...)
|
||||
__attribute__ ((format (printf, 4, 5)));
|
||||
*/
|
||||
|
||||
/** Base Epics Exception */
|
||||
class EpicsException : public std::logic_error {
|
||||
public:
|
||||
explicit EpicsException(const std::string& arg) :
|
||||
std::logic_error(arg) {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* EPICSEXCEPTION_H_ */
|
||||
107
pvDataApp/misc/event.cpp
Normal file
107
pvDataApp/misc/event.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* event.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <epicsThread.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsEvent.h>
|
||||
#include "noDefaultMethods.h"
|
||||
#include "pvType.h"
|
||||
#include "lock.h"
|
||||
#include "event.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static volatile int64 totalConstruct = 0;
|
||||
static volatile int64 totalDestruct = 0;
|
||||
static Mutex *globalMutex = 0;
|
||||
static String alreadyOn("already on list");
|
||||
|
||||
|
||||
static int64 getTotalConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalDestruct;
|
||||
}
|
||||
|
||||
static ConstructDestructCallback *pConstructDestructCallback;
|
||||
|
||||
static void init()
|
||||
{
|
||||
static Mutex mutex = Mutex();
|
||||
Lock xx(&mutex);
|
||||
if(globalMutex==0) {
|
||||
globalMutex = new Mutex();
|
||||
pConstructDestructCallback = new ConstructDestructCallback(
|
||||
String("event"),
|
||||
getTotalConstruct,getTotalDestruct,0);
|
||||
}
|
||||
}
|
||||
|
||||
Event::~Event() {
|
||||
epicsEventDestroy(id);
|
||||
id = 0;
|
||||
Lock xx(globalMutex);
|
||||
totalDestruct++;
|
||||
}
|
||||
|
||||
|
||||
Event::Event(bool full)
|
||||
: id(epicsEventCreate(full?epicsEventFull : epicsEventEmpty))
|
||||
{
|
||||
init();
|
||||
Lock xx(globalMutex);
|
||||
totalConstruct++;
|
||||
}
|
||||
|
||||
ConstructDestructCallback *Event::getConstructDestructCallback()
|
||||
{
|
||||
init();
|
||||
return pConstructDestructCallback;
|
||||
}
|
||||
|
||||
void Event::signal()
|
||||
{
|
||||
if(id==0) throw std::logic_error(String("event was deleted"));
|
||||
epicsEventSignal(id);
|
||||
}
|
||||
|
||||
bool Event::wait ()
|
||||
{
|
||||
if(id==0) throw std::logic_error(String("event was deleted"));
|
||||
epicsEventWaitStatus status = epicsEventWait(id);
|
||||
return status==epicsEventWaitOK ? true : false;
|
||||
}
|
||||
|
||||
bool Event::wait ( double timeOut )
|
||||
{
|
||||
if(id==0) throw std::logic_error(String("event was deleted"));
|
||||
epicsEventWaitStatus status = epicsEventWaitWithTimeout(id,timeOut);
|
||||
return status==epicsEventWaitOK ? true : false;
|
||||
}
|
||||
|
||||
bool Event::tryWait ()
|
||||
{
|
||||
if(id==0) throw std::logic_error(String("event was deleted"));
|
||||
epicsEventWaitStatus status = epicsEventTryWait(id);
|
||||
return status==epicsEventWaitOK ? true : false;
|
||||
}
|
||||
|
||||
}}
|
||||
33
pvDataApp/misc/event.h
Normal file
33
pvDataApp/misc/event.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* event.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsEvent.h>
|
||||
#include "noDefaultMethods.h"
|
||||
#include "pvType.h"
|
||||
#include "showConstructDestruct.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class Event : private NoDefaultMethods {
|
||||
public:
|
||||
explicit Event(bool = false);
|
||||
~Event();
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
void signal();
|
||||
bool wait (); /* blocks until full */
|
||||
bool wait ( double timeOut ); /* false if empty at time out */
|
||||
bool tryWait (); /* false if empty */
|
||||
private:
|
||||
epicsEventId id;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* EVENT_H */
|
||||
187
pvDataApp/misc/executor.cpp
Normal file
187
pvDataApp/misc/executor.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/* executor.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "linkedList.h"
|
||||
#include "lock.h"
|
||||
#include "thread.h"
|
||||
#include "event.h"
|
||||
#include "executor.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static volatile int64 totalConstruct = 0;
|
||||
static volatile int64 totalDestruct = 0;
|
||||
static Mutex *globalMutex = 0;
|
||||
|
||||
static int64 getTotalConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalDestruct;
|
||||
}
|
||||
|
||||
static ConstructDestructCallback *pConstructDestructCallback;
|
||||
|
||||
static void init() {
|
||||
static Mutex mutex = Mutex();
|
||||
Lock xx(&mutex);
|
||||
if(globalMutex==0) {
|
||||
globalMutex = new Mutex();
|
||||
pConstructDestructCallback = new ConstructDestructCallback(
|
||||
String("executor"),
|
||||
getTotalConstruct,getTotalDestruct,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef LinkedListNode<ExecutorNode> ExecutorListNode;
|
||||
typedef LinkedList<ExecutorNode> ExecutorList;
|
||||
|
||||
class ExecutorNode {
|
||||
public:
|
||||
ExecutorNode(Command *command);
|
||||
~ExecutorNode();
|
||||
|
||||
Command *command;
|
||||
ExecutorListNode *node;
|
||||
ExecutorListNode *runNode;
|
||||
};
|
||||
|
||||
ExecutorNode::ExecutorNode(Command *command)
|
||||
: command(command),
|
||||
node(new ExecutorListNode(this)),
|
||||
runNode(new ExecutorListNode(this))
|
||||
{}
|
||||
|
||||
ExecutorNode::~ExecutorNode()
|
||||
{
|
||||
delete node;
|
||||
delete runNode;
|
||||
}
|
||||
|
||||
ConstructDestructCallback *Executor::getConstructDestructCallback()
|
||||
{
|
||||
init();
|
||||
return pConstructDestructCallback;
|
||||
}
|
||||
|
||||
class ExecutorPvt : public Runnable{
|
||||
public:
|
||||
ExecutorPvt(String threadName,ThreadPriority priority);
|
||||
~ExecutorPvt();
|
||||
ExecutorNode * createNode(Command *command);
|
||||
void execute(ExecutorNode *node);
|
||||
virtual void run();
|
||||
private:
|
||||
ExecutorList *executorList;
|
||||
ExecutorList *runList;
|
||||
Event *moreWork;
|
||||
Event *stopped;
|
||||
Mutex mutex;
|
||||
volatile bool alive;
|
||||
Thread *thread;
|
||||
};
|
||||
|
||||
ExecutorPvt::ExecutorPvt(String threadName,ThreadPriority priority)
|
||||
: executorList(new ExecutorList()),
|
||||
runList(new ExecutorList()),
|
||||
moreWork(new Event(false)),
|
||||
stopped(new Event(false)),
|
||||
mutex(Mutex()),
|
||||
alive(true),
|
||||
thread(new Thread(threadName,priority,this))
|
||||
{}
|
||||
|
||||
ExecutorPvt::~ExecutorPvt()
|
||||
{
|
||||
{
|
||||
Lock xx(&mutex);
|
||||
alive = false;
|
||||
}
|
||||
moreWork->signal();
|
||||
{
|
||||
Lock xx(&mutex);
|
||||
stopped->wait();
|
||||
}
|
||||
ExecutorListNode *node;
|
||||
while((node=executorList->removeHead())!=0) {
|
||||
delete node->getObject();
|
||||
}
|
||||
delete thread;
|
||||
delete stopped;
|
||||
delete moreWork;
|
||||
delete runList;
|
||||
delete executorList;
|
||||
}
|
||||
|
||||
void ExecutorPvt::run()
|
||||
{
|
||||
while(alive) {
|
||||
ExecutorListNode * executorListNode = 0;
|
||||
while(alive && runList->isEmpty()) {
|
||||
moreWork->wait();
|
||||
}
|
||||
if(alive) {
|
||||
Lock xx(&mutex);
|
||||
executorListNode = runList->removeHead();
|
||||
}
|
||||
if(alive && executorListNode!=0) {
|
||||
executorListNode->getObject()->command->command();
|
||||
}
|
||||
}
|
||||
stopped->signal();
|
||||
}
|
||||
|
||||
ExecutorNode * ExecutorPvt::createNode(Command *command)
|
||||
{
|
||||
Lock xx(&mutex);
|
||||
ExecutorNode *executorNode = new ExecutorNode(command);
|
||||
executorList->addTail(executorNode->node);
|
||||
return executorNode;
|
||||
}
|
||||
|
||||
void ExecutorPvt::execute(ExecutorNode *node)
|
||||
{
|
||||
Lock xx(&mutex);
|
||||
if(!alive || node->runNode->isOnList()) return;
|
||||
bool isEmpty = runList->isEmpty();
|
||||
runList->addTail(node->runNode);
|
||||
if(isEmpty) moreWork->signal();
|
||||
}
|
||||
|
||||
Executor::Executor(String threadName,ThreadPriority priority)
|
||||
: pImpl(new ExecutorPvt(threadName,priority))
|
||||
{
|
||||
init();
|
||||
Lock xx(globalMutex);
|
||||
totalConstruct++;
|
||||
}
|
||||
|
||||
Executor::~Executor() {
|
||||
delete pImpl;
|
||||
Lock xx(globalMutex);
|
||||
totalDestruct++;
|
||||
}
|
||||
|
||||
ExecutorNode * Executor::createNode(Command*command)
|
||||
{return pImpl->createNode(command);}
|
||||
|
||||
void Executor::execute(ExecutorNode *node) {pImpl->execute(node);}
|
||||
|
||||
}}
|
||||
37
pvDataApp/misc/executor.h
Normal file
37
pvDataApp/misc/executor.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* executor.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef EXECUTOR_H
|
||||
#define EXECUTOR_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "noDefaultMethods.h"
|
||||
#include "pvType.h"
|
||||
#include "thread.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
// This is created by Executor.createNode and passed to Executor.execute
|
||||
class ExecutorNode;
|
||||
|
||||
class Command {
|
||||
public:
|
||||
virtual void command() = 0;
|
||||
};
|
||||
|
||||
class Executor : private NoDefaultMethods {
|
||||
public:
|
||||
Executor(String threadName,ThreadPriority priority);
|
||||
~Executor();
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
ExecutorNode * createNode(Command *command);
|
||||
void execute(ExecutorNode *node);
|
||||
private:
|
||||
class ExecutorPvt *pImpl;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* EXECUTOR_H */
|
||||
88
pvDataApp/misc/linkedList.h
Normal file
88
pvDataApp/misc/linkedList.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/* linkedList.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef LINKEDLIST_H
|
||||
#define LINKEDLIST_H
|
||||
#include "linkedListVoid.h"
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
template <typename T>
|
||||
class LinkedList;
|
||||
|
||||
template <typename T>
|
||||
class LinkedListNode : private LinkedListVoidNode {
|
||||
public:
|
||||
LinkedListNode(T *object) : LinkedListVoidNode(object){}
|
||||
~LinkedListNode() {}
|
||||
T *getObject() { return static_cast<T *>(LinkedListVoidNode::getObject());}
|
||||
bool isOnList() {return LinkedListVoidNode::isOnList();}
|
||||
friend class LinkedList<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class LinkedList : private LinkedListVoid {
|
||||
public:
|
||||
LinkedList() : LinkedListVoid() {}
|
||||
~LinkedList() {}
|
||||
int getLength() {return LinkedListVoid::getLength();}
|
||||
void addTail(LinkedListNode<T> *listNode)
|
||||
{
|
||||
LinkedListVoid::addTail(static_cast<LinkedListVoidNode *>(listNode));
|
||||
}
|
||||
void addHead(LinkedListNode<T> *listNode)
|
||||
{
|
||||
LinkedListVoid::addHead(static_cast<LinkedListVoidNode *>(listNode));
|
||||
}
|
||||
void insertAfter(LinkedListNode<T> *listNode,
|
||||
LinkedListNode<T> *addNode)
|
||||
{
|
||||
LinkedListVoid::insertAfter(
|
||||
static_cast<LinkedListVoidNode *>(listNode),
|
||||
static_cast<LinkedListVoidNode *>(addNode));
|
||||
}
|
||||
void insertBefore(LinkedListNode<T> *listNode,
|
||||
LinkedListNode<T> *addNode)
|
||||
{
|
||||
LinkedListVoid::insertBefore(
|
||||
static_cast<LinkedListVoidNode *>(listNode),
|
||||
static_cast<LinkedListVoidNode *>(addNode));
|
||||
}
|
||||
LinkedListNode<T> *removeTail(){
|
||||
return static_cast<LinkedListNode<T> *>(LinkedListVoid::removeTail());
|
||||
}
|
||||
LinkedListNode<T> *removeHead(){
|
||||
return static_cast<LinkedListNode<T> *>(LinkedListVoid::removeHead());
|
||||
}
|
||||
void remove(LinkedListNode<T> *listNode){
|
||||
LinkedListVoid::remove(static_cast<LinkedListVoidNode *>(listNode));
|
||||
}
|
||||
void remove(T *object){
|
||||
LinkedListVoid::remove(object);
|
||||
}
|
||||
LinkedListNode<T> *getHead(){
|
||||
return static_cast<LinkedListNode<T> *>(LinkedListVoid::getHead());
|
||||
}
|
||||
LinkedListNode<T> *getTail(){
|
||||
return static_cast<LinkedListNode<T> *>(LinkedListVoid::getTail());
|
||||
}
|
||||
LinkedListNode<T> *getNext(LinkedListNode<T> *listNode){
|
||||
return static_cast<LinkedListNode<T> *>(LinkedListVoid::getNext(
|
||||
static_cast<LinkedListVoidNode *>(listNode)));
|
||||
}
|
||||
LinkedListNode<T> *getPrev(LinkedListNode<T> *listNode){
|
||||
return static_cast<LinkedListNode<T> *>(LinkedListVoid::getPrev(
|
||||
static_cast<LinkedListVoidNode *>(listNode)));
|
||||
}
|
||||
bool isEmpty() { return LinkedListVoid::isEmpty();}
|
||||
bool contains(T *object) { return LinkedListVoid::contains(object);}
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
#endif /* LINKEDLIST_H */
|
||||
|
||||
|
||||
|
||||
300
pvDataApp/misc/linkedListVoid.cpp
Normal file
300
pvDataApp/misc/linkedListVoid.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
/* linkedListVoid.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "lock.h"
|
||||
#include "pvType.h"
|
||||
#include "linkedListVoid.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static volatile int64 totalNodeConstruct = 0;
|
||||
static volatile int64 totalNodeDestruct = 0;
|
||||
static volatile int64 totalListConstruct = 0;
|
||||
static volatile int64 totalListDestruct = 0;
|
||||
static Mutex *globalMutex = 0;
|
||||
static String alreadyOnList("already on list");
|
||||
|
||||
static int64 getTotalNodeConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalNodeConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalNodeDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalNodeDestruct;
|
||||
}
|
||||
|
||||
static int64 getTotalListConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalListConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalListDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalListDestruct;
|
||||
}
|
||||
|
||||
static ConstructDestructCallback *pCDCallbackLinkedListNode;
|
||||
static ConstructDestructCallback *pCDCallbackLinkedList;
|
||||
|
||||
static void initPvt()
|
||||
{
|
||||
static Mutex mutex = Mutex();
|
||||
Lock xx(&mutex);
|
||||
if(globalMutex==0) {
|
||||
globalMutex = new Mutex();
|
||||
pCDCallbackLinkedListNode = new ConstructDestructCallback(
|
||||
"linkedListNode",
|
||||
getTotalNodeConstruct,getTotalNodeDestruct,0);
|
||||
|
||||
pCDCallbackLinkedList = new ConstructDestructCallback(
|
||||
"linkedList",
|
||||
getTotalListConstruct,getTotalListDestruct,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LinkedListVoidNode::LinkedListVoidNode(void *object)
|
||||
: object(object),before(0),after(0),linkedListVoid(0)
|
||||
{
|
||||
initPvt();
|
||||
Lock xx(globalMutex);
|
||||
totalNodeConstruct++;
|
||||
}
|
||||
|
||||
LinkedListVoidNode::LinkedListVoidNode(bool isHead)
|
||||
: object(this),before(this),after(this)
|
||||
{
|
||||
initPvt();
|
||||
Lock xx(globalMutex);
|
||||
totalNodeConstruct++;
|
||||
}
|
||||
|
||||
|
||||
LinkedListVoidNode::~LinkedListVoidNode()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
totalNodeDestruct++;
|
||||
}
|
||||
|
||||
ConstructDestructCallback *LinkedListVoidNode::getConstructDestructCallback()
|
||||
{
|
||||
initPvt();
|
||||
return pCDCallbackLinkedListNode;
|
||||
}
|
||||
|
||||
void *LinkedListVoidNode::getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
bool LinkedListVoidNode::isOnList()
|
||||
{
|
||||
if(before==0 && after==0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
LinkedListVoid::LinkedListVoid()
|
||||
: head(new LinkedListVoidNode(true)),length(0)
|
||||
{
|
||||
initPvt();
|
||||
Lock xx(globalMutex);
|
||||
totalListConstruct++;
|
||||
}
|
||||
|
||||
LinkedListVoid::~LinkedListVoid()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
delete head;
|
||||
totalListDestruct++;
|
||||
}
|
||||
|
||||
ConstructDestructCallback *LinkedListVoid::getConstructDestructCallback()
|
||||
{
|
||||
initPvt();
|
||||
return pCDCallbackLinkedList;
|
||||
}
|
||||
|
||||
int LinkedListVoid::getLength()
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
void LinkedListVoid::addTail(LinkedListVoidNode *node)
|
||||
{
|
||||
if(node->before!=0 || node->after!=0) {
|
||||
throw std::logic_error(alreadyOnList);
|
||||
}
|
||||
node->linkedListVoid = this;
|
||||
node->before = head->before;
|
||||
node->after = head;
|
||||
head->before->after = node;
|
||||
head->before = node;
|
||||
++length;
|
||||
}
|
||||
|
||||
void LinkedListVoid::addHead(LinkedListVoidNode *node)
|
||||
{
|
||||
if(node->before!=0 || node->after!=0) {
|
||||
throw std::logic_error(alreadyOnList);
|
||||
}
|
||||
node->linkedListVoid = this;
|
||||
node->after = head->after;
|
||||
node->before = head;
|
||||
head->after->before = node;
|
||||
head->after = node;
|
||||
++length;
|
||||
}
|
||||
|
||||
void LinkedListVoid::insertAfter(LinkedListVoidNode *node,
|
||||
LinkedListVoidNode *addNode)
|
||||
{
|
||||
LinkedListVoidNode *existingNode = node;
|
||||
LinkedListVoidNode *newNode = addNode;
|
||||
if(existingNode->after==0 || existingNode->before==0) {
|
||||
throw std::logic_error(String("listNode not on list"));
|
||||
}
|
||||
if(newNode->before!=0 || newNode->after!=0) {
|
||||
throw std::logic_error(alreadyOnList);
|
||||
}
|
||||
if(node->linkedListVoid!=this) {
|
||||
throw std::logic_error(String("node not on this list"));
|
||||
}
|
||||
newNode->linkedListVoid = this;
|
||||
newNode->after = existingNode->after;
|
||||
newNode->before = existingNode;
|
||||
existingNode->after->before = newNode;
|
||||
existingNode->after = newNode;
|
||||
++length;
|
||||
}
|
||||
|
||||
void LinkedListVoid::insertBefore(LinkedListVoidNode *node,
|
||||
LinkedListVoidNode *addNode)
|
||||
{
|
||||
LinkedListVoidNode *existingNode = node;
|
||||
LinkedListVoidNode *newNode = addNode;
|
||||
if(existingNode->after==0 || existingNode->before==0) {
|
||||
throw std::logic_error(String("listNode not on list"));
|
||||
}
|
||||
if(newNode->before!=0 || newNode->after!=0) {
|
||||
throw std::logic_error(alreadyOnList);
|
||||
}
|
||||
if(node->linkedListVoid!=this) {
|
||||
throw std::logic_error(String("node not on this list"));
|
||||
}
|
||||
newNode->linkedListVoid = this;
|
||||
newNode->after = existingNode;
|
||||
newNode->before = existingNode->before;
|
||||
existingNode->before->after = newNode;
|
||||
existingNode->before = newNode;
|
||||
++length;
|
||||
}
|
||||
|
||||
LinkedListVoidNode *LinkedListVoid::removeTail()
|
||||
{
|
||||
if(head->after==head) return 0;
|
||||
LinkedListVoidNode *node = head->before;
|
||||
remove(head->before);
|
||||
return node;
|
||||
}
|
||||
|
||||
LinkedListVoidNode *LinkedListVoid::removeHead()
|
||||
{
|
||||
if(head->after==head) return 0;
|
||||
LinkedListVoidNode *node = head->after;
|
||||
remove(head->after);
|
||||
return node;
|
||||
}
|
||||
|
||||
void LinkedListVoid::remove(LinkedListVoidNode *node)
|
||||
{
|
||||
if(node->before==0 || node->after==0) {
|
||||
throw std::logic_error(String("node not on list"));
|
||||
}
|
||||
if(node->linkedListVoid!=this) {
|
||||
throw std::logic_error(String("node not on this list"));
|
||||
}
|
||||
node->linkedListVoid = 0;
|
||||
LinkedListVoidNode *prev = node->before;
|
||||
LinkedListVoidNode *next = node->after;
|
||||
node->after = node->before = 0;
|
||||
prev->after = next;
|
||||
next->before = prev;
|
||||
length--;
|
||||
}
|
||||
|
||||
void LinkedListVoid::remove(void * object)
|
||||
{
|
||||
LinkedListVoidNode *node = getHead();
|
||||
while(node!=0) {
|
||||
if(node->getObject()==object) {
|
||||
remove(node);
|
||||
return;
|
||||
}
|
||||
node = getNext(node);
|
||||
}
|
||||
throw std::logic_error(String("object not on this list"));
|
||||
}
|
||||
|
||||
LinkedListVoidNode *LinkedListVoid::getHead()
|
||||
{
|
||||
if(head->after==head) return 0;
|
||||
return head->after;
|
||||
}
|
||||
|
||||
LinkedListVoidNode *LinkedListVoid::getTail()
|
||||
{
|
||||
if(head->after==head) return 0;
|
||||
return head->before;
|
||||
}
|
||||
|
||||
LinkedListVoidNode *LinkedListVoid::getNext(LinkedListVoidNode *listNode)
|
||||
{
|
||||
if(listNode->linkedListVoid!=this) {
|
||||
throw std::logic_error(String("node not on this list"));
|
||||
}
|
||||
if(listNode->after==head) return 0;
|
||||
return listNode->after;
|
||||
}
|
||||
|
||||
LinkedListVoidNode *LinkedListVoid::getPrev(LinkedListVoidNode *listNode)
|
||||
{
|
||||
if(listNode->linkedListVoid!=this) {
|
||||
throw std::logic_error(String("node not on this list"));
|
||||
}
|
||||
if(listNode->before==head) return 0;
|
||||
return listNode->before;
|
||||
}
|
||||
|
||||
bool LinkedListVoid::isEmpty()
|
||||
{
|
||||
if(head->after==head) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinkedListVoid::contains(void * object)
|
||||
{
|
||||
LinkedListVoidNode *node = getHead();
|
||||
while(node!=0) {
|
||||
if(node->getObject()==object) {
|
||||
return true;
|
||||
}
|
||||
node = getNext(node);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}}
|
||||
73
pvDataApp/misc/linkedListVoid.h
Normal file
73
pvDataApp/misc/linkedListVoid.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* linkedListVoid.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include "pvType.h"
|
||||
#include "showConstructDestruct.h"
|
||||
#ifndef LINKEDLISTVOID_H
|
||||
#define LINKEDLISTVOID_H
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class LinkedListVoid;
|
||||
class LinkedListVoidNode;
|
||||
|
||||
class LinkedListVoidNode {
|
||||
public:
|
||||
~LinkedListVoidNode();
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
void *getObject();
|
||||
bool isOnList();
|
||||
protected:
|
||||
LinkedListVoidNode(void *object);
|
||||
private:
|
||||
LinkedListVoidNode(bool isHead);
|
||||
friend class LinkedListVoid;
|
||||
void *object;
|
||||
LinkedListVoidNode *before;
|
||||
LinkedListVoidNode *after;
|
||||
LinkedListVoid *linkedListVoid;
|
||||
// do not implement the following
|
||||
LinkedListVoidNode(const LinkedListVoidNode&);
|
||||
LinkedListVoidNode & operator=(const LinkedListVoidNode&);
|
||||
};
|
||||
|
||||
class LinkedListVoid {
|
||||
public:
|
||||
~LinkedListVoid();
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
int getLength();
|
||||
void addTail(LinkedListVoidNode *listNode);
|
||||
void addHead(LinkedListVoidNode *listNode);
|
||||
void insertAfter(LinkedListVoidNode *listNode,
|
||||
LinkedListVoidNode *addNode);
|
||||
void insertBefore(LinkedListVoidNode *listNode,
|
||||
LinkedListVoidNode *addNode);
|
||||
LinkedListVoidNode *removeTail();
|
||||
LinkedListVoidNode *removeHead();
|
||||
void remove(LinkedListVoidNode *listNode);
|
||||
void remove(void * object);
|
||||
LinkedListVoidNode *getHead();
|
||||
LinkedListVoidNode *getTail();
|
||||
LinkedListVoidNode *getNext(LinkedListVoidNode *listNode);
|
||||
LinkedListVoidNode *getPrev(LinkedListVoidNode *listNode);
|
||||
bool isEmpty();
|
||||
bool contains(void * object);
|
||||
protected:
|
||||
LinkedListVoid();
|
||||
private:
|
||||
friend class LinkedListVoidNode;
|
||||
LinkedListVoidNode *head;
|
||||
int length;
|
||||
// do not implement the following
|
||||
LinkedListVoid(const LinkedListVoid&);
|
||||
LinkedListVoid & operator=(const LinkedListVoid&);
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* LINKEDLISTVOID_H */
|
||||
|
||||
|
||||
|
||||
38
pvDataApp/misc/lock.h
Normal file
38
pvDataApp/misc/lock.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* lock.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef LOCK_H
|
||||
#define LOCK_H
|
||||
#include <epicsMutex.h>
|
||||
#include "noDefaultMethods.h"
|
||||
/* This is based on item 14 of
|
||||
* Effective C++, Third Edition, Scott Meyers
|
||||
*/
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class Mutex {
|
||||
public:
|
||||
Mutex() : id(epicsMutexMustCreate()){}
|
||||
~Mutex() { epicsMutexDestroy(id) ;}
|
||||
void lock(){epicsMutexMustLock(id);}
|
||||
void unlock(){epicsMutexUnlock(id);}
|
||||
private:
|
||||
epicsMutexId id;
|
||||
};
|
||||
|
||||
|
||||
class Lock : private NoDefaultMethods {
|
||||
public:
|
||||
explicit Lock(Mutex *pm)
|
||||
: mutexPtr(pm)
|
||||
{mutexPtr->lock();}
|
||||
~Lock(){mutexPtr->unlock();}
|
||||
private:
|
||||
Mutex *mutexPtr;
|
||||
};
|
||||
}}
|
||||
#endif /* LOCK_H */
|
||||
166
pvDataApp/misc/messageQueue.cpp
Normal file
166
pvDataApp/misc/messageQueue.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/* messageQueue.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "pvType.h"
|
||||
#include "lock.h"
|
||||
#include "requester.h"
|
||||
#include "noDefaultMethods.h"
|
||||
#include "showConstructDestruct.h"
|
||||
#include "queue.h"
|
||||
#include "messageQueue.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static volatile int64 totalQueueConstruct = 0;
|
||||
static volatile int64 totalQueueDestruct = 0;
|
||||
static Mutex *globalMutex = 0;
|
||||
|
||||
static int64 getTotalQueueConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalQueueConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalQueueDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalQueueDestruct;
|
||||
}
|
||||
|
||||
static ConstructDestructCallback *pCDCallbackQueue;
|
||||
|
||||
static void initPvt()
|
||||
{
|
||||
static Mutex mutex = Mutex();
|
||||
Lock xx(&mutex);
|
||||
if(globalMutex==0) {
|
||||
globalMutex = new Mutex();
|
||||
pCDCallbackQueue = new ConstructDestructCallback(
|
||||
"messageQueue",
|
||||
getTotalQueueConstruct,getTotalQueueDestruct,0);
|
||||
}
|
||||
}
|
||||
|
||||
typedef MessageNode * MessageNodePtr;
|
||||
typedef QueueElement<MessageNode> MessageElement;
|
||||
typedef MessageElement *MessageElementPtr;
|
||||
typedef Queue<MessageNode> MessageNodeQueue;
|
||||
|
||||
|
||||
MessageNode::MessageNode()
|
||||
: message(String("")),messageType(infoMessage){}
|
||||
|
||||
MessageNode::~MessageNode() {}
|
||||
|
||||
String MessageNode::getMessage() const { return message;};
|
||||
|
||||
MessageType MessageNode::getMessageType() const { return messageType;}
|
||||
|
||||
|
||||
class MessageQueuePvt {
|
||||
public:
|
||||
MessageNodePtr *messageNodeArray;
|
||||
MessageNodeQueue *queue;
|
||||
MessageNodePtr lastPut;
|
||||
MessageElementPtr lastGet;
|
||||
int size;
|
||||
int overrun;
|
||||
};
|
||||
|
||||
MessageQueue::MessageQueue(int size)
|
||||
: pImpl(new MessageQueuePvt)
|
||||
{
|
||||
initPvt();
|
||||
Lock xx(globalMutex);
|
||||
totalQueueConstruct++;
|
||||
pImpl->size = size;
|
||||
pImpl->overrun = 0;
|
||||
pImpl->lastPut = 0;
|
||||
pImpl->lastGet = 0;
|
||||
pImpl->messageNodeArray = new MessageNodePtr[size];
|
||||
for(int i=0; i<size; i++) {
|
||||
pImpl->messageNodeArray[i] = new MessageNode();
|
||||
}
|
||||
pImpl->queue = new MessageNodeQueue(pImpl->messageNodeArray,size);
|
||||
}
|
||||
|
||||
MessageQueue::~MessageQueue()
|
||||
{
|
||||
delete pImpl->queue;
|
||||
for(int i=0; i< pImpl->size; i++) {
|
||||
delete pImpl->messageNodeArray[i];
|
||||
}
|
||||
delete[] pImpl->messageNodeArray;
|
||||
initPvt();
|
||||
Lock xx(globalMutex);
|
||||
totalQueueDestruct++;
|
||||
}
|
||||
|
||||
MessageNode *MessageQueue::get() {
|
||||
if(pImpl->lastGet!=0) {
|
||||
throw std::logic_error(
|
||||
String("MessageQueue::get() but did not release last"));
|
||||
}
|
||||
MessageElementPtr element = pImpl->queue->getUsed();
|
||||
if(element==0) return 0;
|
||||
pImpl->lastGet = element;
|
||||
return element->getObject();
|
||||
}
|
||||
|
||||
void MessageQueue::release() {
|
||||
if(pImpl->lastGet==0) return;
|
||||
pImpl->queue->releaseUsed(pImpl->lastGet);
|
||||
pImpl->lastGet = 0;
|
||||
}
|
||||
|
||||
bool MessageQueue::put(String message,MessageType messageType,bool replaceLast)
|
||||
{
|
||||
MessageElementPtr element = pImpl->queue->getFree();
|
||||
if(element!=0) {
|
||||
MessageNodePtr node = element->getObject();
|
||||
node->message = message;
|
||||
node->messageType = messageType;
|
||||
pImpl->lastPut = node;
|
||||
pImpl->queue->setUsed(element);
|
||||
return true;
|
||||
}
|
||||
pImpl->overrun++;
|
||||
if(replaceLast) {
|
||||
MessageNodePtr node = pImpl->lastPut;
|
||||
node->message = message;
|
||||
node->messageType = messageType;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageQueue::isEmpty() const
|
||||
{
|
||||
int free = pImpl->queue->getNumberFree();
|
||||
if(free==pImpl->size) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageQueue::isFull() const
|
||||
{
|
||||
if(pImpl->queue->getNumberFree()==0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int MessageQueue::getClearOverrun()
|
||||
{
|
||||
int num = pImpl->overrun;
|
||||
pImpl->overrun = 0;
|
||||
return num;
|
||||
}
|
||||
|
||||
}}
|
||||
49
pvDataApp/misc/messageQueue.h
Normal file
49
pvDataApp/misc/messageQueue.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* messageQueue.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef MESSAGEQUEUE_H
|
||||
#define MESSAGEQUEUE_H
|
||||
#include "pvType.h"
|
||||
#include "requester.h"
|
||||
#include "noDefaultMethods.h"
|
||||
#include "showConstructDestruct.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class MessageNode {
|
||||
public:
|
||||
String getMessage() const;
|
||||
MessageType getMessageType() const;
|
||||
private:
|
||||
MessageNode();
|
||||
~MessageNode();
|
||||
friend class MessageQueue;
|
||||
String message;
|
||||
MessageType messageType;
|
||||
};
|
||||
|
||||
class MessageQueue : private NoDefaultMethods {
|
||||
public:
|
||||
MessageQueue(int size);
|
||||
~MessageQueue();
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
MessageNode *get();
|
||||
// must call release before next get
|
||||
void release();
|
||||
// return (false,true) if message (was not, was) put into queue
|
||||
bool put(String message,MessageType messageType,bool replaceLast);
|
||||
bool isEmpty() const;
|
||||
bool isFull() const;
|
||||
int getClearOverrun();
|
||||
private:
|
||||
class MessageQueuePvt *pImpl;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* MESSAGEQUEUE_H */
|
||||
|
||||
|
||||
|
||||
27
pvDataApp/misc/noDefaultMethods.h
Normal file
27
pvDataApp/misc/noDefaultMethods.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* noDefaultMethods.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef NO_DEFAULT_METHODS_H
|
||||
#define NO_DEFAULT_METHODS_H
|
||||
namespace epics { namespace pvData {
|
||||
/* This is based on Item 6 of
|
||||
* Effective C++, Third Edition, Scott Meyers
|
||||
*/
|
||||
|
||||
|
||||
class NoDefaultMethods {
|
||||
protected:
|
||||
// allow by derived objects
|
||||
NoDefaultMethods(){};
|
||||
~NoDefaultMethods(){}
|
||||
private:
|
||||
// do not implment
|
||||
NoDefaultMethods(const NoDefaultMethods&);
|
||||
NoDefaultMethods & operator=(const NoDefaultMethods &);
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* NO_DEFAULT_METHODS_H */
|
||||
28
pvDataApp/misc/pvType.h
Normal file
28
pvDataApp/misc/pvType.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* pvType.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <string>
|
||||
#ifndef PVTYPE_H
|
||||
#define PVTYPE_H
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
typedef signed char int8;
|
||||
typedef short int16;
|
||||
typedef int int32;
|
||||
typedef long long int64;
|
||||
typedef unsigned int uint32;
|
||||
typedef unsigned long long uint64;
|
||||
|
||||
typedef std::string String;
|
||||
typedef std::string * StringBuilder;
|
||||
typedef String* StringArray;
|
||||
|
||||
}}
|
||||
#endif /* PVTYPE_H */
|
||||
|
||||
|
||||
|
||||
54
pvDataApp/misc/queue.h
Normal file
54
pvDataApp/misc/queue.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* queue.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef QUEUE_H
|
||||
#define QUEUE_H
|
||||
#include "queueVoid.h"
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
template <typename T>
|
||||
class Queue;
|
||||
|
||||
template <typename T>
|
||||
class QueueElement;
|
||||
|
||||
template <typename T>
|
||||
class QueueElement : private QueueElementVoid {
|
||||
public:
|
||||
T *getObject() { return static_cast<T *>(QueueElementVoid::getObject());}
|
||||
protected:
|
||||
QueueElement(T *object) : QueueElementVoid(static_cast<void *>(object)){}
|
||||
~QueueElement() {}
|
||||
friend class Queue<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Queue : private QueueVoid {
|
||||
public:
|
||||
Queue(T *array[],int number)
|
||||
: QueueVoid((ObjectPtr*)array,number)
|
||||
//: QueueVoid(static_cast<ObjectPtr*>(array),number)
|
||||
{}
|
||||
~Queue() {}
|
||||
void clear() {QueueVoid::clear();}
|
||||
int getNumberFree() {return QueueVoid::getNumberFree();}
|
||||
int capacity() {return QueueVoid::capacity();}
|
||||
QueueElement<T> *getFree() {
|
||||
return static_cast<QueueElement<T> *>(QueueVoid::getFree());}
|
||||
void setUsed(QueueElement<T> *queueElement) {
|
||||
QueueVoid::setUsed(static_cast<QueueElementVoid *>(queueElement));}
|
||||
QueueElement<T> *getUsed() {
|
||||
return static_cast<QueueElement<T> *>(QueueVoid::getUsed());}
|
||||
void releaseUsed(QueueElement<T> *queueElement) {
|
||||
QueueVoid::releaseUsed(static_cast<QueueElementVoid *>(queueElement));}
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
#endif /* QUEUE_H */
|
||||
|
||||
|
||||
|
||||
184
pvDataApp/misc/queueVoid.cpp
Normal file
184
pvDataApp/misc/queueVoid.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/* queueVoid.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "lock.h"
|
||||
#include "pvType.h"
|
||||
#include "queueVoid.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static volatile int64 totalElementConstruct = 0;
|
||||
static volatile int64 totalElementDestruct = 0;
|
||||
static volatile int64 totalQueueConstruct = 0;
|
||||
static volatile int64 totalQueueDestruct = 0;
|
||||
static Mutex *globalMutex = 0;
|
||||
|
||||
static int64 getTotalNodeConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalElementConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalNodeDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalElementDestruct;
|
||||
}
|
||||
|
||||
static int64 getTotalListConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalQueueConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalListDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalQueueDestruct;
|
||||
}
|
||||
|
||||
static ConstructDestructCallback *pCDCallbackQueueNode;
|
||||
static ConstructDestructCallback *pCDCallbackQueue;
|
||||
|
||||
static void initPvt()
|
||||
{
|
||||
static Mutex mutex = Mutex();
|
||||
Lock xx(&mutex);
|
||||
if(globalMutex==0) {
|
||||
globalMutex = new Mutex();
|
||||
pCDCallbackQueueNode = new ConstructDestructCallback(
|
||||
"queueElement",
|
||||
getTotalNodeConstruct,getTotalNodeDestruct,0);
|
||||
|
||||
pCDCallbackQueue = new ConstructDestructCallback(
|
||||
"queue",
|
||||
getTotalListConstruct,getTotalListDestruct,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QueueElementVoid::QueueElementVoid(ObjectPtr object)
|
||||
: object(object)
|
||||
{
|
||||
initPvt();
|
||||
Lock xx(globalMutex);
|
||||
totalElementConstruct++;
|
||||
}
|
||||
|
||||
|
||||
QueueElementVoid::~QueueElementVoid()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
totalElementDestruct++;
|
||||
}
|
||||
|
||||
ConstructDestructCallback *QueueElementVoid::getConstructDestructCallback()
|
||||
{
|
||||
initPvt();
|
||||
return pCDCallbackQueueNode;
|
||||
}
|
||||
|
||||
ObjectPtr QueueElementVoid::getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QueueVoid::QueueVoid(ObjectPtr object[],int number)
|
||||
: array(new QueueElementVoidPtr[number]),number(number),
|
||||
numberFree(number),numberUsed(0),
|
||||
nextGetFree(0),nextSetUsed(),
|
||||
nextGetUsed(0),nextReleaseUsed(0)
|
||||
{
|
||||
for(int i=0; i<number; i++) {
|
||||
array[i] = new QueueElementVoid(object[i]);
|
||||
}
|
||||
initPvt();
|
||||
Lock xx(globalMutex);
|
||||
totalQueueConstruct++;
|
||||
}
|
||||
|
||||
QueueVoid::~QueueVoid()
|
||||
{
|
||||
for(int i=0; i<number; i++) {
|
||||
delete array[i];
|
||||
}
|
||||
delete[]array;
|
||||
Lock xx(globalMutex);
|
||||
totalQueueDestruct++;
|
||||
}
|
||||
|
||||
ConstructDestructCallback *QueueVoid::getConstructDestructCallback()
|
||||
{
|
||||
initPvt();
|
||||
return pCDCallbackQueue;
|
||||
}
|
||||
|
||||
void QueueVoid::clear()
|
||||
{
|
||||
numberFree = number;
|
||||
numberUsed = 0;
|
||||
nextGetFree = 0;
|
||||
nextSetUsed = 0;
|
||||
nextGetUsed = 0;
|
||||
nextReleaseUsed = 0;
|
||||
}
|
||||
|
||||
int QueueVoid::getNumberFree()
|
||||
{
|
||||
return numberFree;
|
||||
}
|
||||
|
||||
int QueueVoid::capacity()
|
||||
{
|
||||
return number;
|
||||
}
|
||||
|
||||
QueueElementVoid * QueueVoid::getFree()
|
||||
{
|
||||
if(numberFree==0) return 0;
|
||||
numberFree--;
|
||||
QueueElementVoid *queueElement = array[nextGetFree++];
|
||||
if(nextGetFree>=number) nextGetFree = 0;
|
||||
return queueElement;
|
||||
}
|
||||
|
||||
void QueueVoid::setUsed(QueueElementVoid *queueElement)
|
||||
{
|
||||
if(queueElement!=array[nextSetUsed++]) {
|
||||
throw std::logic_error(String("not correcect queueElement"));
|
||||
}
|
||||
numberUsed++;
|
||||
if(nextSetUsed>=number) nextSetUsed = 0;
|
||||
}
|
||||
|
||||
QueueElementVoid * QueueVoid::getUsed()
|
||||
{
|
||||
if(numberUsed==0) return 0;
|
||||
QueueElementVoid *queueElement = array[nextGetUsed++];
|
||||
if(nextGetUsed>=number) nextGetUsed = 0;
|
||||
return queueElement;
|
||||
}
|
||||
|
||||
void QueueVoid::releaseUsed(QueueElementVoid *queueElement)
|
||||
{
|
||||
if(queueElement!=array[nextReleaseUsed++]) {
|
||||
throw std::logic_error(String(
|
||||
"not queueElement returned by last call to getUsed"));
|
||||
}
|
||||
if(nextReleaseUsed>=number) nextReleaseUsed = 0;
|
||||
numberUsed--;
|
||||
numberFree++;
|
||||
}
|
||||
|
||||
}}
|
||||
61
pvDataApp/misc/queueVoid.h
Normal file
61
pvDataApp/misc/queueVoid.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* queueVoid.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include "showConstructDestruct.h"
|
||||
#ifndef QUEUEVOID_H
|
||||
#define QUEUEVOID_H
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class QueueVoid;
|
||||
class QueueElementVoid;
|
||||
|
||||
typedef void * ObjectPtr;
|
||||
typedef QueueElementVoid * QueueElementVoidPtr;
|
||||
typedef QueueElementVoidPtr * QueueElementVoidPtrArray;
|
||||
|
||||
class QueueElementVoid {
|
||||
public:
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
protected:
|
||||
ObjectPtr getObject();
|
||||
QueueElementVoid(ObjectPtr object);
|
||||
~QueueElementVoid();
|
||||
ObjectPtr object;
|
||||
friend class QueueVoid;
|
||||
};
|
||||
|
||||
|
||||
class QueueVoid {
|
||||
public:
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
protected:
|
||||
QueueVoid(ObjectPtr array[],int number);
|
||||
~QueueVoid();
|
||||
void clear();
|
||||
int getNumberFree();
|
||||
int capacity();
|
||||
QueueElementVoidPtr getFree();
|
||||
void setUsed(QueueElementVoid *queueElement);
|
||||
QueueElementVoid *getUsed();
|
||||
void releaseUsed(QueueElementVoid *queueElement);
|
||||
private:
|
||||
friend class QueueElementVoid;
|
||||
QueueElementVoidPtrArray array;
|
||||
int number;
|
||||
int numberFree;
|
||||
int numberUsed;
|
||||
int nextGetFree;
|
||||
int nextSetUsed;
|
||||
int nextGetUsed;
|
||||
int nextReleaseUsed;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
#endif /* QUEUEVOID_H */
|
||||
|
||||
|
||||
|
||||
20
pvDataApp/misc/requester.cpp
Normal file
20
pvDataApp/misc/requester.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
/* requester.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <string>
|
||||
#include "requester.h"
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static std::string typeName[] = {
|
||||
String("info"),
|
||||
String("warning"),
|
||||
String("error"),
|
||||
String("fatalError")
|
||||
};
|
||||
|
||||
StringArray messageTypeName = typeName;
|
||||
|
||||
}}
|
||||
28
pvDataApp/misc/requester.h
Normal file
28
pvDataApp/misc/requester.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* requester.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <string>
|
||||
#ifndef REQUESTER_H
|
||||
#define REQUESTER_H
|
||||
#include "pvType.h"
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class Requester;
|
||||
|
||||
enum MessageType {
|
||||
infoMessage,warningMessage,errorMessage,fatalErrorMessage
|
||||
};
|
||||
|
||||
extern StringArray messageTypeName;
|
||||
|
||||
class Requester {
|
||||
public:
|
||||
virtual String getRequesterName() = 0;
|
||||
virtual void message(String message,MessageType messageType) = 0;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* REQUESTER_H */
|
||||
54
pvDataApp/misc/serialize.h
Normal file
54
pvDataApp/misc/serialize.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* serialize.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef SERIALIZE_H
|
||||
#define SERIALIZE_H
|
||||
#include "bitSet.h"
|
||||
#include "byteBuffer.h"
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class SerializableControl;
|
||||
class DeserializableControl;
|
||||
class Serializable;
|
||||
class BitSetSerializable;
|
||||
class SerializableArray;
|
||||
|
||||
class SerializableControl {
|
||||
public:
|
||||
virtual void flushSerializeBuffer() =0;
|
||||
virtual void ensureBuffer(int size) =0;
|
||||
};
|
||||
|
||||
class DeserializableControl {
|
||||
public:
|
||||
virtual void ensureData(int size) =0;
|
||||
};
|
||||
|
||||
class Serializable {
|
||||
public:
|
||||
virtual void serialize(ByteBuffer *buffer,
|
||||
SerializableControl *flusher) = 0;
|
||||
virtual void deserialize(ByteBuffer *buffer,
|
||||
DeserializableControl *flusher) = 0;
|
||||
};
|
||||
|
||||
class BitSetSerializable {
|
||||
public:
|
||||
virtual void serialize(ByteBuffer *buffer,
|
||||
SerializableControl *flusher,BitSet *bitSet) = 0;
|
||||
virtual void deserialize(ByteBuffer *buffer,
|
||||
DeserializableControl *flusher,BitSet *bitSet) = 0;
|
||||
};
|
||||
|
||||
|
||||
class SerializableArray : public Serializable {
|
||||
public:
|
||||
virtual void serialize(ByteBuffer *buffer,
|
||||
SerializableControl *flusher, int offset, int count) = 0;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* SERIALIZE_H */
|
||||
125
pvDataApp/misc/serializeHelper.cpp
Normal file
125
pvDataApp/misc/serializeHelper.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/*
|
||||
* serializeHelper.cpp
|
||||
*
|
||||
* Created on: Oct 22, 2010
|
||||
* Author: Miha vitorovic
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <pvType.h>
|
||||
|
||||
#include "epicsException.h"
|
||||
#include "byteBuffer.h"
|
||||
#include "serializeHelper.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace epics {
|
||||
namespace pvData {
|
||||
|
||||
void SerializeHelper::writeSize(int s, ByteBuffer* buffer,
|
||||
SerializableControl* flusher) {
|
||||
flusher->ensureBuffer(sizeof(int64)+1);
|
||||
SerializeHelper::writeSize(s, buffer);
|
||||
}
|
||||
|
||||
void SerializeHelper::writeSize(int s, ByteBuffer* buffer) {
|
||||
if(s==-1) // null
|
||||
buffer->putByte(-1);
|
||||
else if(s<254)
|
||||
buffer->putByte(s);
|
||||
else
|
||||
buffer->putByte(-2)->putInt(s); // (byte)-2 + size
|
||||
}
|
||||
|
||||
int SerializeHelper::readSize(ByteBuffer* buffer,
|
||||
DeserializableControl* control) {
|
||||
control->ensureData(1);
|
||||
int8 b = buffer->getByte();
|
||||
if(b==-1)
|
||||
return -1;
|
||||
else if(b==-2) {
|
||||
control->ensureData(sizeof(int32));
|
||||
int32 s = buffer->getInt();
|
||||
if(s<0) throw EpicsException("negative array size");
|
||||
return s;
|
||||
}
|
||||
else
|
||||
return (int)(b<0 ? b+256 : b);
|
||||
}
|
||||
|
||||
void SerializeHelper::serializeString(const String& value,
|
||||
ByteBuffer* buffer, SerializableControl* flusher) {
|
||||
int len = value.length();
|
||||
SerializeHelper::writeSize(len, buffer, flusher);
|
||||
int i = 0;
|
||||
while(true) {
|
||||
int maxToWrite = min(len-i, buffer->getRemaining());
|
||||
buffer->put(value.data(), i, maxToWrite); // UTF-8
|
||||
i += maxToWrite;
|
||||
if(i<len)
|
||||
flusher->flushSerializeBuffer();
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeHelper::serializeSubstring(const String& value,
|
||||
int offset, int count, ByteBuffer* buffer,
|
||||
SerializableControl* flusher) {
|
||||
if(offset<0)
|
||||
offset = 0;
|
||||
else if(offset>(int)value.length()) offset = value.length();
|
||||
|
||||
if(offset+count>(int)value.length()) count = value.length()-offset;
|
||||
|
||||
SerializeHelper::writeSize(count, buffer, flusher);
|
||||
int i = 0;
|
||||
while(true) {
|
||||
int maxToWrite = min(count-i, buffer->getRemaining());
|
||||
buffer->put(value.data(), offset+i, maxToWrite); // UTF-8
|
||||
i += maxToWrite;
|
||||
if(i<count)
|
||||
flusher->flushSerializeBuffer();
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String SerializeHelper::deserializeString(ByteBuffer* buffer,
|
||||
DeserializableControl* control) {
|
||||
|
||||
int size = SerializeHelper::readSize(buffer, control);
|
||||
if(size>=0) {
|
||||
char* retBuffer = new char[size]; // get the return buffer
|
||||
try {
|
||||
int i = 0;
|
||||
while(true) {
|
||||
int toRead = min(size-i, buffer->getRemaining());
|
||||
buffer->get(retBuffer, i, toRead);
|
||||
i += toRead;
|
||||
if(i<size)
|
||||
control->ensureData(1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
String s = String(retBuffer, size);
|
||||
delete retBuffer;
|
||||
return s;
|
||||
} catch(...) {
|
||||
delete retBuffer; // remove the buffer
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
return String("");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
103
pvDataApp/misc/serializeHelper.h
Normal file
103
pvDataApp/misc/serializeHelper.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
/*
|
||||
* serializeHelper.h
|
||||
*
|
||||
* Created on: Oct 21, 2010
|
||||
* Author: Miha Vitorovic
|
||||
*/
|
||||
|
||||
#ifndef SERIALIZEHELPER_H_
|
||||
#define SERIALIZEHELPER_H_
|
||||
|
||||
#include "serialize.h"
|
||||
#include "byteBuffer.h"
|
||||
#include "noDefaultMethods.h"
|
||||
#include "../pv/pvIntrospect.h"
|
||||
|
||||
namespace epics {
|
||||
namespace pvData {
|
||||
|
||||
class SerializeHelper : public NoDefaultMethods {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Serialize array size.
|
||||
*
|
||||
* @param[in] s size to encode
|
||||
* @param[in] buffer serialization buffer
|
||||
* @param[in] flusher flusher
|
||||
*/
|
||||
static void writeSize(int s, ByteBuffer* buffer,
|
||||
SerializableControl* flusher);
|
||||
|
||||
/**
|
||||
* Deserialize array size.
|
||||
*
|
||||
* @param[in] buffer deserialization buffer.
|
||||
* @returns array size.
|
||||
*/
|
||||
static int readSize(ByteBuffer* buffer,
|
||||
DeserializableControl* control);
|
||||
|
||||
/**
|
||||
* String serialization helper method.
|
||||
*
|
||||
* @param[in] value String to serialize
|
||||
* @param[in] buffer serialization buffer
|
||||
* @param[in] flusher flusher
|
||||
*/
|
||||
static void serializeString(const String& value, ByteBuffer* buffer,
|
||||
SerializableControl* flusher);
|
||||
|
||||
/**
|
||||
* String serialization helper method.
|
||||
*
|
||||
* @param[in] value String to serialize
|
||||
* @param[in] offset start of the substring in {@code value}
|
||||
* @param[in] count the number of characters to write
|
||||
* @param[in] buffer serialization buffer
|
||||
* @param[in] flusher flusher
|
||||
*/
|
||||
static void serializeSubstring(const String& value, int offset,
|
||||
int count, ByteBuffer* buffer,
|
||||
SerializableControl* flusher);
|
||||
|
||||
/**
|
||||
* String deserialization helper method.
|
||||
* TODO This method cannot return "null", but Java implementation
|
||||
* could have serialized "null" value as well. We need to decide
|
||||
* how to deserialize "null".
|
||||
*
|
||||
* @param[in] buffer deserialization buffer
|
||||
* @param[in] control control
|
||||
* @returns deserialized string
|
||||
*
|
||||
* @todo This method cannot return "null", but Java implementation
|
||||
* could have serialized "null" value as well. We need to decide
|
||||
* how to deserialize "null".
|
||||
*/
|
||||
static String deserializeString(ByteBuffer* buffer,
|
||||
DeserializableControl* control);
|
||||
|
||||
private:
|
||||
SerializeHelper() {};
|
||||
~SerializeHelper() {};
|
||||
|
||||
/**
|
||||
* Serialize array size.
|
||||
*
|
||||
* @param[in] s size to encode
|
||||
* @param[in] buffer serialization buffer
|
||||
*/
|
||||
static void writeSize(int s, ByteBuffer* buffer);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SERIALIZEHELPER_H_ */
|
||||
127
pvDataApp/misc/showConstructDestruct.cpp
Normal file
127
pvDataApp/misc/showConstructDestruct.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/* showConstructDestruct.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "noDefaultMethods.h"
|
||||
#include "lock.h"
|
||||
#include "pvType.h"
|
||||
#include "linkedList.h"
|
||||
#include "showConstructDestruct.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static ShowConstructDestruct *pShowConstructDestruct = 0;
|
||||
static Mutex *globalMutex = 0;
|
||||
typedef LinkedListNode<ConstructDestructCallback> ListNode;
|
||||
typedef LinkedList<ConstructDestructCallback> List;
|
||||
static List *list;
|
||||
|
||||
ConstructDestructCallback::ConstructDestructCallback(
|
||||
String name,
|
||||
getTotal construct,
|
||||
getTotal destruct,
|
||||
getTotal reference)
|
||||
: name(name), construct(construct), destruct(destruct) ,reference(reference)
|
||||
{
|
||||
getShowConstructDestruct()->registerCallback(this);
|
||||
}
|
||||
|
||||
ConstructDestructCallback::~ConstructDestructCallback() {}
|
||||
|
||||
String ConstructDestructCallback::getConstructName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
int64 ConstructDestructCallback::getTotalConstruct()
|
||||
{
|
||||
return construct();
|
||||
}
|
||||
|
||||
int64 ConstructDestructCallback:: getTotalDestruct()
|
||||
{
|
||||
return destruct();
|
||||
}
|
||||
|
||||
int64 ConstructDestructCallback::getTotalReferenceCount()
|
||||
{
|
||||
if(reference==0) return 0;
|
||||
return reference();
|
||||
}
|
||||
|
||||
|
||||
ShowConstructDestruct::ShowConstructDestruct() {}
|
||||
|
||||
void ShowConstructDestruct::constuctDestructTotals(FILE *fd)
|
||||
{
|
||||
getShowConstructDestruct(); // make it initialize
|
||||
Lock xx(globalMutex);
|
||||
ListNode *node = list->getHead();
|
||||
while(node!=0) {
|
||||
ConstructDestructCallback *callback = node->getObject();
|
||||
String name = callback->getConstructName();
|
||||
int64 reference = callback->getTotalReferenceCount();
|
||||
int64 construct = callback->getTotalConstruct();
|
||||
int64 destruct = callback->getTotalDestruct();
|
||||
fprintf(fd,"%s: totalConstruct %lli totalDestruct %lli",
|
||||
name.c_str(),construct,destruct);
|
||||
if(reference>0) fprintf(fd," totalReference %lli",reference);
|
||||
fprintf(fd,"\n");
|
||||
node = list->getNext(node);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowConstructDestruct::registerCallback(ConstructDestructCallback *callback)
|
||||
{
|
||||
static ConstructDestructCallback *listCallback = 0;
|
||||
static ConstructDestructCallback *listNodeCallback = 0;
|
||||
Lock xx(globalMutex);
|
||||
ListNode *listNode = 0;
|
||||
if(list==0) {
|
||||
if(callback->getConstructName().compare("linkedListNode")==0) {
|
||||
listNodeCallback = callback;
|
||||
} else if(callback->getConstructName().compare("linkedList")==0) {
|
||||
listCallback = callback;
|
||||
} else {
|
||||
throw std::logic_error(String("ShowConstructDestruct::registerCallback"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(listCallback!=0) {
|
||||
if(listNodeCallback==0) {
|
||||
throw std::logic_error(String(
|
||||
"ShowConstructDestruct::registerCallback expected listNodeCallback!=0"));
|
||||
}
|
||||
listNode = new ListNode(listNodeCallback);
|
||||
list->addTail(listNode);
|
||||
listNode = new ListNode(listCallback);
|
||||
list->addTail(listNode);
|
||||
listCallback = 0;
|
||||
listNodeCallback = 0;
|
||||
}
|
||||
listNode = new ListNode(callback);
|
||||
list->addTail(listNode);
|
||||
}
|
||||
|
||||
ShowConstructDestruct * getShowConstructDestruct()
|
||||
{
|
||||
static Mutex mutex = Mutex();
|
||||
Lock xx(&mutex);
|
||||
if(pShowConstructDestruct==0) {
|
||||
globalMutex = new Mutex();
|
||||
pShowConstructDestruct = new ShowConstructDestruct();
|
||||
list = new List();
|
||||
}
|
||||
return pShowConstructDestruct;
|
||||
}
|
||||
|
||||
}}
|
||||
53
pvDataApp/misc/showConstructDestruct.h
Normal file
53
pvDataApp/misc/showConstructDestruct.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* showConstructDestruct.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef SHOWCONSTRUCTDESTRUCT_H
|
||||
#define SHOWCONSTRUCTDESTRUCT_H
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "noDefaultMethods.h"
|
||||
#include "pvType.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
typedef int64 (*getTotal)();
|
||||
|
||||
class ConstructDestructCallback : private NoDefaultMethods {
|
||||
public:
|
||||
ConstructDestructCallback(
|
||||
String name,
|
||||
getTotal construct,
|
||||
getTotal destruct,
|
||||
getTotal reference);
|
||||
String getConstructName();
|
||||
int64 getTotalConstruct();
|
||||
int64 getTotalDestruct();
|
||||
int64 getTotalReferenceCount();
|
||||
private:
|
||||
~ConstructDestructCallback();
|
||||
String name;
|
||||
getTotal construct;
|
||||
getTotal destruct;
|
||||
getTotal reference;
|
||||
};
|
||||
|
||||
class ShowConstructDestruct : private NoDefaultMethods {
|
||||
public:
|
||||
void constuctDestructTotals(FILE *fd);
|
||||
void registerCallback(ConstructDestructCallback *callback);
|
||||
private:
|
||||
ShowConstructDestruct();
|
||||
friend ShowConstructDestruct* getShowConstructDestruct();
|
||||
};
|
||||
|
||||
extern ShowConstructDestruct* getShowConstructDestruct();
|
||||
|
||||
}}
|
||||
#endif /* SHOWCONSTRUCTDESTRUCT_H */
|
||||
210
pvDataApp/misc/thread.cpp
Normal file
210
pvDataApp/misc/thread.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
/* thread.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <epicsEvent.h>
|
||||
#include "lock.h"
|
||||
#include "event.h"
|
||||
#include "thread.h"
|
||||
#include "linkedList.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static unsigned int epicsPriority[] = {
|
||||
epicsThreadPriorityLow,
|
||||
epicsThreadPriorityLow + 15,
|
||||
epicsThreadPriorityMedium - 15,
|
||||
epicsThreadPriorityMedium,
|
||||
epicsThreadPriorityMedium + 15,
|
||||
epicsThreadPriorityHigh - 15,
|
||||
epicsThreadPriorityHigh
|
||||
};
|
||||
|
||||
unsigned int const * const ThreadPriorityFunc::getEpicsPriorities()
|
||||
{
|
||||
return epicsPriority;
|
||||
}
|
||||
|
||||
|
||||
static String threadPriorityNames[] = {
|
||||
String("lowest"),String("lower"),String("low"),
|
||||
String("middle"),
|
||||
String("high"),String("higher"),String("highest")
|
||||
};
|
||||
|
||||
class ThreadListElement;
|
||||
typedef LinkedListNode<ThreadListElement> ThreadListNode;
|
||||
typedef LinkedList<ThreadListElement> ThreadList;
|
||||
|
||||
static volatile int64 totalConstruct = 0;
|
||||
static volatile int64 totalDestruct = 0;
|
||||
static Mutex *globalMutex = 0;
|
||||
static ThreadList *threadList;
|
||||
|
||||
static int64 getTotalConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalDestruct;
|
||||
}
|
||||
|
||||
static ConstructDestructCallback *pConstructDestructCallback;
|
||||
|
||||
static void init()
|
||||
{
|
||||
static Mutex mutex = Mutex();
|
||||
Lock xx(&mutex);
|
||||
if(globalMutex==0) {
|
||||
globalMutex = new Mutex();
|
||||
threadList = new ThreadList();
|
||||
pConstructDestructCallback = new ConstructDestructCallback(
|
||||
String("thread"),
|
||||
getTotalConstruct,getTotalDestruct,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ThreadListElement {
|
||||
public:
|
||||
ThreadListElement(Thread *thread) : thread(thread),node(new ThreadListNode(this)){}
|
||||
~ThreadListElement(){delete node;}
|
||||
Thread *thread;
|
||||
ThreadListNode *node;
|
||||
};
|
||||
|
||||
|
||||
int ThreadPriorityFunc::getEpicsPriority(ThreadPriority threadPriority) {
|
||||
return epicsPriority[threadPriority];
|
||||
}
|
||||
|
||||
extern "C" void myFunc ( void * pPvt );
|
||||
|
||||
|
||||
class ThreadPvt {
|
||||
public:
|
||||
ThreadPvt(Thread *thread,String name,
|
||||
ThreadPriority priority, Runnable*runnable);
|
||||
virtual ~ThreadPvt();
|
||||
void ready();
|
||||
public: // only used within this source module
|
||||
Thread *thread;
|
||||
String name;
|
||||
ThreadPriority priority;
|
||||
Runnable *runnable;
|
||||
bool isReady;
|
||||
ThreadListElement *threadListElement;
|
||||
Event *waitDone;
|
||||
epicsThreadId id;
|
||||
};
|
||||
|
||||
extern "C" void myFunc ( void * pPvt )
|
||||
{
|
||||
ThreadPvt *threadPvt = (ThreadPvt *)pPvt;
|
||||
threadPvt->runnable->run();
|
||||
threadPvt->waitDone->signal();
|
||||
}
|
||||
|
||||
ThreadPvt::ThreadPvt(Thread *thread,String name,
|
||||
ThreadPriority priority, Runnable *runnable)
|
||||
: thread(thread),name(name),priority(priority),
|
||||
runnable(runnable),
|
||||
isReady(false),
|
||||
threadListElement(new ThreadListElement(thread)),
|
||||
waitDone(new Event()),
|
||||
id(epicsThreadCreate(
|
||||
name.c_str(),
|
||||
epicsPriority[priority],
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
myFunc,this))
|
||||
{
|
||||
init();
|
||||
Lock xx(globalMutex);
|
||||
threadList->addTail(threadListElement->node);
|
||||
totalConstruct++;
|
||||
}
|
||||
|
||||
ThreadPvt::~ThreadPvt()
|
||||
{
|
||||
bool result = waitDone->wait(2.0);
|
||||
if(!result) {
|
||||
throw std::logic_error(String("delete thread but run did not return"));
|
||||
String message("destroy thread ");
|
||||
message += thread->getName();
|
||||
message += " but run did not return";
|
||||
throw std::logic_error(message);
|
||||
}
|
||||
if(!threadListElement->node->isOnList()) {
|
||||
String message("destroy thread ");
|
||||
message += thread->getName();
|
||||
message += " is not on threadlist";
|
||||
throw std::logic_error(message);
|
||||
}
|
||||
threadList->remove(threadListElement->node);
|
||||
delete waitDone;
|
||||
delete threadListElement;
|
||||
Lock xx(globalMutex);
|
||||
totalDestruct++;
|
||||
}
|
||||
|
||||
Thread::Thread(String name,ThreadPriority priority,Runnable *runnable)
|
||||
: pImpl(new ThreadPvt(this,name,priority,runnable))
|
||||
{
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
delete pImpl;
|
||||
}
|
||||
|
||||
ConstructDestructCallback *Thread::getConstructDestructCallback()
|
||||
{
|
||||
init();
|
||||
return pConstructDestructCallback;
|
||||
}
|
||||
|
||||
void Thread::sleep(double seconds)
|
||||
{
|
||||
epicsThreadSleep(seconds);;
|
||||
}
|
||||
|
||||
String Thread::getName()
|
||||
{
|
||||
return pImpl->name;
|
||||
}
|
||||
|
||||
ThreadPriority Thread::getPriority()
|
||||
{
|
||||
return pImpl->priority;
|
||||
}
|
||||
|
||||
void Thread::showThreads(StringBuilder buf)
|
||||
{
|
||||
init();
|
||||
Lock xx(globalMutex);
|
||||
ThreadListNode *node = threadList->getHead();
|
||||
while(node!=0) {
|
||||
Thread *thread = node->getObject()->thread;
|
||||
*buf += thread->getName();
|
||||
*buf += " ";
|
||||
*buf += threadPriorityNames[thread->getPriority()];
|
||||
*buf += "\n";
|
||||
node = threadList->getNext(node);
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
53
pvDataApp/misc/thread.h
Normal file
53
pvDataApp/misc/thread.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* thread.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef THREAD_H
|
||||
#define THREAD_H
|
||||
#include "noDefaultMethods.h"
|
||||
#include "pvType.h"
|
||||
#include "showConstructDestruct.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
enum ThreadPriority {
|
||||
lowestPriority,
|
||||
lowerPriority,
|
||||
lowPriority,
|
||||
middlePriority,
|
||||
highPriority,
|
||||
higherPriority,
|
||||
highestPriority
|
||||
};
|
||||
|
||||
class ThreadPriorityFunc {
|
||||
public:
|
||||
static unsigned int const * const getEpicsPriorities();
|
||||
static int getEpicsPriority(ThreadPriority threadPriority);
|
||||
};
|
||||
|
||||
class Runnable{
|
||||
public:
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
class Thread;
|
||||
|
||||
class Thread : private NoDefaultMethods {
|
||||
public:
|
||||
Thread(String name,ThreadPriority priority,Runnable *runnable);
|
||||
~Thread();
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
String getName();
|
||||
ThreadPriority getPriority();
|
||||
static void showThreads(StringBuilder buf);
|
||||
static void sleep(double seconds);
|
||||
private:
|
||||
class ThreadPvt *pImpl;
|
||||
friend class ThreadPvt;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* THREAD_H */
|
||||
54
pvDataApp/misc/timeFunction.cpp
Normal file
54
pvDataApp/misc/timeFunction.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/* timeFunction.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include "noDefaultMethods.h"
|
||||
#include "pvType.h"
|
||||
#include "timeStamp.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class TimeFunctionRequester {
|
||||
public:
|
||||
virtual void function() = 0;
|
||||
};
|
||||
|
||||
class TimeFunction : private NoDefaultMethods {
|
||||
public:
|
||||
TimeFunction(TimeFunctionRequester *requester);
|
||||
~TimeFunction();
|
||||
double timeCall();
|
||||
private:
|
||||
TimeFunctionRequester *requester;
|
||||
};
|
||||
|
||||
TimeFunction::TimeFunction(TimeFunctionRequester *requester)
|
||||
: requester(requester) {}
|
||||
|
||||
|
||||
TimeFunction::~TimeFunction() {}
|
||||
|
||||
double TimeFunction::timeCall()
|
||||
{
|
||||
TimeStamp startTime;
|
||||
TimeStamp endTime;
|
||||
double perCall = 0.0;
|
||||
long ntimes = 1;
|
||||
while(true) {
|
||||
startTime.getCurrent();
|
||||
for(long i=0; i<ntimes; i++) requester->function();
|
||||
endTime.getCurrent();
|
||||
double diff = TimeStamp::diff(endTime,startTime);
|
||||
if(diff>=1.0) {
|
||||
perCall = diff/(double)ntimes;
|
||||
break;
|
||||
}
|
||||
ntimes *= 2;
|
||||
}
|
||||
return perCall;
|
||||
|
||||
}
|
||||
|
||||
}}
|
||||
30
pvDataApp/misc/timeFunction.h
Normal file
30
pvDataApp/misc/timeFunction.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* timeFunction.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef TIMEFUNCTION_H
|
||||
#define TIMEFUNCTION_H
|
||||
#include "noDefaultMethods.h"
|
||||
#include "pvType.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class TimeFunctionRequester {
|
||||
public:
|
||||
virtual void function() = 0;
|
||||
};
|
||||
|
||||
class TimeFunction : private NoDefaultMethods {
|
||||
public:
|
||||
TimeFunction(TimeFunctionRequester *requester);
|
||||
~TimeFunction();
|
||||
double timeCall();
|
||||
private:
|
||||
TimeFunctionRequester *requester;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
#endif /* TIMEFUNCTION_H */
|
||||
183
pvDataApp/misc/timeStamp.cpp
Normal file
183
pvDataApp/misc/timeStamp.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
/* timeStamp.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
#include <epicsTime.h>
|
||||
#include "noDefaultMethods.h"
|
||||
#include "pvType.h"
|
||||
#include "timeStamp.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
int32 milliSecPerSec = 1000;
|
||||
int32 microSecPerSec = milliSecPerSec*milliSecPerSec;
|
||||
int32 nanoSecPerSec = milliSecPerSec*microSecPerSec;
|
||||
int64 posixEpochAtEpicsEpoch = POSIX_TIME_AT_EPICS_EPOCH;
|
||||
|
||||
TimeStamp::TimeStamp(int64 secondsPastEpoch,int32 nanoSeconds)
|
||||
: secondsPastEpoch(secondsPastEpoch),nanoSeconds(nanoSeconds)
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
void TimeStamp::normalize()
|
||||
{
|
||||
if(nanoSeconds>=0 && nanoSeconds<nanoSecPerSec) return;
|
||||
while(nanoSeconds>=nanoSecPerSec) {
|
||||
nanoSeconds -= nanoSecPerSec;
|
||||
secondsPastEpoch++;
|
||||
}
|
||||
while(nanoSeconds<0) {
|
||||
nanoSeconds += nanoSecPerSec;
|
||||
secondsPastEpoch--;
|
||||
}
|
||||
}
|
||||
|
||||
void TimeStamp::fromTime_t(const time_t & tt)
|
||||
{
|
||||
epicsTimeStamp epicsTime;
|
||||
epicsTimeFromTime_t(&epicsTime,tt);
|
||||
secondsPastEpoch = epicsTime.secPastEpoch + posixEpochAtEpicsEpoch;
|
||||
nanoSeconds = epicsTime.nsec;
|
||||
}
|
||||
|
||||
void TimeStamp::toTime_t(time_t &tt) const
|
||||
{
|
||||
epicsTimeStamp epicsTime;
|
||||
epicsTime.secPastEpoch = secondsPastEpoch-posixEpochAtEpicsEpoch;
|
||||
epicsTime.nsec = nanoSeconds;
|
||||
epicsTimeToTime_t(&tt,&epicsTime);
|
||||
}
|
||||
|
||||
void TimeStamp::put(int64 milliseconds)
|
||||
{
|
||||
secondsPastEpoch = milliseconds/1000;
|
||||
nanoSeconds = (milliseconds%1000)*1000000;
|
||||
}
|
||||
|
||||
void TimeStamp::getCurrent()
|
||||
{
|
||||
epicsTimeStamp epicsTime;
|
||||
epicsTimeGetCurrent(&epicsTime);
|
||||
secondsPastEpoch = epicsTime.secPastEpoch;
|
||||
secondsPastEpoch += posixEpochAtEpicsEpoch;
|
||||
nanoSeconds = epicsTime.nsec;
|
||||
}
|
||||
|
||||
double TimeStamp::toSeconds() const
|
||||
{
|
||||
double value = secondsPastEpoch;
|
||||
double nano = nanoSeconds;
|
||||
value += nano/1e9;
|
||||
return value;
|
||||
}
|
||||
|
||||
int64 TimeStamp::diffInt(TimeStamp const & left,TimeStamp const&right )
|
||||
{
|
||||
int64 sl = left.secondsPastEpoch;
|
||||
int32 nl = left.nanoSeconds;
|
||||
int64 sr = right.secondsPastEpoch;
|
||||
int32 nr = right.nanoSeconds;
|
||||
int64 sdiff = sl - sr;
|
||||
sdiff *= nanoSecPerSec;
|
||||
sdiff += nl - nr;
|
||||
return sdiff;
|
||||
}
|
||||
|
||||
bool TimeStamp::operator==(TimeStamp const &right) const
|
||||
{
|
||||
int64 sdiff = diffInt(*this,right);
|
||||
if(sdiff==0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimeStamp::operator!=(TimeStamp const &right) const
|
||||
{
|
||||
int64 sdiff = diffInt(*this,right);
|
||||
if(sdiff!=0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimeStamp::operator<=(TimeStamp const &right) const
|
||||
{
|
||||
int64 sdiff = diffInt(*this,right);
|
||||
if(sdiff<=0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimeStamp::operator< (TimeStamp const &right) const
|
||||
{
|
||||
int64 sdiff = diffInt(*this,right);
|
||||
if(sdiff<0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimeStamp::operator>=(TimeStamp const &right) const
|
||||
{
|
||||
int64 sdiff = diffInt(*this,right);
|
||||
if(sdiff>=0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimeStamp::operator>(TimeStamp const &right) const
|
||||
{
|
||||
int64 sdiff = diffInt(*this,right);
|
||||
if(sdiff>0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
double TimeStamp::diff(TimeStamp const & a,TimeStamp const & b)
|
||||
{
|
||||
double result = a.secondsPastEpoch - b.secondsPastEpoch;
|
||||
result += (a.nanoSeconds - b.nanoSeconds)/1e9;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
TimeStamp & TimeStamp::operator+=(int64 seconds)
|
||||
{
|
||||
secondsPastEpoch += seconds;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TimeStamp & TimeStamp::operator-=(int64 seconds)
|
||||
{
|
||||
secondsPastEpoch -= seconds;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TimeStamp & TimeStamp::operator+=(double seconds)
|
||||
{
|
||||
int64 secs = seconds;
|
||||
int64 nano = (seconds - secs)*1e9;
|
||||
nanoSeconds += nano;
|
||||
if(nanoSeconds>nanoSecPerSec) {
|
||||
nanoSeconds -= nanoSecPerSec;
|
||||
secondsPastEpoch += 1;
|
||||
} else if(nanoSeconds<-nanoSecPerSec) {
|
||||
nanoSeconds += -nanoSecPerSec;
|
||||
secondsPastEpoch -= 1;
|
||||
}
|
||||
secondsPastEpoch += secs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TimeStamp & TimeStamp::operator-=(double seconds)
|
||||
{
|
||||
return operator+=(-seconds);
|
||||
}
|
||||
|
||||
int64 TimeStamp::getMilliseconds()
|
||||
{
|
||||
return secondsPastEpoch*1000 + nanoSeconds/1000000;
|
||||
}
|
||||
|
||||
}}
|
||||
63
pvDataApp/misc/timeStamp.h
Normal file
63
pvDataApp/misc/timeStamp.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* timeStamp.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef TIMESTAMP_H
|
||||
#define TIMESTAMP_H
|
||||
#include <ctime>
|
||||
#include "epicsTime.h"
|
||||
#include "pvType.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
extern int32 milliSecPerSec;
|
||||
extern int32 microSecPerSec;
|
||||
extern int32 nanoSecPerSec;
|
||||
extern int64 posixEpochAtEpicsEpoch;
|
||||
|
||||
class TimeStamp {
|
||||
public:
|
||||
TimeStamp()
|
||||
:secondsPastEpoch(0),nanoSeconds(0) {}
|
||||
TimeStamp(int64 secondsPastEpoch,int32 nanoSeconds = 0);
|
||||
//default constructors and destructor are OK
|
||||
//This class should not be extended
|
||||
void normalize();
|
||||
void fromTime_t(const time_t &);
|
||||
void toTime_t(time_t &) const;
|
||||
int64 getSecondsPastEpoch() const {return secondsPastEpoch;}
|
||||
int64 getEpicsSecondsPastEpoch() const {
|
||||
return secondsPastEpoch - posixEpochAtEpicsEpoch;
|
||||
}
|
||||
int32 getNanoSeconds() const {return nanoSeconds;}
|
||||
void put(int64 secondsPastEpoch,int32 nanoSeconds = 0) {
|
||||
this->secondsPastEpoch = secondsPastEpoch;
|
||||
this->nanoSeconds = nanoSeconds;
|
||||
normalize();
|
||||
}
|
||||
void put(int64 milliseconds);
|
||||
void getCurrent();
|
||||
double toSeconds() const ;
|
||||
bool operator==(TimeStamp const &) const;
|
||||
bool operator!=(TimeStamp const &) const;
|
||||
bool operator<=(TimeStamp const &) const;
|
||||
bool operator< (TimeStamp const &) const;
|
||||
bool operator>=(TimeStamp const &) const;
|
||||
bool operator> (TimeStamp const &) const;
|
||||
static double diff(TimeStamp const & a,TimeStamp const & b);
|
||||
TimeStamp & operator+=(int64 seconds);
|
||||
TimeStamp & operator-=(int64 seconds);
|
||||
TimeStamp & operator+=(double seconds);
|
||||
TimeStamp & operator-=(double seconds);
|
||||
int64 getMilliseconds(); // milliseconds since epoch
|
||||
private:
|
||||
static int64 diffInt(TimeStamp const &left,TimeStamp const &right );
|
||||
int64 secondsPastEpoch;
|
||||
int32 nanoSeconds;
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
#endif /* TIMESTAMP_H */
|
||||
310
pvDataApp/misc/timer.cpp
Normal file
310
pvDataApp/misc/timer.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/* timer.cpp */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "pvType.h"
|
||||
#include "lock.h"
|
||||
#include "noDefaultMethods.h"
|
||||
#include "showConstructDestruct.h"
|
||||
#include "linkedList.h"
|
||||
#include "thread.h"
|
||||
#include "timeStamp.h"
|
||||
#include "timer.h"
|
||||
#include "event.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
static volatile int64 totalNodeConstruct = 0;
|
||||
static volatile int64 totalNodeDestruct = 0;
|
||||
static volatile int64 totalTimerConstruct = 0;
|
||||
static volatile int64 totalTimerDestruct = 0;
|
||||
static Mutex *globalMutex = 0;
|
||||
|
||||
static int64 getTotalTimerNodeConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalNodeConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalTimerNodeDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalNodeDestruct;
|
||||
}
|
||||
|
||||
static int64 getTotalTimerConstruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalTimerConstruct;
|
||||
}
|
||||
|
||||
static int64 getTotalTimerDestruct()
|
||||
{
|
||||
Lock xx(globalMutex);
|
||||
return totalTimerDestruct;
|
||||
}
|
||||
|
||||
static ConstructDestructCallback *pCDCallbackTimerNode;
|
||||
static ConstructDestructCallback *pCDCallbackTimer;
|
||||
|
||||
static void init()
|
||||
{
|
||||
static Mutex mutex = Mutex();
|
||||
Lock xx(&mutex);
|
||||
if(globalMutex==0) {
|
||||
globalMutex = new Mutex();
|
||||
pCDCallbackTimerNode = new ConstructDestructCallback(
|
||||
"timerNode",
|
||||
getTotalTimerNodeConstruct,getTotalTimerNodeDestruct,0);
|
||||
|
||||
pCDCallbackTimer = new ConstructDestructCallback(
|
||||
"timer",
|
||||
getTotalTimerConstruct,getTotalTimerDestruct,0);
|
||||
}
|
||||
}
|
||||
|
||||
ConstructDestructCallback * TimerNode::getConstructDestructCallback()
|
||||
{
|
||||
init();
|
||||
return pCDCallbackTimerNode;
|
||||
}
|
||||
|
||||
ConstructDestructCallback * Timer::getConstructDestructCallback()
|
||||
{
|
||||
init();
|
||||
return pCDCallbackTimer;
|
||||
}
|
||||
|
||||
class TimerNodePvt;
|
||||
|
||||
typedef LinkedListNode<TimerNodePvt> TimerListNode;
|
||||
typedef LinkedList<TimerNodePvt> TimerList;
|
||||
|
||||
class TimerNodePvt {
|
||||
public:
|
||||
TimerNode *timerNode;
|
||||
TimerCallback *callback;
|
||||
TimerListNode *timerListNode;
|
||||
TimeStamp timeToRun;
|
||||
TimerPvt *timerPvt;
|
||||
double period;
|
||||
TimerNodePvt(TimerNode *timerNode,TimerCallback *callback);
|
||||
~TimerNodePvt();
|
||||
};
|
||||
|
||||
TimerNodePvt::TimerNodePvt(TimerNode *timerNode,TimerCallback *callback)
|
||||
: timerNode(timerNode),callback(callback),
|
||||
timerListNode(new TimerListNode(this)),timeToRun(TimeStamp()),
|
||||
timerPvt(0), period(0.0)
|
||||
{}
|
||||
|
||||
TimerNodePvt::~TimerNodePvt()
|
||||
{
|
||||
delete timerListNode;
|
||||
}
|
||||
|
||||
struct TimerPvt : public Runnable{
|
||||
public:
|
||||
TimerPvt(String threadName,ThreadPriority priority);
|
||||
~TimerPvt();
|
||||
virtual void run();
|
||||
public: // only used by this source module
|
||||
TimerList *timerList;
|
||||
Mutex mutex;
|
||||
Event *waitForWork;
|
||||
Event *waitForDone;
|
||||
volatile bool alive;
|
||||
Thread *thread;
|
||||
};
|
||||
|
||||
TimerPvt::TimerPvt(String threadName,ThreadPriority priority)
|
||||
: timerList(new TimerList()),
|
||||
mutex(Mutex()),
|
||||
waitForWork(new Event(false)),
|
||||
waitForDone(new Event(false)),
|
||||
alive(true),
|
||||
thread(new Thread(threadName,priority,this))
|
||||
{}
|
||||
|
||||
TimerPvt::~TimerPvt()
|
||||
{
|
||||
delete thread;
|
||||
delete waitForDone;
|
||||
delete waitForWork;
|
||||
delete timerList;
|
||||
}
|
||||
|
||||
static void addElement(TimerPvt *timer,TimerNodePvt *node)
|
||||
{
|
||||
TimerList *timerList = timer->timerList;
|
||||
TimerListNode *nextNode = timerList->getHead();
|
||||
if(nextNode==0) {
|
||||
timerList->addTail(node->timerListNode);
|
||||
return;
|
||||
}
|
||||
while(true) {
|
||||
TimerNodePvt *timerListNode = nextNode->getObject();
|
||||
if((node->timeToRun)<(timerListNode->timeToRun)) {
|
||||
timerList->insertBefore(timerListNode->timerListNode,node->timerListNode);
|
||||
return;
|
||||
}
|
||||
nextNode = timerList->getNext(timerListNode->timerListNode);
|
||||
if(nextNode==0) {
|
||||
timerList->addTail(node->timerListNode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TimerNode::TimerNode(TimerCallback *callback)
|
||||
: pImpl(new TimerNodePvt(this,callback))
|
||||
{
|
||||
init();
|
||||
Lock xx(globalMutex);
|
||||
totalNodeConstruct++;
|
||||
}
|
||||
|
||||
|
||||
TimerNode::~TimerNode()
|
||||
{
|
||||
cancel();
|
||||
delete pImpl;
|
||||
Lock xx(globalMutex);
|
||||
totalNodeDestruct++;
|
||||
}
|
||||
|
||||
void TimerNode::cancel()
|
||||
{
|
||||
TimerPvt *timerPvt = pImpl->timerPvt;
|
||||
if(timerPvt==0) return;
|
||||
Lock xx(&timerPvt->mutex);
|
||||
if(pImpl->timerPvt==0) return;
|
||||
pImpl->timerPvt->timerList->remove(pImpl);
|
||||
pImpl->timerPvt = 0;
|
||||
}
|
||||
|
||||
bool TimerNode::isScheduled()
|
||||
{
|
||||
TimerPvt *pvt = pImpl->timerPvt;
|
||||
if(pvt==0) return false;
|
||||
Lock xx(&pvt->mutex);
|
||||
return pImpl->timerListNode->isOnList();
|
||||
}
|
||||
|
||||
|
||||
void TimerPvt::run()
|
||||
{
|
||||
TimeStamp currentTime;
|
||||
while(alive) {
|
||||
currentTime.getCurrent();
|
||||
TimeStamp *timeToRun = 0;
|
||||
double period = 0.0;
|
||||
TimerNodePvt *nodeToCall = 0;
|
||||
{
|
||||
Lock xx(&mutex);
|
||||
TimerListNode *timerListNode = timerList->getHead();
|
||||
if(timerListNode!=0) {
|
||||
TimerNodePvt *timerNodePvt = timerListNode->getObject();
|
||||
timeToRun = &timerNodePvt->timeToRun;
|
||||
double diff = TimeStamp::diff(
|
||||
*timeToRun,currentTime);
|
||||
if(diff<=0.0) {
|
||||
nodeToCall = timerNodePvt;
|
||||
timerList->removeHead();
|
||||
period = timerNodePvt->period;
|
||||
if(period>0.0) {
|
||||
timerNodePvt->timeToRun += period;
|
||||
addElement(this,timerNodePvt);
|
||||
} else {
|
||||
timerNodePvt->timerPvt = 0;
|
||||
}
|
||||
timerListNode = timerList->getHead();
|
||||
if(timerListNode!=0) {
|
||||
timerNodePvt = timerListNode->getObject();
|
||||
timeToRun = &timerNodePvt->timeToRun;
|
||||
} else {
|
||||
timeToRun = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(nodeToCall!=0) {
|
||||
nodeToCall->callback->callback();
|
||||
}
|
||||
if(!alive) break;
|
||||
if(timeToRun==0) {
|
||||
waitForWork->wait();
|
||||
} else {
|
||||
double delay = TimeStamp::diff(*timeToRun,currentTime);
|
||||
waitForWork->wait(delay);
|
||||
}
|
||||
}
|
||||
waitForDone->signal();
|
||||
}
|
||||
|
||||
Timer::Timer(String threadName, ThreadPriority priority)
|
||||
: pImpl(new TimerPvt(threadName,priority))
|
||||
{
|
||||
init();
|
||||
Lock xx(globalMutex);
|
||||
totalTimerConstruct++;
|
||||
}
|
||||
|
||||
Timer::~Timer() {
|
||||
{
|
||||
Lock xx(&pImpl->mutex);
|
||||
pImpl->alive = false;
|
||||
}
|
||||
pImpl->waitForWork->signal();
|
||||
pImpl->waitForDone->wait();
|
||||
TimerList *timerList = pImpl->timerList;
|
||||
TimerListNode *node = 0;
|
||||
while((node = timerList->removeHead())!=0) {
|
||||
node->getObject()->callback->timerStopped();
|
||||
}
|
||||
delete pImpl;
|
||||
Lock xx(globalMutex);
|
||||
totalTimerDestruct++;
|
||||
}
|
||||
|
||||
void Timer::scheduleAfterDelay(TimerNode *timerNode,double delay)
|
||||
{
|
||||
schedulePeriodic(timerNode,delay,0.0);
|
||||
}
|
||||
void Timer::schedulePeriodic(TimerNode *timerNode,double delay,double period)
|
||||
{
|
||||
TimerNodePvt *timerNodePvt = timerNode->pImpl;
|
||||
if(timerNodePvt->timerListNode->isOnList()) {
|
||||
throw std::logic_error(String("already queued"));
|
||||
}
|
||||
if(!pImpl->alive) {
|
||||
timerNodePvt->callback->timerStopped();
|
||||
return;
|
||||
}
|
||||
TimeStamp *timeStamp = &timerNodePvt->timeToRun;
|
||||
timeStamp->getCurrent();
|
||||
*timeStamp += delay;
|
||||
timerNodePvt->period = period;
|
||||
bool isFirst = false;
|
||||
{
|
||||
Lock xx(&pImpl->mutex);
|
||||
timerNodePvt->timerPvt = pImpl;
|
||||
addElement(pImpl,timerNodePvt);
|
||||
TimerNodePvt *first = pImpl->timerList->getHead()->getObject();
|
||||
if(first==timerNodePvt) isFirst = true;
|
||||
}
|
||||
if(isFirst) pImpl->waitForWork->signal();
|
||||
}
|
||||
|
||||
}}
|
||||
53
pvDataApp/misc/timer.h
Normal file
53
pvDataApp/misc/timer.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* timer.h */
|
||||
/**
|
||||
* Copyright - See the COPYRIGHT that is included with this distribution.
|
||||
* EPICS pvDataCPP is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "pvType.h"
|
||||
#include "thread.h"
|
||||
#include "noDefaultMethods.h"
|
||||
#include "showConstructDestruct.h"
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class TimerCallback {
|
||||
public:
|
||||
virtual void callback() = 0;
|
||||
virtual void timerStopped() = 0;
|
||||
};
|
||||
|
||||
class TimerNode : private NoDefaultMethods {
|
||||
public:
|
||||
TimerNode(TimerCallback *timerCallback);
|
||||
~TimerNode();
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
void cancel();
|
||||
bool isScheduled();
|
||||
private:
|
||||
class TimerNodePvt *pImpl;
|
||||
friend class Timer;
|
||||
};
|
||||
|
||||
class Timer : private NoDefaultMethods {
|
||||
public:
|
||||
Timer(String threadName, ThreadPriority priority);
|
||||
~Timer();
|
||||
static ConstructDestructCallback *getConstructDestructCallback();
|
||||
void scheduleAfterDelay(TimerNode *timerNode,double delay);
|
||||
void schedulePeriodic(TimerNode *timerNode,double delay,double period);
|
||||
private:
|
||||
class TimerPvt *pImpl;
|
||||
friend class TimerNode;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif /* TIMER_H */
|
||||
Reference in New Issue
Block a user