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