diff --git a/pvDataApp/factory/PVDataCreateFactory.cpp b/pvDataApp/factory/PVDataCreateFactory.cpp index 7561336..0a2fe00 100644 --- a/pvDataApp/factory/PVDataCreateFactory.cpp +++ b/pvDataApp/factory/PVDataCreateFactory.cpp @@ -196,51 +196,31 @@ public: typedef const std::vector const_vector; typedef std::tr1::shared_ptr shared_vector; + typedef ::epics::pvData::shared_vector svector; + typedef ::epics::pvData::shared_vector 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 &data) ; - virtual size_t put(size_t offset,size_t length, const_pointer from, - size_t fromOffset); - virtual void shareData( - std::tr1::shared_ptr > 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 -T *DefaultPVArray::get() -{ - std::vector *vec = value.get(); - T *praw = &((*vec)[0]); - return praw; -} - -template -T *DefaultPVArray::get() const -{ - std::vector *vec = value.get(); - T *praw = &((*vec)[0]); - return praw; -} - - template DefaultPVArray::DefaultPVArray(ScalarArrayConstPtr const & scalarArray) : PVValueArray(scalarArray), - value(std::tr1::shared_ptr >(new std::vector())) + value() { } @@ -251,101 +231,41 @@ DefaultPVArray::~DefaultPVArray() template void DefaultPVArray::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 array; - array.reserve(capacity); - array.resize(length); - T * from = get(); - for (size_t i=0; iswap(array); - } else { - value->reserve(capacity); - } - PVArray::setCapacityLength(capacity,length); } template void DefaultPVArray::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 +const typename DefaultPVArray::svector& DefaultPVArray::viewUnsafe() const +{ + return value; } template -size_t DefaultPVArray::get(size_t offset, size_t len, PVArrayData &data) +void DefaultPVArray::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 -size_t DefaultPVArray::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;isetLength(length); - this->postPut(); - return len; -} - -template -void DefaultPVArray::shareData( - std::tr1::shared_ptr > const & sharedValue, - std::size_t capacity, - std::size_t length) -{ - value = sharedValue; - PVArray::setCapacityLength(capacity,length); -} template void DefaultPVArray::serialize(ByteBuffer *pbuffer, @@ -357,93 +277,91 @@ template void DefaultPVArray::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()) - 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(; iget(); - */ - size_t maxCount = min(size-i, (pbuffer->getRemaining()/sizeof(T))); - pbuffer->getArray(pvalue+i, maxCount); - i += maxCount; - - if(iensureData(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()) + 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_bytesensureData(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 void DefaultPVArray::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()) - 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(get()); - while(true) { - - /* - size_t maxIndex = min(end-i, (int)(pbuffer->getRemaining()/sizeof(T)))+i; - for(; iput(value[i]); - */ - - size_t maxCount = min(end-i, (int)(pbuffer->getRemaining()/sizeof(T))); - pbuffer->putArray(pvalue+i, maxCount); - i += maxCount; - - if(igetRemaining(); + 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::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 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 void DefaultPVArray::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 #include #include @@ -19,6 +28,7 @@ #include #include #include +#include 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 - inline void getAs(typename ScalarTypeTraits::type* ptr, - size_t count, size_t offset = 0) const + inline void + getAs(shared_vector::type>& out) const { - getAs(ID, (void*)ptr, count, offset); + shared_vector temp(static_shared_vector_cast(out)); + getAs(ID, temp); + out = static_shared_vector_cast::type>(temp); } - virtual void getAs(ScalarType, void*, size_t, size_t) const = 0; + virtual void + getAs(ScalarType, shared_vector& out) const = 0; + /** + * Assign the given value after conversion. + * + * A copy and element-wise conversion is are always performed. + */ template - inline void putFrom(const typename ScalarTypeTraits::type* ptr, - size_t count, size_t offset = 0) + inline size_t copyOut(typename ScalarTypeTraits::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 + inline void putFrom(const shared_vector::type>& inp) + { + shared_vector temp(static_shared_vector_cast(inp)); + putFrom(ID, temp); + } + virtual void putFrom(ScalarType, const shared_vector&) = 0; + + /** + * Assign the given value after conversion. + * + * A copy and element-wise conversion is are always performed. + */ + template + inline void copyIn(const typename ScalarTypeTraits::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 > + template + struct shared_ptr_vector_deletor { + typedef std::tr1::shared_ptr > shared_vector; + shared_vector vec; + shared_ptr_vector_deletor(const shared_vector& v) + :vec(v) {} + void operator()(T*){vec.reset();} + }; +} + template 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 svector; + typedef ::epics::pvData::shared_vector const_svector; + + // begin deprecated typedef PVArrayData ArrayDataType; typedef std::vector vector; typedef const std::vector const_vector; typedef std::tr1::shared_ptr 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(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& out) const { - castUnsafeV(count, dtype, ptr, typeCode, (const void*)(get()+offset)); + const svector& data(viewUnsafe()); + ::epics::pvData::shared_vector temp(static_shared_vector_cast(data)); + if(id==typeCode) { + out = temp; // no convert = no copy + } else { + //TODO: reuse out if possible?? + ::epics::pvData::shared_vector 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()& inp) + { + if(id==typeCode) { + svector next(static_shared_vector_cast(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(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 temp; + pv.getAs(typeCode, temp); + svector next(static_shared_vector_cast(temp)); + this->swap(next); } protected: @@ -1124,13 +1326,6 @@ protected: friend class PVDataCreate; }; -template -std::size_t PVValueArray::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 */