Reducing the overhead between ChatlineModelItem and MessageModelItem.
authorMarcus Eggenberger <egs@quassel-irc.org>
Tue, 13 Jan 2009 15:34:18 +0000 (16:34 +0100)
committerMarcus Eggenberger <egs@quassel-irc.org>
Tue, 13 Jan 2009 15:35:09 +0000 (16:35 +0100)
This concludes my work on restructuring the MessageModel to not be
such a malloc hog. Also it seems that performance can be further
increased by reducing virtual calls on the MessageModelItems.

src/client/messagemodel.cpp
src/client/messagemodel.h
src/qtui/chatlinemodelitem.cpp
src/qtui/chatlinemodelitem.h
src/uisupport/uistyle.cpp
src/uisupport/uistyle.h

index 76943d4..ca77bfd 100644 (file)
@@ -409,13 +409,6 @@ void MessageModel::buffersPermanentlyMerged(BufferId bufferId1, BufferId bufferI
 // ========================================
 //  MessageModelItem
 // ========================================
 // ========================================
 //  MessageModelItem
 // ========================================
-MessageModelItem::MessageModelItem(const Message &msg)
-  : _msg(msg)
-{
-  if(!_msg.sender().contains('!'))
-    _msg.setFlags(_msg.flags() |= Message::ServerMsg);
-}
-
 QVariant MessageModelItem::data(int column, int role) const {
   if(column < MessageModel::TimestampColumn || column > MessageModel::ContentsColumn)
     return QVariant();
 QVariant MessageModelItem::data(int column, int role) const {
   if(column < MessageModel::TimestampColumn || column > MessageModel::ContentsColumn)
     return QVariant();
index 5da8309..0d540cd 100644 (file)
@@ -118,19 +118,19 @@ public:
    *  Subclasses need to provide Qt::DisplayRole at least, which should describe the plaintext
    *  strings without formattings (e.g. for searching purposes).
    */
    *  Subclasses need to provide Qt::DisplayRole at least, which should describe the plaintext
    *  strings without formattings (e.g. for searching purposes).
    */
-  MessageModelItem(const Message &);
+  MessageModelItem() {}
   inline virtual ~MessageModelItem() {}
 
   virtual QVariant data(int column, int role) const;
   virtual bool setData(int column, const QVariant &value, int role);
 
   inline virtual ~MessageModelItem() {}
 
   virtual QVariant data(int column, int role) const;
   virtual bool setData(int column, const QVariant &value, int role);
 
-  inline const Message &message() const { return _msg; }
-  inline const QDateTime &timestamp() const { return _msg.timestamp(); }
-  inline const MsgId &msgId() const { return _msg.msgId(); }
-  inline const BufferId &bufferId() const { return _msg.bufferId(); }
-  inline void setBufferId(BufferId bufferId) { _msg.setBufferId(bufferId); }
-  inline Message::Type msgType() const { return _msg.type(); }
-  inline Message::Flags msgFlags() const { return _msg.flags(); }
+  virtual const Message &message() const = 0;
+  virtual const QDateTime &timestamp() const = 0;
+  virtual const MsgId &msgId() const = 0;
+  virtual const BufferId &bufferId() const = 0;
+  virtual void setBufferId(BufferId bufferId) = 0;
+  virtual Message::Type msgType() const = 0;
+  virtual Message::Flags msgFlags() const = 0;
 
   // For sorting
   bool operator<(const MessageModelItem &) const;
 
   // For sorting
   bool operator<(const MessageModelItem &) const;
index a3de834..2264fa5 100644 (file)
@@ -37,108 +37,93 @@ typedef struct {
     unsigned unused                  :2;
 } HB_CharAttributes_Dummy;
 
     unsigned unused                  :2;
 } HB_CharAttributes_Dummy;
 
-// PRIVATE DATA FOR CHATLINE MODEL ITEM
-class ChatLineModelItemPrivate {
 
 
-public:
-  inline ChatLineModelItemPrivate(const Message &msg);
-  ~ChatLineModelItemPrivate();
-
-  inline bool needsStyling();
-  QVariant data(MessageModel::ColumnType column, int role);
-
-private:
-  void style();
-  void computeWrapList();
-
-  ChatLineModel::WrapList _wrapList;
-  Message *_msgBuffer;
-  UiStyle::StyledMessage *_styledMsg;
-
-  static unsigned char *TextBoundaryFinderBuffer;
-  static int TextBoundaryFinderBufferSize;
-};
-
-ChatLineModelItemPrivate::ChatLineModelItemPrivate(const Message &msg)
-: _msgBuffer(new Message(msg)),
-  _styledMsg(0) {
+unsigned char *ChatLineModelItem::TextBoundaryFinderBuffer = (unsigned char *)malloc(512 * sizeof(HB_CharAttributes_Dummy));
+int ChatLineModelItem::TextBoundaryFinderBufferSize = 512 * (sizeof(HB_CharAttributes_Dummy) / sizeof(unsigned char));
 
 
+// ****************************************
+// the actual ChatLineModelItem
+// ****************************************
+ChatLineModelItem::ChatLineModelItem(const Message &msg)
+  : MessageModelItem(),
+    _styledMsg(msg)
+{
+  if(!msg.sender().contains('!'))
+    _styledMsg.setFlags(msg.flags() |= Message::ServerMsg);
 }
 
 }
 
-ChatLineModelItemPrivate::~ChatLineModelItemPrivate() {
-  if(_msgBuffer) {
-    delete _msgBuffer;
-  } else {
-    delete _styledMsg;
+QVariant ChatLineModelItem::data(int column, int role) const {
+  MessageModel::ColumnType col = (MessageModel::ColumnType)column;
+  QVariant variant;
+  switch(col) {
+  case ChatLineModel::TimestampColumn:
+    variant = timestampData(role);
+    break;
+  case ChatLineModel::SenderColumn:
+    variant = senderData(role);
+    break;
+  case ChatLineModel::ContentsColumn:
+    variant = contentsData(role);
+    break;
+  default:
+    break;
   }
   }
+  if(!variant.isValid())
+    return MessageModelItem::data(column, role);
+  return variant;
 }
 
 }
 
-bool ChatLineModelItemPrivate::needsStyling() {
-  return (bool)_msgBuffer;
+QVariant ChatLineModelItem::timestampData(int role) const {
+  switch(role) {
+  case ChatLineModel::DisplayRole:
+    return _styledMsg.decoratedTimestamp();
+  case ChatLineModel::EditRole:
+    return _styledMsg.timestamp();
+  case ChatLineModel::FormatRole:
+    return QVariant::fromValue<UiStyle::FormatList>(UiStyle::FormatList() << qMakePair((quint16)0, (quint32)_styledMsg.timestampFormat()));
+  }
+  return QVariant();
 }
 
 }
 
-QVariant ChatLineModelItemPrivate::data(MessageModel::ColumnType column, int role) {
-  if(needsStyling())
-    style();
-  switch(column) {
-    case ChatLineModel::TimestampColumn:
-      switch(role) {
-        case ChatLineModel::DisplayRole:
-          return _styledMsg->decoratedTimestamp();
-        case ChatLineModel::EditRole:
-          return _styledMsg->timestamp();
-        case ChatLineModel::FormatRole:
-          return QVariant::fromValue<UiStyle::FormatList>(UiStyle::FormatList()
-                                    << qMakePair((quint16)0, (quint32)_styledMsg->timestampFormat()));
-      }
-      break;
-    case ChatLineModel::SenderColumn:
-      switch(role) {
-        case ChatLineModel::DisplayRole:
-          return _styledMsg->decoratedSender();
-        case ChatLineModel::EditRole:
-          return _styledMsg->plainSender();
-        case ChatLineModel::FormatRole:
-          return QVariant::fromValue<UiStyle::FormatList>(UiStyle::FormatList()
-                                    << qMakePair((quint16)0, (quint32)_styledMsg->senderFormat()));
-      }
-      break;
-    case ChatLineModel::ContentsColumn:
-      switch(role) {
-        case ChatLineModel::DisplayRole:
-        case ChatLineModel::EditRole:
-          return _styledMsg->plainContents();
-        case ChatLineModel::FormatRole:
-          return QVariant::fromValue<UiStyle::FormatList>(_styledMsg->contentsFormatList());
-        case ChatLineModel::WrapListRole:
-          if(_wrapList.isEmpty())
-            computeWrapList();
-          return QVariant::fromValue<ChatLineModel::WrapList>(_wrapList);
-      }
-      break;
-    default:
-      Q_ASSERT(false);
-      return 0;
+QVariant ChatLineModelItem::senderData(int role) const {
+  switch(role) {
+  case ChatLineModel::DisplayRole:
+    return _styledMsg.decoratedSender();
+  case ChatLineModel::EditRole:
+    return _styledMsg.plainSender();
+  case ChatLineModel::FormatRole:
+    return QVariant::fromValue<UiStyle::FormatList>(UiStyle::FormatList() << qMakePair((quint16)0, (quint32)_styledMsg.senderFormat()));
   }
   return QVariant();
 }
 
   }
   return QVariant();
 }
 
-void ChatLineModelItemPrivate::style() {
-  _styledMsg = new QtUiStyle::StyledMessage(*_msgBuffer);
-  _styledMsg->style(QtUi::style());
-  delete _msgBuffer;
-  _msgBuffer = 0;
+QVariant ChatLineModelItem::contentsData(int role) const {
+  if(_styledMsg.needsStyling())
+    _styledMsg.style(QtUi::style());
+
+  switch(role) {
+  case ChatLineModel::DisplayRole:
+  case ChatLineModel::EditRole:
+    return _styledMsg.plainContents();
+  case ChatLineModel::FormatRole:
+    return QVariant::fromValue<UiStyle::FormatList>(_styledMsg.contentsFormatList());
+  case ChatLineModel::WrapListRole:
+    if(_wrapList.isEmpty())
+      computeWrapList();
+    return QVariant::fromValue<ChatLineModel::WrapList>(_wrapList);
+  }
+  return QVariant();
 }
 
 }
 
-void ChatLineModelItemPrivate::computeWrapList() {
-  int length = _styledMsg->contents().length();
+void ChatLineModelItem::computeWrapList() const {
+  int length = _styledMsg.plainContents().length();
   if(!length)
     return;
 
   enum Mode { SearchStart, SearchEnd };
 
   QList<ChatLineModel::Word> wplist;  // use a temp list which we'll later copy into a QVector for efficiency
   if(!length)
     return;
 
   enum Mode { SearchStart, SearchEnd };
 
   QList<ChatLineModel::Word> wplist;  // use a temp list which we'll later copy into a QVector for efficiency
-  QTextBoundaryFinder finder(QTextBoundaryFinder::Word, _styledMsg->contents().unicode(), length,
+  QTextBoundaryFinder finder(QTextBoundaryFinder::Word, _styledMsg.plainContents().unicode(), length,
                               TextBoundaryFinderBuffer, TextBoundaryFinderBufferSize);
 
   int idx;
                               TextBoundaryFinderBuffer, TextBoundaryFinderBufferSize);
 
   int idx;
@@ -150,12 +135,12 @@ void ChatLineModelItemPrivate::computeWrapList() {
   word.start = 0;
   qreal wordstartx = 0;
 
   word.start = 0;
   qreal wordstartx = 0;
 
-  QTextLayout layout(_styledMsg->contents());
+  QTextLayout layout(_styledMsg.plainContents());
   QTextOption option;
   option.setWrapMode(QTextOption::NoWrap);
   layout.setTextOption(option);
 
   QTextOption option;
   option.setWrapMode(QTextOption::NoWrap);
   layout.setTextOption(option);
 
-  layout.setAdditionalFormats(QtUi::style()->toTextLayoutList(_styledMsg->contentsFormatList(), length));
+  layout.setAdditionalFormats(QtUi::style()->toTextLayoutList(_styledMsg.contentsFormatList(), length));
   layout.beginLayout();
   QTextLine line = layout.createLine();
   line.setNumColumns(length);
   layout.beginLayout();
   QTextLine line = layout.createLine();
   line.setNumColumns(length);
@@ -204,30 +189,3 @@ void ChatLineModelItemPrivate::computeWrapList() {
   }
 }
 
   }
 }
 
-unsigned char *ChatLineModelItemPrivate::TextBoundaryFinderBuffer = (unsigned char *)malloc(512 * sizeof(HB_CharAttributes_Dummy));
-int ChatLineModelItemPrivate::TextBoundaryFinderBufferSize = 512 * (sizeof(HB_CharAttributes_Dummy) / sizeof(unsigned char));
-
-// ****************************************
-// the actual ChatLineModelItem
-// ****************************************
-ChatLineModelItem::ChatLineModelItem(const Message &msg)
-  : MessageModelItem(msg),
-    _data(new ChatLineModelItemPrivate(msg))
-{
-}
-
-ChatLineModelItem::ChatLineModelItem(const ChatLineModelItem &other)
-  : MessageModelItem(other)
-{
-  _data = new ChatLineModelItemPrivate(message());
-}
-
-ChatLineModelItem::~ChatLineModelItem() {
-  delete _data;
-}
-
-QVariant ChatLineModelItem::data(int column, int role) const {
-  QVariant d = _data->data((MessageModel::ColumnType)column, role);
-  if(!d.isValid()) return MessageModelItem::data(column, role);
-  return d;
-}
index d4979cd..5382a7b 100644 (file)
 
 #include "messagemodel.h"
 
 
 #include "messagemodel.h"
 
-class ChatLineModelItemPrivate;
+#include "uistyle.h"
 
 class ChatLineModelItem : public MessageModelItem {
 public:
   ChatLineModelItem(const Message &);
 
 class ChatLineModelItem : public MessageModelItem {
 public:
   ChatLineModelItem(const Message &);
-  ChatLineModelItem(const ChatLineModelItem &other);
-  ~ChatLineModelItem();
 
   virtual QVariant data(int column, int role) const;
 
 
   virtual QVariant data(int column, int role) const;
 
+  virtual inline const Message &message() const { return _styledMsg; }
+  virtual inline const QDateTime &timestamp() const { return _styledMsg.timestamp(); }
+  virtual inline const MsgId &msgId() const { return _styledMsg.msgId(); }
+  virtual inline const BufferId &bufferId() const { return _styledMsg.bufferId(); }
+  virtual inline void setBufferId(BufferId bufferId) { _styledMsg.setBufferId(bufferId); }
+  virtual inline Message::Type msgType() const { return _styledMsg.type(); }
+  virtual inline Message::Flags msgFlags() const { return _styledMsg.flags(); }
+
   /// Used to store information about words to be used for wrapping
   struct Word {
     quint16 start;
   /// Used to store information about words to be used for wrapping
   struct Word {
     quint16 start;
@@ -43,7 +49,17 @@ public:
   typedef QVector<Word> WrapList;
 
 private:
   typedef QVector<Word> WrapList;
 
 private:
-  ChatLineModelItemPrivate *_data;
+  virtual QVariant timestampData(int role) const;
+  virtual QVariant senderData(int role) const;
+  virtual QVariant contentsData(int role) const;
+
+  void computeWrapList() const;
+
+  mutable WrapList _wrapList;
+  UiStyle::StyledMessage _styledMsg;
+
+  static unsigned char *TextBoundaryFinderBuffer;
+  static int TextBoundaryFinderBufferSize;
 };
 
 #endif
 };
 
 #endif
index 141ff33..dafcc5f 100644 (file)
@@ -318,7 +318,7 @@ UiStyle::StyledMessage::StyledMessage(const Message &msg)
 {
 }
 
 {
 }
 
-void UiStyle::StyledMessage::style(UiStyle *style) {
+void UiStyle::StyledMessage::style(UiStyle *style) const {
   QString user = userFromMask(sender());
   QString host = hostFromMask(sender());
   QString nick = nickFromMask(sender());
   QString user = userFromMask(sender());
   QString host = hostFromMask(sender());
   QString nick = nickFromMask(sender());
@@ -442,6 +442,7 @@ UiStyle::FormatType UiStyle::StyledMessage::senderFormat() const {
   }
 }
 
   }
 }
 
+
 /***********************************************************************************/
 
 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList) {
 /***********************************************************************************/
 
 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList) {
index 7692227..7f0614b 100644 (file)
@@ -156,20 +156,23 @@ class UiStyle::StyledMessage : public Message {
 public:
   explicit StyledMessage(const Message &message);
 
 public:
   explicit StyledMessage(const Message &message);
 
+  //! Styling is only needed for calls to plainContents() and contentsFormatList()
+  // StyledMessage can't style lazily by itself, as it doesn't know the used style
   bool inline needsStyling() const { return _contents.plainText.isNull(); }
   bool inline needsStyling() const { return _contents.plainText.isNull(); }
-  void style(UiStyle *style);
+  void style(UiStyle *style) const;
+
 
   QString decoratedTimestamp() const;
   QString plainSender() const;             //!< Nickname (no decorations) for Plain and Notice, empty else
   QString decoratedSender() const;
 
   QString decoratedTimestamp() const;
   QString plainSender() const;             //!< Nickname (no decorations) for Plain and Notice, empty else
   QString decoratedSender() const;
-  const QString &plainContents() const { return _contents.plainText; }
+  inline const QString &plainContents() const { return _contents.plainText; }
 
   inline FormatType timestampFormat() const { return UiStyle::Timestamp; }
   FormatType senderFormat() const;
   inline const FormatList &contentsFormatList() const { return _contents.formatList; }
 
 private:
 
   inline FormatType timestampFormat() const { return UiStyle::Timestamp; }
   FormatType senderFormat() const;
   inline const FormatList &contentsFormatList() const { return _contents.formatList; }
 
 private:
-  StyledString _contents;
+  mutable StyledString _contents;
 };
 
 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList);
 };
 
 QDataStream &operator<<(QDataStream &out, const UiStyle::FormatList &formatList);