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