Remoto - VFS: VFS_datastore.cpp Source File
Remoto - VFS
VFS_datastore.cpp
Go to the documentation of this file.
1 
2 #include <QTimerEvent>
3 #include <QtGlobal>
4 //#include <QThread>
5 
6 #include "VFS.h"
7 #include "VFS_datastore.h"
8 
31 VFS_datastore_cache_entry::VFS_datastore_cache_entry(bool debug, int flushInterval, int expireInterval, bool raw)
32 : QObject()
33 , _data()
34 , _rawData()
35 , _size(0)
36 , _raw(raw)
37 , _dirty(false)
38 , _valid(false)
39 , _reason("")
40 , _debug(debug)
41 , _flushInterval(flushInterval)
42 , _expireID(0)
43 , _expireInterval(expireInterval)
44 {
45  _expireInterval = qMax(_expireInterval,flushInterval);
46 
47  if (flushInterval > 0)
48  _flushID = startTimer(flushInterval); //this always runs, regardless of touch() status, because we need to flush at some point!
49 
50  touch(); //start the expire timer and set the ID
51 }
52 
58 {
59  //printf("DATASTORE CACHE ENTRY DTOR: %p '%s'\n",this,qUtf8Printable(_key));
60 
61  if (_dirty && _valid)
62  printf("WARNING: deleting dirty cache entry: '%s'\n",qUtf8Printable(_key));
63 }
64 
75 void VFS_datastore_cache_entry::timerEvent(QTimerEvent *event)
76 {
77  int id = event->timerId();
78 
79  if (id == _flushID)
80  {
81  if (!_valid)
83  VFS::CRITICAL("Deleting invalid cache entry. Data may be lost.\n"+_reason);
84  emit expired(this);
85  return;
86  }
87 
88  if (_dirty)
89  flush();
90 
91  if (_dirty) //if it's still dirty, there's a serious problem. Report it.
93  }
94  else if (id == _expireID)
95  {
96  //printf("EXPIRE DATASTORE ENTRY!\n");
97 
98  if (!_valid)
100  VFS::CRITICAL("Deleting invalid cache entry. Data may be lost.\n"+_reason);
101  emit expired(this);
102  return;
103  }
104 
105  if (_dirty)
106  flush();
107 
108  if (_dirty) //if it's still dirty, there's a serious problem. Report it.
110  else
111  emit expired(this);
112  }
113  else
114  printf("Bad timerId: %d!\n",id);
115 }
116 
122 {
123  return _size;
124 }
125 
136 {
137  //_size = QJsonDocument(json).toJson().size();
138  _data.setObject(json);
139  //int s;
140  //_data.rawData( &s ); //get the encoded size of the data
141  //_size = s;
142  _size = (quint64) _data.toJson().size();
143 
144  _dirty = true;
145 
146  touch();
147 }
148 
157 {
158  touch();
159 
160  return _data;
161 }
162 
171 {
172  _rawData = data;
173  _size = data.size();
174 
175  _dirty = true;
176 
177  touch();
178 }
179 
188 {
189  touch();
190 
191  return _rawData;
192 }
193 
201 bool VFS_datastore_cache_entry::valid(QString &reason)
202 {
203  if (!_valid)
204  reason=_reason;
205 
206  return _valid;
207 }
208 
216 {
217  if (_expireInterval>0)
218  { _expireTimer.start(_expireInterval,this); //push expiration into the future
219  _expireID = _expireTimer.timerId();
220  }
221 
222  emit touched();
223 }
224 
242 : QCache<QString, VFS_datastore_cache_entry>(size)
243 {
244 }
245 
247 {
248 }
249 
250 /*
251 VFS_datastore_cache_entry *VFS_datastore_cache::createEntry(QString p)
252 {
253  Q_UNUSED(p);
254 
255  printf("VFS_datastore_cache::createEntry() must be subclassed.\n");
256 
257  return 0;
258 }
259 
260 VFS_datastore_cache_entry *VFS_datastore_cache::operator [](QString p)
261 {
262  VFS_datastore_cache_entry *e;
263 
264  if (!(e = object(p)))
265  { e = createEntry(p);
266  if (!insert(p,e,e->size()))
267  {
268  VFS::WARN( QString("'%2' was too large to cache!").arg(p) );
269  e->deleteLater();
270  }
271  }
272 
273  return e;
274 }
275 */
276 
277 
290 {
291  quint64 size = object->size();
292  object->_key = key;
293 
294  if (QCache<QString, VFS_datastore_cache_entry>::totalCost() + size > (quint64) maxCost())
295  return false;
296 
297  //printf("DATASTORE CACHE INSERT: %p %p '%s' @ %s\n",this,object,qUtf8Printable(key),qUtf8Printable(this->thread()->objectName()));
298 
299  bool i = QCache<QString, VFS_datastore_cache_entry>::insert(key,object,size);
300 
301  if (i)
302  {
303  object->connect(object,SIGNAL(expired(VFS_datastore_cache_entry*)),this,SLOT(expired(VFS_datastore_cache_entry*)),Qt::UniqueConnection);
304  object->connect(object,SIGNAL(touched()),this,SLOT(touched()),Qt::UniqueConnection);
305  }
306 
307  return i;
308 }
309 
318 {
319  bool s = QCache<QString, VFS_datastore_cache_entry>::remove(e->_key);
320 
321  if (!s)
322  VFS::WARN("WARN: VFS_datastore_cache tried to remove an entry that wasn't cached.\n");
323 
324  //printf("%p TOTAL COST: %d\n",this,totalCost());
325 }
326 
327 
335 {
336  VFS_datastore_cache_entry *e = qobject_cast<VFS_datastore_cache_entry *>( sender() );
337 
338  if (e)
339  {
340  //assuming this updates the key in the cache
341  object(e->_key);
342  }
343  else
344  {
345  VFS::WARN("A cache entry failed to be touched.");
346  }
347 }
348 
349 
366 VFS_datastore::VFS_datastore(quint64 size, bool debug)
367 : VFS_node()
368 , _cache(size)
369 , _debug(debug)
370 {
371 }
372 
374 {
375 }
376 
377 
386 {
387  return true;
388 }
389 
390 
399 {
400  Q_UNUSED(r)
401 
402  return this;
403 }
404 
405 
412 {
413  QString s = QString("Max Cost: %1\nTotal cost: %2\nTotal entries: %3").arg(_cache.maxCost()).arg(_cache.totalCost()).arg(_cache.size());
414 
415  return s;
416 }
417 
433 QJsonObject VFS_datastore::applyJsonDiff(QJsonObject obj, QJsonObject diff, QString trace, QString user)
434 {
435  bool _debug = false;
436 
437  QJsonObject::iterator i = diff.begin();
438 
439  int L = 9;
440 
441  while (i != diff.end()) //iterate through the diff entries and apply them
442  {
443  //if the value for the entry is null
444  //delete the field
445  //if the value is an object
446  //recursively merge
447  //if the value is not an object
448  //add if not exist
449  //update/set otherwise
450 
451  if (_debug)
452  VFS::WARN( QString("%1 => %2").arg(i.key()).arg(i.value().toString()) );
453 
454  QString var = i.key();
455  QJsonValue val = i.value();
456 
457  //VFS::ERROR(QString("type: %1").arg(val.type()) );
458  //printf("type: %d\n",val.type() );
459 
460  if (val.isNull()) //remove field
461  {
462  //printf("%s apply diff case 1\n",qUtf8Printable(var));
463  obj.remove(var);
464 
465  if (_debug)
466  VFS::WARN( QString("%1%2 => (removed)").arg(trace).arg(var), L, user );
467  }
468  else if (val.isArray())
469  {
470  //printf("%s apply diff case 2\n",qUtf8Printable(var));
471  //obj[var] = val;
472  obj.insert(var,val);
473 
474  if (_debug)
475  VFS::WARN( QString("%1%2 => '%3'").arg(trace).arg(var).arg( QString(QJsonDocument(val.toArray()).toJson()).replace('\n',"") ), L, user );
476  }
477  else if (val.isObject() && obj.contains(var) && obj[var].isObject())
478  {
479  //printf("%s apply diff case 3\n",qUtf8Printable(var));
480  //obj[var] = applyJsonDiff( obj[var].toObject(), val.toObject(), trace+var+".", user );
481  obj.insert(var,applyJsonDiff( obj[var].toObject(), val.toObject(), trace+var+".", user ));
482  }
483  else //update or create field
484  {
485  //printf("%s apply diff case 4\n",qUtf8Printable(var));
486  //obj[var] = val;
487 
488  if (obj.contains(var))
489  {
490  QJsonValue vv = obj[var];
491 
492  if (vv.isObject()) //upgrade the value to a value field
493  {
494  QJsonObject oo = vv.toObject();
495  oo["value"] = val;
496  obj.insert(var,oo);
497  }
498  else
499  obj.insert(var,val);
500 
501  }
502  else
503  obj.insert(var,val);
504 
505  if (_debug)
506  {
507  if (val.isString())
508  VFS::WARN( QString("%1%2 => '%3'").arg(trace).arg(var).arg(val.toString()), L, user );
509  else if (val.isDouble())
510  VFS::WARN( QString("%1%2 => %3").arg(trace).arg(var).arg(val.toDouble()), L, user );
511  else if (val.isBool())
512  VFS::WARN( QString("%1%2 => %3").arg(trace).arg(var).arg(val.toBool() ? "true" : "false"), L, user );
513  else if (val.isObject())
514  VFS::WARN( QString("%1%2 => %3").arg(trace).arg(var).arg("[object]"), L, user );
515  else
516  VFS::WARN( QString("%1%2 => %3").arg(trace).arg(var).arg("UNKNOWN TYPE"), L, user );
517  }
518  }
519 
520  ++i;
521  }
522 
523  return obj;
524 }
525 
539 QString VFS_datastore::applyDeltaDiff(QString data, QJsonObject diff, QString trace, QString user)
540 {
541  bool _debug = true;
542  int L = 9;
543 
544  QJsonObject delta = diff["delta"].toObject();
545 
546  QString action = delta["action"].toString();
547  int srow = delta["start"].toObject()["row"].toInt();
548  int scol = delta["start"].toObject()["column"].toInt();
549  int erow = delta["end"].toObject()["row"].toInt();
550  int ecol = delta["end"].toObject()["column"].toInt();
551  QStringList dlines;
552  foreach (const QJsonValue & value, delta["lines"].toArray()) {
553  dlines << value.toString();
554  }
555 
556  //QString data = obj["data"].toString();
557  QStringList lines = data.split("\n");
558 
559  if (_debug)
560  VFS::WARN( QString("Delta: %1%2 => '%3'").arg(trace).arg(action).arg(dlines.join("\n")), L, user );
561 
562  if (action=="remove")
563  {
564  if (srow >= 0 && srow < lines.length())
565  {
566  if (srow == erow) //remove a chunk of one line
567  {
568  lines[srow].remove(scol,ecol-scol);
569  }
570  else if (erow > srow) //remove an ending and a beginning, and possibly some rows in between
571  {
572  //lines[srow].truncate(scol);
573  //lines[erow].remove(0,ecol);
574  lines[srow] = lines[srow].left(scol) + lines[erow].remove(0,ecol);
575  for (int i=srow+1,j=srow+1;i<=erow;i++)
576  lines.removeAt(j);
577  }
578  else
579  VFS::ERROR( QString("%1Bad delta. Remove start row greater than end row.").arg(trace), 0, user );
580  }
581  else
582  VFS::ERROR( QString("%1Bad delta. Remove start row out of bounds.").arg(trace), 0, user );
583  }
584  else if (action=="insert")
585  {
586  if (srow >= 0 && srow < lines.length())
587  lines[srow] = lines[srow].insert(scol,dlines.join("\n"));
588  else
589  VFS::ERROR( QString("%1Bad delta. Insert start row out of bounds.").arg(trace), 0, user );
590  }
591 
592  //obj["data"] = lines.join("\n");
593  //return obj;
594 
595  return lines.join("\n");
596 }
A pure virtual class which must be subclassed for data storage.
Definition: VFS_datastore.h:13
quint64 size()
quint64 _size
The size of the data. Always initialized to zero. Must be set in a subclass.
Definition: VFS_datastore.h:34
virtual bool valid(QString &reason)
virtual void setData(QJsonObject json)
Write data to the entry.
virtual ~VFS_datastore_cache_entry()
Destroy the cache entry.
void touched()
Signal emitted when a cache entry has made a change and should climb the cache queue.
virtual QByteArray getRawData()
Return the data from an entry.
QString _reason
The reason this may be invalid. Set by a subclass.
Definition: VFS_datastore.h:38
void expired(VFS_datastore_cache_entry *)
Signal emitted when a cache entry has expired and is no longer in use.
int _expireInterval
Expire interval, in milliseconds.
Definition: VFS_datastore.h:44
QByteArray _rawData
Raw binary data. Used if _raw=true.
Definition: VFS_datastore.h:33
virtual QJsonDocument getData()
Return the data from an entry.
bool _valid
Defaults to false. A subclass will determine if _valid is true, like for instance if an underlying fi...
Definition: VFS_datastore.h:37
bool _dirty
Changes have occurred but they are not commited to storage yet.
Definition: VFS_datastore.h:36
QBasicTimer _expireTimer
The expiration timer.
Definition: VFS_datastore.h:45
QString _key
The cache entry key.
Definition: VFS_datastore.h:26
virtual void touch()
Reset the expiration timer.
QJsonDocument _data
The actual cache data.
Definition: VFS_datastore.h:32
int _flushID
Timer ID for flush events.
Definition: VFS_datastore.h:41
virtual void timerEvent(QTimerEvent *e)
Either a flush or expire has happened.
int _expireID
Timer ID for expire events.
Definition: VFS_datastore.h:43
virtual void flush()=0
A pure virtual function that must be overridden in subclasses. This is the function that actually com...
VFS_datastore_cache_entry(bool debug, int flushInterval, int expireInterval, bool raw=false)
Construct the cache entry.
void expired(VFS_datastore_cache_entry *)
A cache entry has exipred.
bool insert(const QString &key, VFS_datastore_cache_entry *object)
Insert a new entry.
virtual ~VFS_datastore_cache()
VFS_datastore_cache(quint64 size)
void touched()
A cache entry has been touched.
VFS_datastore_cache _cache
The data cache.
bool _debug
Debug mode.
virtual bool isContainer()
All datastore subclasses are containers.
static QString applyDeltaDiff(QString data, QJsonObject delta, QString trace="", QString user="server")
Apply a delta diff to a json object.
VFS_datastore(quint64 size, bool debug=false)
static QJsonObject applyJsonDiff(QJsonObject obj, QJsonObject diff, QString trace="", QString user="server")
Apply a json diff to a json object.
virtual QString reportDetails()
Report the current cache usage.
virtual ~VFS_datastore()
virtual VFS_node * find(VFS_request *r)
VFS_node is the base class from which all other VFS_node classes derive.
Definition: VFS_node.h:143
void diff(VFS_node *origin, VFS_request *t)
Emit a diff, which will trigger notifySubscribers() for a mounted node.
The base class for all requests between nodes.
Definition: VFS_node.h:54
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
Definition: VFS.cpp:307
static void CRITICAL(QString message)
Send a message to the VFS::_critical VFS_stream.
Definition: VFS.cpp:357
static void WARN(QString message, int level=0, QString user="server")
Send a message to the VFS::_warnings VFS_stream.
Definition: VFS.cpp:258
setter user
a setter DOCME