New array API for PVValueArray using shared_vector<T>

* In PVScalarArray

Add methods assign, getAs/putFrom, and copyOut/copyIn to allow get/put
with implicit convert.

assign() copys on PVScalarArray to another converting as necessary.
If the types do not match then an allocate and convert is done.

getAs/putFrom work with shared_vector<T> and can avoid a allocate
and convert operation if the types match.

copyOut/copyIn use plain C arrays will do either a copy if the types
match, and a convert otherwise.  No allocation is performed.

* In PVValueArray<T>

All array operations re-implemented in terms of
two virtual methods

  virtual const shared_vector<T>& viewUnsafe() const;
  virtual void swap(shared_vector<T>&);

Some convienence methods are also included:

  shared_vector<const T> view() const
  shared_vector<T> take()
  shared_vector<T> reuse()

Deprecate get(...), put(...), and shareData(...)

Remove getVector() and getSharedVector()

Adjust DefaultPVArray accordingly
This commit is contained in:
Michael Davidsaver
2013-05-02 18:09:53 -04:00
parent 5f4ca240b4
commit e843779555
2 changed files with 364 additions and 254 deletions

View File

@@ -196,51 +196,31 @@ public:
typedef const std::vector<T> const_vector;
typedef std::tr1::shared_ptr<vector> shared_vector;
typedef ::epics::pvData::shared_vector<T> svector;
typedef ::epics::pvData::shared_vector<const T> const_svector;
DefaultPVArray(ScalarArrayConstPtr const & scalarArray);
virtual ~DefaultPVArray();
virtual void setCapacity(size_t capacity);
virtual void setLength(size_t length);
virtual size_t get(size_t offset, size_t length, PVArrayData<T> &data) ;
virtual size_t put(size_t offset,size_t length, const_pointer from,
size_t fromOffset);
virtual void shareData(
std::tr1::shared_ptr<std::vector<T> > const & value,
std::size_t capacity,
std::size_t length);
virtual pointer get() ;
virtual pointer get() const ;
virtual vector const & getVector() { return *value.get(); }
virtual shared_vector const & getSharedVector(){return value;};
virtual const svector& viewUnsafe() const;
virtual void swap(svector &other);
// from Serializable
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const;
private:
shared_vector value;
svector value;
};
template<typename T>
T *DefaultPVArray<T>::get()
{
std::vector<T> *vec = value.get();
T *praw = &((*vec)[0]);
return praw;
}
template<typename T>
T *DefaultPVArray<T>::get() const
{
std::vector<T> *vec = value.get();
T *praw = &((*vec)[0]);
return praw;
}
template<typename T>
DefaultPVArray<T>::DefaultPVArray(ScalarArrayConstPtr const & scalarArray)
: PVValueArray<T>(scalarArray),
value(std::tr1::shared_ptr<std::vector<T> >(new std::vector<T>()))
value()
{ }
@@ -251,101 +231,41 @@ DefaultPVArray<T>::~DefaultPVArray()
template<typename T>
void DefaultPVArray<T>::setCapacity(size_t capacity)
{
if(PVArray::getCapacity()==capacity) return;
if(!PVArray::isCapacityMutable()) {
std::string message("not capacityMutable");
PVField::message(message, errorMessage);
return;
if(capacity>value.capacity()) {
value.reserve(capacity);
PVArray::setCapacityLength(value.capacity(), value.size());
}
size_t length = PVArray::getLength();
if(length>capacity) length = capacity;
size_t oldCapacity = PVArray::getCapacity();
if(oldCapacity>capacity) {
std::vector<T> array;
array.reserve(capacity);
array.resize(length);
T * from = get();
for (size_t i=0; i<length; i++) array[i] = from[i];
value->swap(array);
} else {
value->reserve(capacity);
}
PVArray::setCapacityLength(capacity,length);
}
template<typename T>
void DefaultPVArray<T>::setLength(size_t length)
{
if(PVArray::getLength()==length) return;
size_t capacity = PVArray::getCapacity();
if(length>capacity) {
if(!PVArray::isCapacityMutable()) {
std::string message("not capacityMutable");
PVField::message(message, errorMessage);
return;
}
setCapacity(length);
}
value->resize(length);
PVArray::setCapacityLength(capacity,length);
if(length == value.size())
return;
else if(length < value.size())
value.slice(0, length);
else
value.resize(length);
PVArray::setCapacityLength(value.capacity(), value.size());
}
template<typename T>
const typename DefaultPVArray<T>::svector& DefaultPVArray<T>::viewUnsafe() const
{
return value;
}
template<typename T>
size_t DefaultPVArray<T>::get(size_t offset, size_t len, PVArrayData<T> &data)
void DefaultPVArray<T>::swap(svector &other)
{
size_t n = len;
size_t length = this->getLength();
if(offset+len > length) {
n = length-offset;
//if(n<0) n = 0;
}
data.data = *value.get();
data.offset = offset;
return n;
if(this->isImmutable())
THROW_EXCEPTION2(std::logic_error,"Immutable");
value.swap(other);
PVArray::setCapacityLength(value.capacity(), value.size());
}
template<typename T>
size_t DefaultPVArray<T>::put(size_t offset,size_t len,
const_pointer from,size_t fromOffset)
{
if(PVField::isImmutable()) {
PVField::message("field is immutable",errorMessage);
return 0;
}
T * pvalue = get();
if(from==pvalue) return len;
if(len<1) return 0;
size_t length = this->getLength();
size_t capacity = this->getCapacity();
if(offset+len > length) {
size_t newlength = offset + len;
if(newlength>capacity) {
setCapacity(newlength);
newlength = this->getCapacity();
len = newlength - offset;
if(len<=0) return 0;
}
length = newlength;
setLength(length);
}
pvalue = get();
for(size_t i=0;i<len;i++) {
pvalue[i+offset] = from[i+fromOffset];
}
this->setLength(length);
this->postPut();
return len;
}
template<typename T>
void DefaultPVArray<T>::shareData(
std::tr1::shared_ptr<std::vector<T> > const & sharedValue,
std::size_t capacity,
std::size_t length)
{
value = sharedValue;
PVArray::setCapacityLength(capacity,length);
}
template<typename T>
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
@@ -357,93 +277,91 @@ template<typename T>
void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
size_t size = SerializeHelper::readSize(pbuffer, pcontrol);
// alignment if (size>0) { pcontrol->ensureData(sizeof(T)-1); pbuffer->align(sizeof(T)); }
//if(size>=0) {
// prepare array, if necessary
if(size>this->getCapacity()) this->setCapacity(size);
// set new length
this->setLength(size);
// try to avoid deserializing from the buffer
// this is only possible if we do not need to do endian-swapping
if (!pbuffer->reverse<T>())
if (pcontrol->directDeserialize(pbuffer, (char*)(get()), size, sizeof(T)))
{
// inform about the change?
PVField::postPut();
return;
}
value.resize(size); // TODO: avoid copy of stuff we will then overwrite
// retrieve value from the buffer
size_t i = 0;
T * pvalue = get();
while(true) {
/*
size_t maxIndex = min(size-i, (int)(pbuffer->getRemaining()/sizeof(T)))+i;
for(; i<maxIndex; i++)
value[i] = pbuffer->get<T>();
*/
size_t maxCount = min(size-i, (pbuffer->getRemaining()/sizeof(T)));
pbuffer->getArray(pvalue+i, maxCount);
i += maxCount;
if(i<size)
pcontrol->ensureData(sizeof(T)); // this is not OK since can exceen max local buffer (size-i)*sizeof(T));
else
break;
}
PVArray::setCapacityLength(value.capacity(), value.size());
T* cur = value.data();
// try to avoid deserializing from the buffer
// this is only possible if we do not need to do endian-swapping
if (!pbuffer->reverse<T>())
if (pcontrol->directDeserialize(pbuffer, (char*)cur, size, sizeof(T)))
{
// inform about the change?
PVField::postPut();
//}
// TODO null arrays (size == -1) not supported
return;
}
// retrieve value from the buffer
size_t remaining = size;
while(remaining) {
const size_t have_bytes = pbuffer->getRemaining();
// correctly rounds down in an element is partially received
const size_t available = have_bytes/sizeof(T);
if(available == 0) {
size_t want = sizeof(T);
if(remaining==1 && sizeof(T)>1) {
// Need to wait for the last few bytes
// of the final element.
// available==0 implies have_bytes<sizeof(T)
want = sizeof(T) - have_bytes;
}
// recv() at least one element, or remaining buffer
pcontrol->ensureData(want);
continue;
}
const size_t n2read = std::min(remaining, available);
pbuffer->getArray(cur, n2read);
cur += n2read;
remaining -= n2read;
}
// inform about the change?
PVField::postPut();
}
template<typename T>
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const {
// cache
size_t length = this->getLength();
SerializableControl *pflusher, size_t offset, size_t count) const
{
//TODO: avoid incrementing the ref counter...
svector temp(value);
temp.slice(offset, count);
count = temp.size();
// check bounds
/*if(offset<0)
offset = 0;
else*/ if(offset>length) offset = length;
//if(count<0) count = length;
size_t maxCount = length-offset;
if(count>maxCount) count = maxCount;
// write
SerializeHelper::writeSize(count, pbuffer, pflusher);
//if (count == 0) return; pcontrol->ensureData(sizeof(T)-1); pbuffer->align(sizeof(T));
SerializeHelper::writeSize(temp.size(), pbuffer, pflusher);
T* cur = temp.data();
// try to avoid copying into the buffer
// this is only possible if we do not need to do endian-swapping
if (!pbuffer->reverse<T>())
if (pflusher->directSerialize(pbuffer, (const char*)(get()+offset), count, sizeof(T)))
if (pflusher->directSerialize(pbuffer, (const char*)cur, count, sizeof(T)))
return;
size_t end = offset+count;
size_t i = offset;
T * pvalue = const_cast<T *>(get());
while(true) {
/*
size_t maxIndex = min<int>(end-i, (int)(pbuffer->getRemaining()/sizeof(T)))+i;
for(; i<maxIndex; i++)
pbuffer->put<T>(value[i]);
*/
size_t maxCount = min<int>(end-i, (int)(pbuffer->getRemaining()/sizeof(T)));
pbuffer->putArray(pvalue+i, maxCount);
i += maxCount;
if(i<end)
while(count) {
const size_t empty = pbuffer->getRemaining();
const size_t space_for = empty/sizeof(T);
if(space_for==0) {
pflusher->flushSerializeBuffer();
else
break;
// Can we be certain that more space is now free???
// If not then we spinnnnnnnnn
continue;
}
const size_t n2send = std::min(count, space_for);
pbuffer->putArray(cur, n2send);
cur += n2send;
count -= n2send;
}
pflusher->flushSerializeBuffer();
}
// specializations for String
@@ -452,42 +370,36 @@ template<>
void DefaultPVArray<String>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
size_t size = SerializeHelper::readSize(pbuffer, pcontrol);
//if(size>=0) {
// prepare array, if necessary
if(size>getCapacity()) setCapacity(size);
// set new length
setLength(size);
// retrieve value from the buffer
String * pvalue = get();
for(size_t i = 0; i<size; i++) {
pvalue[i] = SerializeHelper::deserializeString(pbuffer,
pcontrol);
}
// inform about the change?
postPut();
//}
// TODO null arrays (size == -1) not supported
// Decide if we must re-allocate
if(size > value.size() || !value.unique())
value.resize(size);
else if(size < value.size())
value.slice(0, size);
setCapacityLength(size, size);
String * pvalue = value.data();
for(size_t i = 0; i<size; i++) {
pvalue[i] = SerializeHelper::deserializeString(pbuffer,
pcontrol);
}
// inform about the change?
postPut();
}
template<>
void DefaultPVArray<String>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const {
size_t length = getLength();
// check bounds
/*if(offset<0)
offset = 0;
else*/ if(offset>length) offset = length;
//if(count<0) count = length;
svector temp(value);
temp.slice(offset, count);
size_t maxCount = length-offset;
if(count>maxCount) count = maxCount;
SerializeHelper::writeSize(temp.size(), pbuffer, pflusher);
// write
SerializeHelper::writeSize(count, pbuffer, pflusher);
size_t end = offset+count;
String * pvalue = get();
for(size_t i = offset; i<end; i++) {
String * pvalue = temp.data();
for(size_t i = 0; i<temp.size(); i++) {
SerializeHelper::serializeString(pvalue[i], pbuffer, pflusher);
}
}

View File

@@ -9,6 +9,15 @@
*/
#ifndef PVDATA_H
#define PVDATA_H
#ifdef __GNUC__
#define USAGE_DEPRECATED __attribute__((deprecated))
#define USAGE_ERROR(MSG) __attribute__((error(MSG)))
#else
#define USAGE_DEPRECATED
#define USAGE_ERROR(MSG)
#endif
#include <string>
#include <map>
#include <stdexcept>
@@ -19,6 +28,7 @@
#include <pv/pvIntrospect.h>
#include <pv/requester.h>
#include <pv/typeCast.h>
#include <pv/sharedVector.h>
namespace epics { namespace pvData {
@@ -657,22 +667,75 @@ public:
*/
const ScalarArrayConstPtr getScalarArray() const ;
/**
* Fetch the current value and convert to the requeted type.
*
* A copy is made if the requested type does not match
* the element type. If the types do match then
* no copy is made.
*/
template<ScalarType ID>
inline void getAs(typename ScalarTypeTraits<ID>::type* ptr,
size_t count, size_t offset = 0) const
inline void
getAs(shared_vector<typename ScalarTypeTraits<ID>::type>& out) const
{
getAs(ID, (void*)ptr, count, offset);
shared_vector<void> temp(static_shared_vector_cast<void>(out));
getAs(ID, temp);
out = static_shared_vector_cast<typename ScalarTypeTraits<ID>::type>(temp);
}
virtual void getAs(ScalarType, void*, size_t, size_t) const = 0;
virtual void
getAs(ScalarType, shared_vector<void>& out) const = 0;
/**
* Assign the given value after conversion.
*
* A copy and element-wise conversion is are always performed.
*/
template<ScalarType ID>
inline void putFrom(const typename ScalarTypeTraits<ID>::type* ptr,
size_t count, size_t offset = 0)
inline size_t copyOut(typename ScalarTypeTraits<ID>::type* inp, size_t len) const
{
putFrom(ID, (const void*)ptr, count, offset);
return copyOut(ID, (void*)inp, len);
}
virtual void putFrom(ScalarType, const void*, size_t ,size_t) = 0;
virtual size_t copyOut(ScalarType, void*, size_t) const = 0;
/**
* Assign the given value after conversion.
*
* A copy and element-wise conversion is performed unless
* the element type of the PVScalarArray matches the
* type of the provided data.
* If the types do match then a new refernce to the provided
* data is kept.
*/
template<ScalarType ID>
inline void putFrom(const shared_vector<typename ScalarTypeTraits<ID>::type>& inp)
{
shared_vector<void> temp(static_shared_vector_cast<void>(inp));
putFrom(ID, temp);
}
virtual void putFrom(ScalarType, const shared_vector<void>&) = 0;
/**
* Assign the given value after conversion.
*
* A copy and element-wise conversion is are always performed.
*/
template<ScalarType ID>
inline void copyIn(const typename ScalarTypeTraits<ID>::type* inp, size_t len)
{
copyIn(ID, (const void*)inp, len);
}
virtual void copyIn(ScalarType, const void*, size_t) = 0;
/**
* Assign the given PVScalarArray's value.
*
* A copy and element-wise conversion is performed unless
* the element type of the PVScalarArray matches the
* type of the provided data.
* If the types do match then a new refernce to the provided
* data is kept.
*/
virtual void assign(PVScalarArray& pv) = 0;
protected:
@@ -1016,6 +1079,19 @@ private:
friend class PVDataCreate;
};
namespace detail {
// adaptor to allow epics::pvData::shared_vector to hold a reference
// to a shared_ptr<std::vector<> >
template<typename T>
struct shared_ptr_vector_deletor {
typedef std::tr1::shared_ptr<std::vector<T> > shared_vector;
shared_vector vec;
shared_ptr_vector_deletor(const shared_vector& v)
:vec(v) {}
void operator()(T*){vec.reset();}
};
}
template<typename T>
class PVValueArray : public PVScalarArray {
public:
@@ -1023,12 +1099,19 @@ public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
//TODO: full namespace can be removed along with local typedef 'shared_vector'
typedef ::epics::pvData::shared_vector<T> svector;
typedef ::epics::pvData::shared_vector<const T> const_svector;
// begin deprecated
typedef PVArrayData<T> ArrayDataType;
typedef std::vector<T> vector;
typedef const std::vector<T> const_vector;
typedef std::tr1::shared_ptr<vector> shared_vector;
typedef PVValueArray & reference;
typedef const PVValueArray & const_reference;
// end deprecated
static const ScalarType typeCode;
@@ -1036,40 +1119,122 @@ public:
* Destructor
*/
virtual ~PVValueArray() {}
// Primative array manipulations
//! unchecked writable reference
//! Before you call this directly, consider using one
//! other the following methods.
virtual const svector& viewUnsafe() const = 0;
//! Exchange our contents for the provided.
//! Fails for Immutable arrays
virtual void swap(svector& other) = 0;
//! Discard current contents and replaced with the provided.
//! Fails for Immutable arrays
virtual void replace(const svector& next)
{
svector temp(next);
this->swap(temp);
}
// Derived operations
//! Fetch a read-only view of the current array data
inline const_svector view() const
{
const_svector newref(this->viewUnsafe());
return newref;
}
//! Remove and return the current array data
inline svector take()
{
svector result;
this->swap(result);
return result;
}
//! take() with an implied make_unique()
inline svector reuse()
{
svector result;
this->swap(result);
result.make_unique();
return result;
}
/**
* Get array elements
* @param offset The offset of the first element,
* @param length The number of elements to get.
* @param data The place where the data is placed.
*/
virtual std::size_t get(
std::size_t offset, std::size_t length, ArrayDataType &data) = 0;
std::size_t get(
std::size_t offset, std::size_t length, ArrayDataType &data) USAGE_DEPRECATED
{
const_svector ref = this->view();
ref.slice(offset, length);
data.data.resize(ref.size());
data.offset = 0;
std::copy(ref.begin(), ref.end(), data.data.begin());
return ref.size();
}
/**
* Put data into the array.
* Copy data into the array growing the length as needed.
* @param offset The offset of the first element,
* @param length The number of elements to get.
* @param from The new values to put into the array.
* @param fromOffset The offset in from.
* @return The number of elements put into the array.
*/
virtual std::size_t put(std::size_t offset,
std::size_t length, const_pointer from, std::size_t fromOffset) = 0;
virtual std::size_t put(std::size_t offset,
std::size_t length, const_vector &from, std::size_t fromOffset);
std::size_t put(std::size_t offset,
std::size_t length, const_pointer from, std::size_t fromOffset) USAGE_DEPRECATED
{
from += fromOffset;
svector temp;
this->swap(temp);
if(temp.size() < length+offset)
temp.resize(length+offset);
else
temp.make_unique();
std::copy(from, from + length, temp.begin() + offset);
this->swap(temp);
return length;
}
std::size_t put(std::size_t offset,
std::size_t length, const_vector &from, std::size_t fromOffset) USAGE_DEPRECATED
{ return this->put(offset,length, &from[0], fromOffset); }
/**
* Share data from another source.
* @param value The data to share.
* @param capacity The capacity of the array.
* @param length The length of the array.
*/
virtual void shareData(
void shareData(
shared_vector const & value,
std::size_t capacity,
std::size_t length) = 0;
virtual pointer get() = 0;
virtual pointer get() const = 0;
virtual vector const & getVector() = 0;
virtual shared_vector const & getSharedVector() = 0;
std::size_t length) USAGE_DEPRECATED
{
vector& vref = *value.get();
typename svector::shared_pointer_type p(&vref[0],
detail::shared_ptr_vector_deletor<T>(value));
svector temp(p, 0, std::min(length, vref.size()));
this->swap(temp);
}
pointer get() const {
return this->viewUnsafe().data();
}
vector const & getVector() USAGE_ERROR("No longer implemented");
shared_vector const & getSharedVector() USAGE_ERROR("No longer implemented");
std::ostream& dumpValue(std::ostream& o) const
{
@@ -1092,30 +1257,67 @@ public:
return o << *(get() + index);
}
virtual void getAs(ScalarType dtype, void* ptr, size_t count, size_t offset) const
virtual void
getAs(ScalarType id, ::epics::pvData::shared_vector<void>& out) const
{
castUnsafeV(count, dtype, ptr, typeCode, (const void*)(get()+offset));
const svector& data(viewUnsafe());
::epics::pvData::shared_vector<void> temp(static_shared_vector_cast<void>(data));
if(id==typeCode) {
out = temp; // no convert = no copy
} else {
//TODO: reuse out if possible??
::epics::pvData::shared_vector<void> vcopy(ScalarTypeFunc::allocArray(id, data.size()));
castUnsafeV(data.size(), id, vcopy.data(), typeCode, temp.data());
out.swap(vcopy);
}
}
virtual void putFrom(ScalarType dtype, const void*ptr, size_t count, size_t offset)
virtual size_t copyOut(ScalarType id, void* ptr, size_t olen) const
{
if(getLength()<offset+count)
setLength(offset+count);
castUnsafeV(count, typeCode, (void*)(get()+offset), dtype, ptr);
const svector& data(viewUnsafe());
size_t len = std::min(olen, data.size());
castUnsafeV(len, id, ptr, typeCode, (const void*)data.data());
return len;
}
virtual void
putFrom(ScalarType id, const ::epics::pvData::shared_vector<void>& inp)
{
if(id==typeCode) {
svector next(static_shared_vector_cast<T>(inp));
this->swap(next); // no convert == no copy
} else {
size_t len = inp.size() / ScalarTypeFunc::elementSize(id);
svector result;
this->swap(result);
result.resize(len);
castUnsafeV(len, typeCode, result.data(), id, inp.data());
this->swap(result);
}
}
virtual void copyIn(ScalarType id, const void* ptr, size_t len)
{
svector data;
this->swap(data);
data.resize(len);
castUnsafeV(len, typeCode, (void*)data.data(), id, ptr);
this->swap(data);
}
virtual void assign(PVScalarArray& pv)
{
if(this==&pv)
return;
if(isImmutable())
throw std::invalid_argument("Destination is immutable");
if(pv.isImmutable() && typeCode==pv.getScalarArray()->getElementType()) {
PVValueArray& pvr = static_cast<PVValueArray&>(pv);
shareData(pvr.getSharedVector(), pvr.getCapacity(), pvr.getLength());
} else {
setLength(pv.getLength());
pv.getAs(typeCode, (void*)get(), std::min(getLength(),pv.getLength()), 0);
}
::epics::pvData::shared_vector<void> temp;
pv.getAs(typeCode, temp);
svector next(static_shared_vector_cast<T>(temp));
this->swap(next);
}
protected:
@@ -1124,13 +1326,6 @@ protected:
friend class PVDataCreate;
};
template<typename T>
std::size_t PVValueArray<T>::put(
std::size_t offset,
std::size_t length,
const_vector &from,
std::size_t fromOffset)
{ return put(offset,length, &from[0], fromOffset); }
/**
* Definitions for the various scalarArray types.
@@ -1281,6 +1476,9 @@ private:
*/
extern PVDataCreatePtr getPVDataCreate();
#undef USAGE_DEPRECATED
#undef USAGE_ERROR
}}
#endif /* PVDATA_H */