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