diff options
Diffstat (limited to 'cpp/src/qpid/framing/FieldTable.cpp')
-rw-r--r-- | cpp/src/qpid/framing/FieldTable.cpp | 219 |
1 files changed, 202 insertions, 17 deletions
diff --git a/cpp/src/qpid/framing/FieldTable.cpp b/cpp/src/qpid/framing/FieldTable.cpp index f80d2f9fb1..0f7140b627 100644 --- a/cpp/src/qpid/framing/FieldTable.cpp +++ b/cpp/src/qpid/framing/FieldTable.cpp @@ -28,29 +28,93 @@ #include "qpid/Msg.h" #include <assert.h> +// The locking rationale in the FieldTable seems a little odd, but it +// maintains the concurrent guarantees and requirements that were in +// place before the cachedBytes/cachedSize were added: +// +// The FieldTable client code needs to make sure that they call no write +// operation in parallel with any other operation on the FieldTable. +// However multiple parallel read operations are safe. +// +// To this end the only code that is locked is code that can transparently +// change the state of the FieldTable during a read only operation. +// (In other words the code that required the mutable members in the class +// definition!) +// namespace qpid { + +using sys::Mutex; +using sys::ScopedLock; + namespace framing { +FieldTable::FieldTable() : + cachedSize(0), + newBytes(false) +{ +} + FieldTable::FieldTable(const FieldTable& ft) { - *this = ft; + ScopedLock<Mutex> l(ft.lock); // lock _source_ FieldTable + + cachedBytes = ft.cachedBytes; + cachedSize = ft.cachedSize; + newBytes = ft.newBytes; + + // Only copy the values if we have no raw data + // - copying the map is expensive and we can + // reconstruct it if necessary from the raw data + if (cachedBytes) { + newBytes = true; + return; + } + // In practice Encoding the source field table and only copying + // the encoded bytes is faster than copying the whole value map. + // (Because we nearly always copy a field table internally before + // encoding it to send, but don't change it after the copy) + if (!ft.values.empty()) { + // Side effect of getting encoded size will cache it in ft.cachedSize + ft.cachedBytes = boost::shared_array<uint8_t>(new uint8_t[ft.encodedSize()]); + + Buffer buffer((char*)&ft.cachedBytes[0], ft.cachedSize); + + // Cut and paste ahead... + buffer.putLong(ft.encodedSize() - 4); + buffer.putLong(ft.values.size()); + for (ValueMap::const_iterator i = ft.values.begin(); i!=ft.values.end(); ++i) { + buffer.putShortString(i->first); + i->second->encode(buffer); + } + + cachedBytes = ft.cachedBytes; + cachedSize = ft.cachedSize; + newBytes = true; + } } FieldTable& FieldTable::operator=(const FieldTable& ft) { - clear(); - values = ft.values; - return *this; + FieldTable nft(ft); + values.swap(nft.values); + cachedBytes.swap(nft.cachedBytes); + cachedSize = nft.cachedSize; + newBytes = nft.newBytes; + return (*this); } -FieldTable::~FieldTable() {} - uint32_t FieldTable::encodedSize() const { + ScopedLock<Mutex> l(lock); + + if (cachedSize != 0) { + return cachedSize; + } uint32_t len(4/*size field*/ + 4/*count field*/); for(ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { // shortstr_len_byte + key size + value size - len += 1 + (i->first).size() + (i->second)->encodedSize(); + len += 1 + (i->first).size() + (i->second)->encodedSize(); } + cachedSize = len; return len; } @@ -66,6 +130,7 @@ std::ostream& operator<<(std::ostream& out, const FieldTable::ValueMap::value_ty } std::ostream& operator<<(std::ostream& out, const FieldTable& t) { + t.realDecode(); out << "{"; FieldTable::ValueMap::const_iterator i = t.begin(); if (i != t.end()) out << *i++; @@ -77,48 +142,70 @@ std::ostream& operator<<(std::ostream& out, const FieldTable& t) { } void FieldTable::set(const std::string& name, const ValuePtr& value){ + realDecode(); values[name] = value; + flushRawCache(); } void FieldTable::setString(const std::string& name, const std::string& value){ + realDecode(); values[name] = ValuePtr(new Str16Value(value)); + flushRawCache(); } void FieldTable::setInt(const std::string& name, const int value){ + realDecode(); values[name] = ValuePtr(new IntegerValue(value)); + flushRawCache(); } void FieldTable::setInt64(const std::string& name, const int64_t value){ + realDecode(); values[name] = ValuePtr(new Integer64Value(value)); + flushRawCache(); } void FieldTable::setTimestamp(const std::string& name, const uint64_t value){ + realDecode(); values[name] = ValuePtr(new TimeValue(value)); + flushRawCache(); } void FieldTable::setUInt64(const std::string& name, const uint64_t value){ + realDecode(); values[name] = ValuePtr(new Unsigned64Value(value)); + flushRawCache(); } void FieldTable::setTable(const std::string& name, const FieldTable& value) { + realDecode(); values[name] = ValuePtr(new FieldTableValue(value)); + flushRawCache(); } void FieldTable::setArray(const std::string& name, const Array& value) { + realDecode(); values[name] = ValuePtr(new ArrayValue(value)); + flushRawCache(); } void FieldTable::setFloat(const std::string& name, const float value){ + realDecode(); values[name] = ValuePtr(new FloatValue(value)); + flushRawCache(); } void FieldTable::setDouble(const std::string& name, double value){ + realDecode(); values[name] = ValuePtr(new DoubleValue(value)); + flushRawCache(); } FieldTable::ValuePtr FieldTable::get(const std::string& name) const { + // Ensure we have any values we're trying to read + realDecode(); ValuePtr value; ValueMap::const_iterator i = values.find(name); if ( i!=values.end() ) @@ -188,37 +275,82 @@ bool FieldTable::getDouble(const std::string& name, double& value) const { //} void FieldTable::encode(Buffer& buffer) const { - buffer.putLong(encodedSize() - 4); - buffer.putLong(values.size()); - for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { - buffer.putShortString(i->first); - i->second->encode(buffer); + // If we've still got the input field table + // we can just copy it directly to the output + if (cachedBytes) { + ScopedLock<Mutex> l(lock); + buffer.putRawData(&cachedBytes[0], cachedSize); + } else { + buffer.putLong(encodedSize() - 4); + buffer.putLong(values.size()); + for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { + buffer.putShortString(i->first); + i->second->encode(buffer); + } } } +// Decode lazily - just record the raw bytes until we need them void FieldTable::decode(Buffer& buffer){ - clear(); if (buffer.available() < 4) throw IllegalArgumentException(QPID_MSG("Not enough data for field table.")); + uint32_t p = buffer.getPosition(); uint32_t len = buffer.getLong(); if (len) { uint32_t available = buffer.available(); if ((available < len) || (available < 4)) throw IllegalArgumentException(QPID_MSG("Not enough data for field table.")); + } + ScopedLock<Mutex> l(lock); + // Throw away previous stored values + values.clear(); + // Copy data into our buffer + cachedBytes = boost::shared_array<uint8_t>(new uint8_t[len + 4]); + cachedSize = len + 4; + newBytes = true; + buffer.setPosition(p); + buffer.getRawData(&cachedBytes[0], cachedSize); +} + +void FieldTable::realDecode() const +{ + ScopedLock<Mutex> l(lock); + + // If we've got no raw data stored up then nothing to do + if (!newBytes) + return; + + Buffer buffer((char*)&cachedBytes[0], cachedSize); + uint32_t len = buffer.getLong(); + if (len) { + uint32_t available = buffer.available(); uint32_t count = buffer.getLong(); uint32_t leftover = available - len; while(buffer.available() > leftover && count--){ std::string name; ValuePtr value(new FieldValue); - + buffer.getShortString(name); value->decode(buffer); values[name] = ValuePtr(value); - } + } } + newBytes = false; +} + +void FieldTable::flushRawCache() +{ + ScopedLock<Mutex> l(lock); + // We can only flush the cache if there are no cached bytes to decode + assert(newBytes==false); + // Avoid recreating shared array unless we actually have one. + if (cachedBytes) cachedBytes.reset(); + cachedSize = 0; } bool FieldTable::operator==(const FieldTable& x) const { + realDecode(); + x.realDecode(); if (values.size() != x.values.size()) return false; for (ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { ValueMap::const_iterator j = x.values.find(i->first); @@ -230,20 +362,73 @@ bool FieldTable::operator==(const FieldTable& x) const { void FieldTable::erase(const std::string& name) { - if (values.find(name) != values.end()) + realDecode(); + if (values.find(name) != values.end()) { values.erase(name); + flushRawCache(); + } +} + +void FieldTable::clear() +{ + values.clear(); + newBytes = false; + flushRawCache(); +} + +// Map-like interface. +FieldTable::ValueMap::const_iterator FieldTable::begin() const +{ + realDecode(); + return values.begin(); +} + +FieldTable::ValueMap::const_iterator FieldTable::end() const +{ + realDecode(); + return values.end(); +} + +FieldTable::ValueMap::const_iterator FieldTable::find(const std::string& s) const +{ + realDecode(); + return values.find(s); +} + +FieldTable::ValueMap::iterator FieldTable::begin() +{ + realDecode(); + flushRawCache(); + return values.begin(); +} + +FieldTable::ValueMap::iterator FieldTable::end() +{ + realDecode(); + flushRawCache(); + return values.end(); +} + +FieldTable::ValueMap::iterator FieldTable::find(const std::string& s) +{ + realDecode(); + flushRawCache(); + return values.find(s); } std::pair<FieldTable::ValueMap::iterator, bool> FieldTable::insert(const ValueMap::value_type& value) { + realDecode(); + flushRawCache(); return values.insert(value); } FieldTable::ValueMap::iterator FieldTable::insert(ValueMap::iterator position, const ValueMap::value_type& value) { + realDecode(); + flushRawCache(); return values.insert(position, value); } - } } |