doxygen: intern/itasc tagged
[blender.git] / intern / itasc / Cache.cpp
1 /** \file itasc/Cache.cpp
2  *  \ingroup itasc
3  */
4 /* $Id$
5  * Cache.cpp
6  *
7  *  Created on: Feb 24, 2009
8  *      Author: benoit bolsee
9  */
10 #include <string.h>
11 #include <assert.h>
12 #include <math.h>
13 #include <stdlib.h>
14 #include "Cache.hpp"
15
16 namespace iTaSC {
17
18 CacheEntry::~CacheEntry()
19 {
20    for (unsigned int id=0; id < m_count; id++)
21                 m_channelArray[id].clear();
22    if (m_channelArray)
23            free(m_channelArray);
24 }
25
26 CacheItem *CacheChannel::_findBlock(CacheBuffer *buffer, unsigned short timeOffset, unsigned int *retBlock)
27 {
28         // the timestamp is necessarily in this buffer
29         unsigned int lowBlock, highBlock, midBlock;
30         if (timeOffset <= buffer->lookup[0].m_timeOffset) {
31                 // special case: the item is in the first block, search from start
32                 *retBlock = 0;
33                 return &buffer->m_firstItem;
34         }
35         // general case, the item is in the middle of the buffer
36         // before doing a dycotomic search, we will assume that timestamp
37         // are regularly spaced so that we can try to locate the block directly
38         highBlock = buffer->m_lastItemPositionW>>m_positionToBlockShiftW;
39         lowBlock = midBlock = (timeOffset*highBlock)/(buffer->m_lastTimestamp-buffer->m_firstTimestamp);
40         // give some space for security
41         if (lowBlock > 0)
42                 lowBlock--;
43         if (timeOffset <= buffer->lookup[lowBlock].m_timeOffset) {
44                 // bad guess, but we know this block is a good high block, just use it
45                 highBlock = lowBlock;
46                 lowBlock = 0;
47         } else {
48                 // ok, good guess, now check the high block, give some space
49                 if (midBlock < highBlock)
50                         midBlock++;
51                 if (timeOffset <= buffer->lookup[midBlock].m_timeOffset) {
52                         // good guess, keep that block as the high block
53                         highBlock = midBlock;
54                 }
55         }
56         // the item is in a different block, do a dycotomic search
57         // the timestamp is alway > lowBlock and <= highBlock
58         while (1) {
59                 midBlock = (lowBlock+highBlock)/2;
60                 if (midBlock == lowBlock) {
61                         // low block and high block are contigous, we can start search from the low block
62                         break;
63                 } else if (timeOffset <= buffer->lookup[midBlock].m_timeOffset) {
64                         highBlock = midBlock;
65                 } else {
66                         lowBlock = midBlock;
67                 }
68         }
69         assert (lowBlock != highBlock);
70         *retBlock = highBlock;
71         return CACHE_BLOCK_ITEM_ADDR(this,buffer,lowBlock);
72 }
73
74 void CacheChannel::clear()
75 {
76         CacheBuffer *buffer, *next;
77         for (buffer=m_firstBuffer; buffer != 0; buffer = next) {
78                 next = buffer->m_next;
79                 free(buffer);
80         }
81         m_firstBuffer = NULL;
82         m_lastBuffer = NULL;
83         if (initItem) {
84                 free(initItem);
85                 initItem = NULL;
86         }
87 }
88
89 CacheBuffer* CacheChannel::allocBuffer()
90 {
91         CacheBuffer* buffer;
92         if (!m_busy)
93                 return NULL;
94         buffer = (CacheBuffer*)malloc(CACHE_BUFFER_HEADER_SIZE+(m_bufferSizeW<<2));
95         if (buffer) {
96                 memset(buffer, 0, CACHE_BUFFER_HEADER_SIZE);
97         }
98         return buffer;
99 }
100
101 CacheItem* CacheChannel::findItemOrLater(unsigned int timestamp, CacheBuffer **rBuffer)
102 {
103         CacheBuffer* buffer;
104         CacheItem *item, *limit;
105         if (!m_busy)
106                 return NULL;
107         if (timestamp == 0 && initItem) {
108                 *rBuffer = NULL;
109                 return initItem;
110         }
111         for (buffer=m_firstBuffer; buffer; buffer = buffer->m_next) {
112                 if (buffer->m_firstFreePositionW == 0)
113                         // buffer is empty, this must be the last and we didn't find the timestamp
114                         return NULL;
115                 if (timestamp < buffer->m_firstTimestamp) {
116                         *rBuffer = buffer;
117                         return &buffer->m_firstItem;
118                 }
119                 if (timestamp <= buffer->m_lastTimestamp) {
120                         // the timestamp is necessarily in this buffer
121                         unsigned short timeOffset = (unsigned short)(timestamp-buffer->m_firstTimestamp);
122                         unsigned int highBlock;
123                         item = _findBlock(buffer, timeOffset, &highBlock);
124                         // now we do a linear search until we find a timestamp that is equal or higher
125                         // we should normally always find an item but let's put a limit just in case
126                         limit = CACHE_BLOCK_ITEM_ADDR(this,buffer,highBlock);
127                         while (item<=limit && item->m_timeOffset < timeOffset )
128                                 item = CACHE_NEXT_ITEM(item);
129                         assert(item<=limit);
130                         *rBuffer = buffer;
131                         return item;
132                 }
133                 // search in next buffer
134         }
135         return NULL;
136 }
137
138 CacheItem* CacheChannel::findItemEarlier(unsigned int timestamp, CacheBuffer **rBuffer)
139 {
140         CacheBuffer *buffer, *prevBuffer;
141         CacheItem *item, *limit, *prevItem;
142         if (!m_busy)
143                 return NULL;
144         if (timestamp == 0)
145                 return NULL;
146         for (prevBuffer=NULL, buffer=m_firstBuffer; buffer; prevBuffer = buffer, buffer = buffer->m_next) {
147                 if (buffer->m_firstFreePositionW == 0)
148                         // buffer is empty, this must be the last and we didn't find the timestamp
149                         return NULL;
150                 if (timestamp <= buffer->m_firstTimestamp) {
151                         if (prevBuffer == NULL) {
152                                 // no item before, except the initial item
153                                 *rBuffer = NULL;
154                                 return initItem;
155                         }
156                         // the item is necessarily the last one of previous buffer
157                         *rBuffer = prevBuffer;
158                         return CACHE_ITEM_ADDR(prevBuffer,prevBuffer->m_lastItemPositionW);
159                 }
160                 if (timestamp <= buffer->m_lastTimestamp) {
161                         // the timestamp is necessarily in this buffer
162                         unsigned short timeOffset = (unsigned short)(timestamp-buffer->m_firstTimestamp);
163                         unsigned int highBlock;
164                         item = _findBlock(buffer, timeOffset, &highBlock);
165                         // now we do a linear search until we find a timestamp that is equal or higher
166                         // we should normally always find an item but let's put a limit just in case
167                         limit = CACHE_BLOCK_ITEM_ADDR(this,buffer,highBlock);
168                         prevItem = NULL;
169                         while (item<=limit && item->m_timeOffset < timeOffset) {
170                                 prevItem = item;
171                                 item = CACHE_NEXT_ITEM(item);
172                         }
173                         assert(item<=limit && prevItem!=NULL);
174                         *rBuffer = buffer;
175                         return prevItem;
176                 }
177                 // search in next buffer
178         }
179         // pass all buffer, the last item is the last item of the last buffer
180         if (prevBuffer == NULL) {
181                 // no item before, except the initial item
182                 *rBuffer = NULL;
183                 return initItem;
184         }
185         // the item is necessarily the last one of previous buffer
186         *rBuffer = prevBuffer;
187         return CACHE_ITEM_ADDR(prevBuffer,prevBuffer->m_lastItemPositionW);
188 }
189
190
191 Cache::Cache()
192 {
193 }
194
195 Cache::~Cache()
196 {
197         CacheMap::iterator it;
198         for (it=m_cache.begin(); it!=m_cache.end(); it=m_cache.begin()) {
199                 deleteDevice(it->first);
200         }
201 }
202
203 int Cache::addChannel(const void *device, const char *name, unsigned int maxItemSize)
204 {
205         CacheMap::iterator it = m_cache.find(device);
206         CacheEntry *entry;
207         CacheChannel *channel;
208         unsigned int id;
209
210         if (maxItemSize > 0x3FFF0)
211                 return -1;
212
213         if (it == m_cache.end()) {
214                 // device does not exist yet, create a new entry
215                 entry = new CacheEntry();
216                 if (entry == NULL)
217                         return -1;
218                 if (!m_cache.insert(CacheMap::value_type(device,entry)).second)
219                         return -1;
220         } else {
221                 entry = it->second;
222         }
223         // locate a channel with the same name and reuse
224         for (channel=entry->m_channelArray, id=0; id<entry->m_count; id++, channel++) {
225                 if (channel->m_busy && !strcmp(name, channel->m_name)) {
226                         // make this channel free again
227                         deleteChannel(device, id);
228                         // there can only be one channel with the same name
229                         break;
230                 }
231         }
232         for (channel=entry->m_channelArray, id=0; id<entry->m_count; id++, channel++) {
233                 // locate a free channel
234                 if (!channel->m_busy)
235                         break;
236         }
237         if (id == entry->m_count) {
238                 // no channel free, create new channels
239                 int newcount = entry->m_count + CACHE_CHANNEL_EXTEND_SIZE;
240                 channel = (CacheChannel*)realloc(entry->m_channelArray, newcount*sizeof(CacheChannel));
241                 if (channel == NULL)
242                         return -1;
243                 entry->m_channelArray = channel;
244                 memset(&entry->m_channelArray[entry->m_count], 0, CACHE_CHANNEL_EXTEND_SIZE*sizeof(CacheChannel));
245                 entry->m_count = newcount;
246                 channel = &entry->m_channelArray[id];
247         }
248         // compute the optimal buffer size
249         // The buffer size must be selected so that
250         // - it does not contain more than 1630 items (=1s of cache assuming 25 items per second)
251         // - it contains at least one item
252         // - it's not bigger than 256kb and preferably around 32kb
253         // - it a multiple of 4
254         unsigned int bufSize = 1630*(maxItemSize+4);
255         if (bufSize >= CACHE_DEFAULT_BUFFER_SIZE)
256                 bufSize = CACHE_DEFAULT_BUFFER_SIZE;
257         if (bufSize < maxItemSize+16)
258                 bufSize = maxItemSize+16;
259         bufSize = (bufSize + 3) & ~0x3;
260         // compute block size and offset bit mask
261         // the block size is computed so that
262         // - it is a power of 2
263         // - there is at least one item per block
264         // - there is no more than CACHE_LOOKUP_TABLE_SIZE blocks per buffer
265         unsigned int blockSize = bufSize/CACHE_LOOKUP_TABLE_SIZE;
266         if (blockSize < maxItemSize+12)
267                 blockSize = maxItemSize+12;
268         // find the power of 2 that is immediately larger than blockSize
269         unsigned int m;
270         unsigned int pwr2Size = blockSize;
271         while ((m = (pwr2Size & (pwr2Size-1))) != 0)
272                 pwr2Size = m;
273         blockSize = (pwr2Size < blockSize) ? pwr2Size<<1 : pwr2Size;
274         // convert byte size to word size because all positions and size are expressed in 32 bit words
275         blockSize >>= 2;
276         channel->m_blockSizeW = blockSize;
277         channel->m_bufferSizeW = bufSize>>2;
278         channel->m_firstBuffer = NULL;
279         channel->m_lastBuffer = NULL;
280         channel->m_busy = 1;
281         channel->initItem = NULL;
282         channel->m_maxItemSizeB = maxItemSize;
283         strncpy(channel->m_name, name, sizeof(channel->m_name));
284         channel->m_name[sizeof(channel->m_name)-1] = 0;
285         channel->m_positionToOffsetMaskW = (blockSize-1);
286         for (m=0; blockSize!=1; m++, blockSize>>=1);
287         channel->m_positionToBlockShiftW = m;
288         return (int)id;
289 }
290
291 int Cache::deleteChannel(const void *device, int id)
292 {
293         CacheMap::iterator it = m_cache.find(device);
294         CacheEntry *entry;
295
296         if (it == m_cache.end()) {
297                 // device does not exist
298                 return -1;
299         }
300         entry = it->second;
301         if (id < 0 || id >= (int)entry->m_count || !entry->m_channelArray[id].m_busy)
302                 return -1;
303         entry->m_channelArray[id].clear();
304         entry->m_channelArray[id].m_busy = 0;
305         return 0;
306 }
307
308 int Cache::deleteDevice(const void *device)
309 {
310         CacheMap::iterator it = m_cache.find(device);
311         CacheEntry *entry;
312
313         if (it == m_cache.end()) {
314                 // device does not exist
315                 return -1;
316         }
317         entry = it->second;
318         delete entry;
319         m_cache.erase(it);
320         return 0;
321 }
322
323 void Cache::clearCacheFrom(const void *device, CacheTS timestamp)
324 {
325         CacheMap::iterator it = (device) ? m_cache.find(device) : m_cache.begin();
326         CacheEntry *entry;
327         CacheChannel *channel;
328         CacheBuffer *buffer, *nextBuffer, *prevBuffer;
329         CacheItem *item, *prevItem, *nextItem;
330         unsigned int positionW, block;
331
332         while (it != m_cache.end()) {
333                 entry = it->second;
334                 for (unsigned int ch=0; ch<entry->m_count; ch++) {
335                         channel = &entry->m_channelArray[ch];
336                         if (channel->m_busy) {
337                                 item = channel->findItemOrLater(timestamp, &buffer);
338                                 if (item ) {
339                                         if (!buffer) {
340                                                 // this is possible if we return the special timestamp=0 item, delete all buffers
341                                                 channel->clear();
342                                         } else {
343                                                 // this item and all later items will be removed, clear any later buffer
344                                                 while ((nextBuffer = buffer->m_next) != NULL) {
345                                                         buffer->m_next = nextBuffer->m_next;
346                                                         free(nextBuffer);
347                                                 }
348                                                 positionW = CACHE_ITEM_POSITIONW(buffer,item);
349                                                 if (positionW == 0) {
350                                                         // this item is the first one of the buffer, remove the buffer completely
351                                                         // first find the buffer just before it
352                                                         nextBuffer = channel->m_firstBuffer;
353                                                         prevBuffer = NULL;
354                                                         while (nextBuffer != buffer) {
355                                                                 prevBuffer = nextBuffer;
356                                                                 nextBuffer = nextBuffer->m_next;
357                                                                 // we must quit this loop before reaching the end of the list
358                                                                 assert(nextBuffer);
359                                                         }
360                                                         free(buffer);
361                                                         buffer = prevBuffer;
362                                                         if (buffer == NULL)
363                                                                 // this was also the first buffer
364                                                                 channel->m_firstBuffer = NULL;
365                                                 } else {
366                                                         // removing this item means finding the previous item to make it the last one
367                                                         block = positionW>>channel->m_positionToBlockShiftW;
368                                                         if (block == 0) {
369                                                                 // start from first item, we know it is not our item because positionW > 0
370                                                                 prevItem = &buffer->m_firstItem;
371                                                         } else {
372                                                                 // no need to check the current block, it will point to our item or a later one
373                                                                 // but the previous block will be a good start for sure.
374                                                                 block--;
375                                                                 prevItem = CACHE_BLOCK_ITEM_ADDR(channel,buffer,block);
376                                                         }
377                                                         while ((nextItem = CACHE_NEXT_ITEM(prevItem)) < item)
378                                                                 prevItem = nextItem;
379                                                         // we must have found our item
380                                                         assert(nextItem==item);
381                                                         // now set the buffer
382                                                         buffer->m_lastItemPositionW = CACHE_ITEM_POSITIONW(buffer,prevItem);
383                                                         buffer->m_firstFreePositionW = positionW;
384                                                         buffer->m_lastTimestamp = buffer->m_firstTimestamp + prevItem->m_timeOffset;
385                                                         block = buffer->m_lastItemPositionW>>channel->m_positionToBlockShiftW;
386                                                         buffer->lookup[block].m_offsetW = buffer->m_lastItemPositionW&channel->m_positionToOffsetMaskW;
387                                                         buffer->lookup[block].m_timeOffset = prevItem->m_timeOffset;
388                                                 }
389                                                 // set the channel
390                                                 channel->m_lastBuffer = buffer;
391                                                 if (buffer) {
392                                                         channel->m_lastTimestamp = buffer->m_lastTimestamp;
393                                                         channel->m_lastItemPositionW = buffer->m_lastItemPositionW;
394                                                 }
395                                         }
396                                 }
397                         }
398                 }
399                 if (device)
400                         break;
401                 ++it;
402         }
403 }
404
405 void *Cache::addCacheItem(const void *device, int id, unsigned int timestamp, void *data, unsigned int length)
406 {
407         CacheMap::iterator it = m_cache.find(device);
408         CacheEntry *entry;
409         CacheChannel *channel;
410         CacheBuffer *buffer, *next;
411         CacheItem *item;
412         unsigned int positionW, sizeW, block;
413
414         if (it == m_cache.end()) {
415                 // device does not exist
416                 return NULL;
417         }
418         entry = it->second;
419         if (id < 0 || id >= (int) entry->m_count || !entry->m_channelArray[id].m_busy)
420                 return NULL;
421         channel = &entry->m_channelArray[id];
422         if (length > channel->m_maxItemSizeB)
423                 return NULL;
424         if (timestamp == 0) {
425                 // initial item, delete all buffers
426                 channel->clear();
427                 // and create initial item
428                 item = NULL;
429                 // we will allocate the memory, which is always pointer aligned => compute size
430                 // with NULL will give same result.
431                 sizeW = CACHE_ITEM_SIZEW(item,length);
432                 item = (CacheItem*)calloc(sizeW, 4);
433                 item->m_sizeW = sizeW;
434                 channel->initItem = item;
435         } else {
436                 if (!channel->m_lastBuffer) {
437                         // no item in buffer, insert item at first position of first buffer
438                         positionW = 0;
439                         if ((buffer = channel->m_firstBuffer) == NULL) {
440                                 buffer = channel->allocBuffer();
441                                 channel->m_firstBuffer = buffer;
442                         }
443                 } else if (timestamp > channel->m_lastTimestamp) {
444                         // this is the normal case: we are writing past lastest timestamp
445                         buffer = channel->m_lastBuffer;
446                         positionW = buffer->m_firstFreePositionW;
447                 } else if (timestamp == channel->m_lastTimestamp) {
448                         // common case, rewriting the last timestamp, just reuse the last position
449                         buffer = channel->m_lastBuffer;
450                         positionW = channel->m_lastItemPositionW;
451                 } else {
452                         // general case, write in the middle of the buffer, locate the timestamp
453                         // (or the timestamp just after), clear this item and all future items,
454                         // and write at that position
455                         item = channel->findItemOrLater(timestamp, &buffer);
456                         if (item == NULL) {
457                                 // this should not happen
458                                 return NULL;
459                         }
460                         // this item will become the last one of this channel, clear any later buffer
461                         while ((next = buffer->m_next) != NULL) {
462                                 buffer->m_next = next->m_next;
463                                 free(next);
464                         }
465                         // no need to update the buffer, this will be done when the item is written
466                         positionW = CACHE_ITEM_POSITIONW(buffer,item);
467                 }
468                 item = CACHE_ITEM_ADDR(buffer,positionW);
469                 sizeW = CACHE_ITEM_SIZEW(item,length);
470                 // we have positionW pointing where we can put the item
471                 // before we do that we have to check if we can:
472                 // - enough room
473                 // - timestamp not too late
474                 if ((positionW+sizeW > channel->m_bufferSizeW) ||
475                         (positionW > 0 && timestamp >= buffer->m_firstTimestamp+0x10000)) {
476                         // we must allocate a new buffer to store this item
477                         // but before we must make sure that the current buffer is consistent
478                         if (positionW != buffer->m_firstFreePositionW) {
479                                 // This means that we were trying to write in the middle of the buffer.
480                                 // We must set the buffer right with positionW being the last position
481                                 // and find the item before positionW to make it the last.
482                                 block = positionW>>channel->m_positionToBlockShiftW;
483                                 CacheItem *previousItem, *nextItem;
484                                 if (block == 0) {
485                                         // start from first item, we know it is not our item because positionW > 0
486                                         previousItem = &buffer->m_firstItem;
487                                 } else {
488                                         // no need to check the current block, it will point to our item or a later one
489                                         // but the previous block will be a good start for sure.
490                                         block--;
491                                         previousItem = CACHE_BLOCK_ITEM_ADDR(channel,buffer,block);
492                                 }
493                                 while ((nextItem = CACHE_NEXT_ITEM(previousItem)) < item)
494                                         previousItem = nextItem;
495                                 // we must have found our item
496                                 assert(nextItem==item);
497                                 // now set the buffer
498                                 buffer->m_lastItemPositionW = CACHE_ITEM_POSITIONW(buffer,previousItem);
499                                 buffer->m_firstFreePositionW = positionW;
500                                 buffer->m_lastTimestamp = buffer->m_firstTimestamp + previousItem->m_timeOffset;
501                                 block = buffer->m_lastItemPositionW>>channel->m_positionToBlockShiftW;
502                                 buffer->lookup[block].m_offsetW = buffer->m_lastItemPositionW&channel->m_positionToOffsetMaskW;
503                                 buffer->lookup[block].m_timeOffset = previousItem->m_timeOffset;
504                                 // and also the channel, just in case
505                                 channel->m_lastBuffer = buffer;
506                                 channel->m_lastTimestamp = buffer->m_lastTimestamp;
507                                 channel->m_lastItemPositionW = buffer->m_lastItemPositionW;
508                         }
509                         // now allocate a new buffer
510                         buffer->m_next = channel->allocBuffer();
511                         if (buffer->m_next == NULL)
512                                 return NULL;
513                         buffer = buffer->m_next;
514                         positionW = 0;
515                         item = &buffer->m_firstItem;
516                         sizeW = CACHE_ITEM_SIZEW(item,length);
517                 }
518                 // all check passed, ready to write the item
519                 item->m_sizeW = sizeW;
520                 if (positionW == 0) {
521                         item->m_timeOffset = 0;
522                         buffer->m_firstTimestamp = timestamp;
523                 } else {
524                         item->m_timeOffset = (unsigned short)(timestamp-buffer->m_firstTimestamp);
525                 }
526                 buffer->m_lastItemPositionW = positionW;
527                 buffer->m_firstFreePositionW = positionW+sizeW;
528                 buffer->m_lastTimestamp = timestamp;
529                 block = positionW>>channel->m_positionToBlockShiftW;
530                 buffer->lookup[block].m_offsetW = positionW&channel->m_positionToOffsetMaskW;
531                 buffer->lookup[block].m_timeOffset = item->m_timeOffset;
532                 buffer->m_lastItemPositionW = CACHE_ITEM_POSITIONW(buffer,item);
533                 buffer->m_firstFreePositionW = buffer->m_lastItemPositionW+item->m_sizeW;
534                 channel->m_lastBuffer = buffer;
535                 channel->m_lastItemPositionW = positionW;
536                 channel->m_lastTimestamp = timestamp;
537         }
538         // now copy the item
539         void *itemData = CACHE_ITEM_DATA_POINTER(item);
540         if (data)
541                 memcpy(itemData, data, length);
542         return itemData;
543 }
544
545 const void *Cache::getPreviousCacheItem(const void *device, int id, unsigned int *timestamp)
546 {
547         CacheMap::iterator it;
548         CacheEntry *entry;
549         CacheChannel *channel;
550         CacheBuffer *buffer;
551         CacheItem *item;
552
553         if (device) {
554                 it = m_cache.find(device);      
555         } else {
556                 it = m_cache.begin();
557         }
558         if (it == m_cache.end()) {
559                 // device does not exist
560                 return NULL;
561         }
562         entry = it->second;
563         if (id < 0 || id >= (int) entry->m_count || !entry->m_channelArray[id].m_busy)
564                 return NULL;
565         channel = &entry->m_channelArray[id];
566         if ((item = channel->findItemEarlier(*timestamp,&buffer)) == NULL)
567                 return NULL;
568         *timestamp = (buffer) ? buffer->m_firstTimestamp+item->m_timeOffset : 0;
569         return CACHE_ITEM_DATA_POINTER(item);
570 }
571
572 const CacheItem *Cache::getCurrentCacheItemInternal(const void *device, int id, CacheTS timestamp)
573 {
574         CacheMap::iterator it = m_cache.find(device);
575         CacheEntry *entry;
576         CacheChannel *channel;
577         CacheBuffer *buffer;
578         CacheItem *item;
579
580         if (it == m_cache.end()) {
581                 // device does not exist
582                 return NULL;
583         }
584         entry = it->second;
585         if (id < 0 || id >= (int) entry->m_count || !entry->m_channelArray[id].m_busy)
586                 return NULL;
587         channel = &entry->m_channelArray[id];
588         if ((item = channel->findItemOrLater(timestamp,&buffer)) == NULL)
589                 return NULL;
590         if (buffer && buffer->m_firstTimestamp+item->m_timeOffset != timestamp)
591                 return NULL;
592         return item;
593 }
594
595 const void *Cache::getCurrentCacheItem(const void *device, int channel, unsigned int timestamp)
596 {
597         const CacheItem *item = getCurrentCacheItemInternal(device, channel, timestamp);
598         return (item) ? CACHE_ITEM_DATA_POINTER(item) : NULL;
599 }
600
601 double *Cache::addCacheVectorIfDifferent(const void *device, int channel, CacheTS timestamp, double *newdata, unsigned int length, double threshold)
602 {
603         const CacheItem *item = getCurrentCacheItemInternal(device, channel, timestamp);
604         unsigned int sizeW = CACHE_ITEM_SIZEW(item,length*sizeof(double));
605         if (!item || item->m_sizeW != sizeW)
606                 return (double*)addCacheItem(device, channel, timestamp, newdata, length*sizeof(double));
607         double *olddata = (double*)CACHE_ITEM_DATA_POINTER(item);
608         if (!length)
609                 return olddata;
610         double *ref = olddata;
611         double *v = newdata;
612         unsigned int i;
613         for (i=length; i>0; --i) {
614                 if (fabs(*v-*ref) > threshold)
615                         break;
616                 *ref++ = *v++;
617         }
618         if (i) 
619                 olddata = (double*)addCacheItem(device, channel, timestamp, newdata, length*sizeof(double));
620         return olddata;
621 }
622
623 }