diff --git a/src/factory/Compare.cpp b/src/factory/Compare.cpp index 18ee2ba..6d902df 100644 --- a/src/factory/Compare.cpp +++ b/src/factory/Compare.cpp @@ -134,6 +134,13 @@ bool operator==(const UnionArray& a, const UnionArray& b) return *(a.getUnion().get())==*(b.getUnion().get()); } +bool operator==(const BoundedString& a, const BoundedString& b) +{ + if(&a==&b) + return true; + return a.getMaximumLength()==b.getMaximumLength(); +} + // PVXXX object comparison namespace { diff --git a/src/factory/FieldCreateFactory.cpp b/src/factory/FieldCreateFactory.cpp index 36e9971..ff7feb7 100644 --- a/src/factory/FieldCreateFactory.cpp +++ b/src/factory/FieldCreateFactory.cpp @@ -111,6 +111,38 @@ void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*contro throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead"); } + + + +std::string BoundedString::getID() const +{ + std::ostringstream id; + id << Scalar::getID() << '(' << maxLength << ')'; + return id.str(); +} + +void BoundedString::serialize(ByteBuffer *buffer, SerializableControl *control) const +{ + control->ensureBuffer(1); + buffer->putByte(0x83); + SerializeHelper::writeSize(maxLength, buffer, control); +} + +std::size_t BoundedString::getMaximumLength() const +{ + return maxLength; +} + +BoundedString::BoundedString(std::size_t maxStringLength) : + Scalar(pvString), maxLength(maxStringLength) +{ + if (maxLength == 0) + throw std::invalid_argument("maxLength == 0"); +} + +BoundedString::~BoundedString() {} + + static string emptyStringtring; static void serializeStructureField(const Structure* structure, ByteBuffer* buffer, SerializableControl* control) @@ -684,6 +716,12 @@ FieldBuilderPtr FieldBuilder::add(string const & name, ScalarType scalarType) return shared_from_this(); } +FieldBuilderPtr FieldBuilder::addBoundedString(std::string const & name, std::size_t maxLength) +{ + fields.push_back(fieldCreate->createBoundedString(maxLength)); fieldNames.push_back(name); + return shared_from_this(); +} + FieldBuilderPtr FieldBuilder::add(string const & name, FieldConstPtr const & field) { fields.push_back(field); fieldNames.push_back(name); @@ -719,6 +757,10 @@ FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const fields.push_back(fieldCreate->createUnionArray(static_pointer_cast(element))); break; case scalar: + + if (std::tr1::dynamic_pointer_cast(element).get()) + throw std::invalid_argument("bounded string arrays are not supported"); + fields.push_back(fieldCreate->createScalarArray(static_pointer_cast(element)->getScalarType())); break; // scalarArray? @@ -828,7 +870,15 @@ ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const return scalars[scalarType]; } - + +BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const +{ + // TODO use std::make_shared + std::tr1::shared_ptr s(new BoundedString(maxLength), Field::Deleter()); + BoundedStringConstPtr sa = s; + return sa; +} + ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const { if(elementType<0 || elementType>MAX_SCALAR_TYPE) @@ -1047,6 +1097,20 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl // Type type = Type.union; variant union (aka any type) return variantUnion; } + else if (typeCode == 0x83) + { + // TODO cache? + // bounded string + + size_t size = SerializeHelper::readSize(buffer, control); + + // TODO use std::make_shared + std::tr1::shared_ptr sp( + new BoundedString(size), + Field::Deleter()); + FieldConstPtr p = sp; + return p; + } else throw std::invalid_argument("invalid type encoding"); } diff --git a/src/factory/PVDataCreateFactory.cpp b/src/factory/PVDataCreateFactory.cpp index 97f3800..e88543c 100644 --- a/src/factory/PVDataCreateFactory.cpp +++ b/src/factory/PVDataCreateFactory.cpp @@ -145,11 +145,18 @@ public: SerializableControl *pflusher, size_t offset, size_t count) const; private: string value; + std::size_t maxLength; }; BasePVString::BasePVString(ScalarConstPtr const & scalar) : PVString(scalar),value() -{} +{ + BoundedStringConstPtr boundedString = std::tr1::dynamic_pointer_cast(scalar); + if (boundedString.get()) + maxLength = boundedString->getMaximumLength(); + else + maxLength = 0; +} BasePVString::~BasePVString() {} @@ -157,6 +164,9 @@ string BasePVString::get() const { return value;} void BasePVString::put(string val) { + if (maxLength > 0 && val.length() > maxLength) + throw std::overflow_error("string too long"); + value = val; postPut(); } diff --git a/src/pv/convert.h b/src/pv/convert.h index e6cdc40..edb2abf 100644 --- a/src/pv/convert.h +++ b/src/pv/convert.h @@ -35,6 +35,7 @@ bool epicsShareExtern operator==(const Structure&, const Structure&); bool epicsShareExtern operator==(const StructureArray&, const StructureArray&); bool epicsShareExtern operator==(const Union&, const Union&); bool epicsShareExtern operator==(const UnionArray&, const UnionArray&); +bool epicsShareExtern operator==(const BoundedString&, const BoundedString&); static inline bool operator!=(const Field& a, const Field& b) {return !(a==b);} @@ -50,6 +51,8 @@ static inline bool operator!=(const Union& a, const Union& b) {return !(a==b);} static inline bool operator!=(const UnionArray& a, const UnionArray& b) {return !(a==b);} +static inline bool operator!=(const BoundedString& a, const BoundedString& b) +{return !(a==b);} /** diff --git a/src/pv/pvIntrospect.h b/src/pv/pvIntrospect.h index 68ca43a..1dab6d9 100644 --- a/src/pv/pvIntrospect.h +++ b/src/pv/pvIntrospect.h @@ -93,6 +93,8 @@ class StructureArray; class Union; class UnionArray; +class BoundedString; + /** * typedef for a shared pointer to an immutable Field. */ @@ -129,6 +131,10 @@ typedef std::tr1::shared_ptr UnionConstPtr; * typedef for a shared pointer to an immutable UnionArray. */ typedef std::tr1::shared_ptr UnionArrayConstPtr; +/** + * typedef for a shared pointer to an immutable BoundedString. + */ +typedef std::tr1::shared_ptr BoundedStringConstPtr; /** * Definition of support field types. @@ -366,6 +372,33 @@ private: friend class ScalarArray; friend class BoundedScalarArray; friend class FixedScalarArray; + friend class BoundedString; +}; + +/** + * This class implements introspection object for BoundedString. + */ +class epicsShareClass BoundedString : public Scalar{ +public: + POINTER_DEFINITIONS(BoundedString); + /** + * Destructor. + */ + virtual ~BoundedString(); + typedef BoundedString& reference; + typedef const BoundedString& const_reference; + + virtual std::string getID() const; + + virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const; + + std::size_t getMaximumLength() const; + +protected: + BoundedString(std::size_t maxStringLength); +private: + std::size_t maxLength; + friend class FieldCreate; }; /** @@ -812,6 +845,14 @@ public: */ FieldBuilderPtr add(std::string const & name, ScalarType scalarType); + /** + * Add a {@code BoundedString}. + * @param name name of the array. + * @param maxLength a string maximum length. + * @return this instance of a {@code FieldBuilder}. + */ + FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength); + /** * Add a {@code Field} (e.g. {@code Structure}, {@code Union}). * @param name name of the array. @@ -961,6 +1002,13 @@ public: * @throws An {@code IllegalArgumentException} if an illegal type is specified. */ ScalarConstPtr createScalar(ScalarType scalarType) const; + /** + * Create a {@code BoundedString}. + * @param maxLength a string maximum length. + * @return a {@code BoundedString} interface for the newly created object. + * @throws An {@code IllegalArgumentException} if maxLength == 0. + */ + BoundedStringConstPtr createBoundedString(std::size_t maxLength) const; /** * Create an {@code Array} field, variable size array. * @param elementType The {@code scalarType} for array elements diff --git a/testApp/misc/testSerialization.cpp b/testApp/misc/testSerialization.cpp index 276a388..b8db603 100644 --- a/testApp/misc/testSerialization.cpp +++ b/testApp/misc/testSerialization.cpp @@ -720,6 +720,38 @@ void testArraySizeType() { serializationTest(pvs); } +void testBoundedString() { + testDiag("Testing bounded string..."); + + FieldCreatePtr fieldCreate = getFieldCreate(); + + StructureConstPtr s = fieldCreate->createFieldBuilder()-> + add("str", pvString)-> + addBoundedString("boundedStr", 8)-> + add("scalar", pvDouble)-> + createStructure(); + testOk1(s.get() != 0); + testOk1(Structure::DEFAULT_ID == s->getID()); + testOk1(3 == s->getFields().size()); + + serializationFieldTest(s); + PVStructurePtr pvs = getPVDataCreate()->createPVStructure(s); + serializationTest(pvs); + + PVStringPtr pvStr = pvs->getSubField("boundedStr"); + pvStr->put(""); + pvStr->put("small"); + pvStr->put("exact123"); + + try { + pvStr->put("tooLargeString"); + testFail("too large string accepted"); + } catch (std::overflow_error oe) { + // OK + } + +} + void testStringCopy() { string s1 = "abc"; string s2 = s1; @@ -731,7 +763,7 @@ void testStringCopy() { MAIN(testSerialization) { - testPlan(221); + testPlan(226); flusher = new SerializableControlImpl(); control = new DeserializableControlImpl(); @@ -751,6 +783,7 @@ MAIN(testSerialization) { testUnion(); testArraySizeType(); + testBoundedString(); delete buffer; diff --git a/testApp/pv/testIntrospect.cpp b/testApp/pv/testIntrospect.cpp index cf6a4b7..1ddc910 100644 --- a/testApp/pv/testIntrospect.cpp +++ b/testApp/pv/testIntrospect.cpp @@ -225,6 +225,21 @@ static void testUnion() } +static void testBoundedString() +{ + testDiag("testBoundedString"); + + BoundedStringConstPtr bs = fieldCreate->createBoundedString(8); + + Type type = bs->getType(); + testOk1(type==scalar); + + ScalarType scalarType = bs->getScalarType(); + testOk1(scalarType==pvString); + + testOk1(bs->getMaximumLength()==8); +} + #define testExcept(EXCEPT, CMD) try{ CMD; testFail( "No exception from: " #CMD); } \ catch(EXCEPT& e) {testPass("Got expected exception from: " #CMD);} \ @@ -296,7 +311,7 @@ static void testMapping() MAIN(testIntrospect) { - testPlan(324); + testPlan(327); fieldCreate = getFieldCreate(); pvDataCreate = getPVDataCreate(); standardField = getStandardField(); @@ -304,6 +319,7 @@ MAIN(testIntrospect) testScalarArray(); testStructure(); testUnion(); + testBoundedString(); testError(); testMapping(); return testDone();