Blender  V2.59
Cache.cpp
Go to the documentation of this file.
00001 
00004 /* $Id: Cache.cpp 35155 2011-02-25 11:45:16Z jesterking $
00005  * Cache.cpp
00006  *
00007  *  Created on: Feb 24, 2009
00008  *      Author: benoit bolsee
00009  */
00010 #include <string.h>
00011 #include <assert.h>
00012 #include <math.h>
00013 #include <stdlib.h>
00014 #include "Cache.hpp"
00015 
00016 namespace iTaSC {
00017 
00018 CacheEntry::~CacheEntry()
00019 {
00020    for (unsigned int id=0; id < m_count; id++)
00021                 m_channelArray[id].clear();
00022    if (m_channelArray)
00023            free(m_channelArray);
00024 }
00025 
00026 CacheItem *CacheChannel::_findBlock(CacheBuffer *buffer, unsigned short timeOffset, unsigned int *retBlock)
00027 {
00028         // the timestamp is necessarily in this buffer
00029         unsigned int lowBlock, highBlock, midBlock;
00030         if (timeOffset <= buffer->lookup[0].m_timeOffset) {
00031                 // special case: the item is in the first block, search from start
00032                 *retBlock = 0;
00033                 return &buffer->m_firstItem;
00034         }
00035         // general case, the item is in the middle of the buffer
00036         // before doing a dycotomic search, we will assume that timestamp
00037         // are regularly spaced so that we can try to locate the block directly
00038         highBlock = buffer->m_lastItemPositionW>>m_positionToBlockShiftW;
00039         lowBlock = midBlock = (timeOffset*highBlock)/(buffer->m_lastTimestamp-buffer->m_firstTimestamp);
00040         // give some space for security
00041         if (lowBlock > 0)
00042                 lowBlock--;
00043         if (timeOffset <= buffer->lookup[lowBlock].m_timeOffset) {
00044                 // bad guess, but we know this block is a good high block, just use it
00045                 highBlock = lowBlock;
00046                 lowBlock = 0;
00047         } else {
00048                 // ok, good guess, now check the high block, give some space
00049                 if (midBlock < highBlock)
00050                         midBlock++;
00051                 if (timeOffset <= buffer->lookup[midBlock].m_timeOffset) {
00052                         // good guess, keep that block as the high block
00053                         highBlock = midBlock;
00054                 }
00055         }
00056         // the item is in a different block, do a dycotomic search
00057         // the timestamp is alway > lowBlock and <= highBlock
00058         while (1) {
00059                 midBlock = (lowBlock+highBlock)/2;
00060                 if (midBlock == lowBlock) {
00061                         // low block and high block are contigous, we can start search from the low block
00062                         break;
00063                 } else if (timeOffset <= buffer->lookup[midBlock].m_timeOffset) {
00064                         highBlock = midBlock;
00065                 } else {
00066                         lowBlock = midBlock;
00067                 }
00068         }
00069         assert (lowBlock != highBlock);
00070         *retBlock = highBlock;
00071         return CACHE_BLOCK_ITEM_ADDR(this,buffer,lowBlock);
00072 }
00073 
00074 void CacheChannel::clear()
00075 {
00076         CacheBuffer *buffer, *next;
00077         for (buffer=m_firstBuffer; buffer != 0; buffer = next) {
00078                 next = buffer->m_next;
00079                 free(buffer);
00080         }
00081         m_firstBuffer = NULL;
00082         m_lastBuffer = NULL;
00083         if (initItem) {
00084                 free(initItem);
00085                 initItem = NULL;
00086         }
00087 }
00088 
00089 CacheBuffer* CacheChannel::allocBuffer()
00090 {
00091         CacheBuffer* buffer;
00092         if (!m_busy)
00093                 return NULL;
00094         buffer = (CacheBuffer*)malloc(CACHE_BUFFER_HEADER_SIZE+(m_bufferSizeW<<2));
00095         if (buffer) {
00096                 memset(buffer, 0, CACHE_BUFFER_HEADER_SIZE);
00097         }
00098         return buffer;
00099 }
00100 
00101 CacheItem* CacheChannel::findItemOrLater(unsigned int timestamp, CacheBuffer **rBuffer)
00102 {
00103         CacheBuffer* buffer;
00104         CacheItem *item, *limit;
00105         if (!m_busy)
00106                 return NULL;
00107         if (timestamp == 0 && initItem) {
00108                 *rBuffer = NULL;
00109                 return initItem;
00110         }
00111         for (buffer=m_firstBuffer; buffer; buffer = buffer->m_next) {
00112                 if (buffer->m_firstFreePositionW == 0)
00113                         // buffer is empty, this must be the last and we didn't find the timestamp
00114                         return NULL;
00115                 if (timestamp < buffer->m_firstTimestamp) {
00116                         *rBuffer = buffer;
00117                         return &buffer->m_firstItem;
00118                 }
00119                 if (timestamp <= buffer->m_lastTimestamp) {
00120                         // the timestamp is necessarily in this buffer
00121                         unsigned short timeOffset = (unsigned short)(timestamp-buffer->m_firstTimestamp);
00122                         unsigned int highBlock;
00123                         item = _findBlock(buffer, timeOffset, &highBlock);
00124                         // now we do a linear search until we find a timestamp that is equal or higher
00125                         // we should normally always find an item but let's put a limit just in case
00126                         limit = CACHE_BLOCK_ITEM_ADDR(this,buffer,highBlock);
00127                         while (item<=limit && item->m_timeOffset < timeOffset )
00128                                 item = CACHE_NEXT_ITEM(item);
00129                         assert(item<=limit);
00130                         *rBuffer = buffer;
00131                         return item;
00132                 }
00133                 // search in next buffer
00134         }
00135         return NULL;
00136 }
00137 
00138 CacheItem* CacheChannel::findItemEarlier(unsigned int timestamp, CacheBuffer **rBuffer)
00139 {
00140         CacheBuffer *buffer, *prevBuffer;
00141         CacheItem *item, *limit, *prevItem;
00142         if (!m_busy)
00143                 return NULL;
00144         if (timestamp == 0)
00145                 return NULL;
00146         for (prevBuffer=NULL, buffer=m_firstBuffer; buffer; prevBuffer = buffer, buffer = buffer->m_next) {
00147                 if (buffer->m_firstFreePositionW == 0)
00148                         // buffer is empty, this must be the last and we didn't find the timestamp
00149                         return NULL;
00150                 if (timestamp <= buffer->m_firstTimestamp) {
00151                         if (prevBuffer == NULL) {
00152                                 // no item before, except the initial item
00153                                 *rBuffer = NULL;
00154                                 return initItem;
00155                         }
00156                         // the item is necessarily the last one of previous buffer
00157                         *rBuffer = prevBuffer;
00158                         return CACHE_ITEM_ADDR(prevBuffer,prevBuffer->m_lastItemPositionW);
00159                 }
00160                 if (timestamp <= buffer->m_lastTimestamp) {
00161                         // the timestamp is necessarily in this buffer
00162                         unsigned short timeOffset = (unsigned short)(timestamp-buffer->m_firstTimestamp);
00163                         unsigned int highBlock;
00164                         item = _findBlock(buffer, timeOffset, &highBlock);
00165                         // now we do a linear search until we find a timestamp that is equal or higher
00166                         // we should normally always find an item but let's put a limit just in case
00167                         limit = CACHE_BLOCK_ITEM_ADDR(this,buffer,highBlock);
00168                         prevItem = NULL;
00169                         while (item<=limit && item->m_timeOffset < timeOffset) {
00170                                 prevItem = item;
00171                                 item = CACHE_NEXT_ITEM(item);
00172                         }
00173                         assert(item<=limit && prevItem!=NULL);
00174                         *rBuffer = buffer;
00175                         return prevItem;
00176                 }
00177                 // search in next buffer
00178         }
00179         // pass all buffer, the last item is the last item of the last buffer
00180         if (prevBuffer == NULL) {
00181                 // no item before, except the initial item
00182                 *rBuffer = NULL;
00183                 return initItem;
00184         }
00185         // the item is necessarily the last one of previous buffer
00186         *rBuffer = prevBuffer;
00187         return CACHE_ITEM_ADDR(prevBuffer,prevBuffer->m_lastItemPositionW);
00188 }
00189 
00190 
00191 Cache::Cache()
00192 {
00193 }
00194 
00195 Cache::~Cache()
00196 {
00197         CacheMap::iterator it;
00198         for (it=m_cache.begin(); it!=m_cache.end(); it=m_cache.begin()) {
00199                 deleteDevice(it->first);
00200         }
00201 }
00202 
00203 int Cache::addChannel(const void *device, const char *name, unsigned int maxItemSize)
00204 {
00205         CacheMap::iterator it = m_cache.find(device);
00206         CacheEntry *entry;
00207         CacheChannel *channel;
00208         unsigned int id;
00209 
00210         if (maxItemSize > 0x3FFF0)
00211                 return -1;
00212 
00213         if (it == m_cache.end()) {
00214                 // device does not exist yet, create a new entry
00215                 entry = new CacheEntry();
00216                 if (entry == NULL)
00217                         return -1;
00218                 if (!m_cache.insert(CacheMap::value_type(device,entry)).second)
00219                         return -1;
00220         } else {
00221                 entry = it->second;
00222         }
00223         // locate a channel with the same name and reuse
00224         for (channel=entry->m_channelArray, id=0; id<entry->m_count; id++, channel++) {
00225                 if (channel->m_busy && !strcmp(name, channel->m_name)) {
00226                         // make this channel free again
00227                         deleteChannel(device, id);
00228                         // there can only be one channel with the same name
00229                         break;
00230                 }
00231         }
00232         for (channel=entry->m_channelArray, id=0; id<entry->m_count; id++, channel++) {
00233                 // locate a free channel
00234                 if (!channel->m_busy)
00235                         break;
00236         }
00237         if (id == entry->m_count) {
00238                 // no channel free, create new channels
00239                 int newcount = entry->m_count + CACHE_CHANNEL_EXTEND_SIZE;
00240                 channel = (CacheChannel*)realloc(entry->m_channelArray, newcount*sizeof(CacheChannel));
00241                 if (channel == NULL)
00242                         return -1;
00243                 entry->m_channelArray = channel;
00244                 memset(&entry->m_channelArray[entry->m_count], 0, CACHE_CHANNEL_EXTEND_SIZE*sizeof(CacheChannel));
00245                 entry->m_count = newcount;
00246                 channel = &entry->m_channelArray[id];
00247         }
00248         // compute the optimal buffer size
00249         // The buffer size must be selected so that
00250         // - it does not contain more than 1630 items (=1s of cache assuming 25 items per second)
00251         // - it contains at least one item
00252         // - it's not bigger than 256kb and preferably around 32kb
00253         // - it a multiple of 4
00254         unsigned int bufSize = 1630*(maxItemSize+4);
00255         if (bufSize >= CACHE_DEFAULT_BUFFER_SIZE)
00256                 bufSize = CACHE_DEFAULT_BUFFER_SIZE;
00257         if (bufSize < maxItemSize+16)
00258                 bufSize = maxItemSize+16;
00259         bufSize = (bufSize + 3) & ~0x3;
00260         // compute block size and offset bit mask
00261         // the block size is computed so that
00262         // - it is a power of 2
00263         // - there is at least one item per block
00264         // - there is no more than CACHE_LOOKUP_TABLE_SIZE blocks per buffer
00265         unsigned int blockSize = bufSize/CACHE_LOOKUP_TABLE_SIZE;
00266         if (blockSize < maxItemSize+12)
00267                 blockSize = maxItemSize+12;
00268         // find the power of 2 that is immediately larger than blockSize
00269         unsigned int m;
00270         unsigned int pwr2Size = blockSize;
00271         while ((m = (pwr2Size & (pwr2Size-1))) != 0)
00272                 pwr2Size = m;
00273         blockSize = (pwr2Size < blockSize) ? pwr2Size<<1 : pwr2Size;
00274         // convert byte size to word size because all positions and size are expressed in 32 bit words
00275         blockSize >>= 2;
00276         channel->m_blockSizeW = blockSize;
00277         channel->m_bufferSizeW = bufSize>>2;
00278         channel->m_firstBuffer = NULL;
00279         channel->m_lastBuffer = NULL;
00280         channel->m_busy = 1;
00281         channel->initItem = NULL;
00282         channel->m_maxItemSizeB = maxItemSize;
00283         strncpy(channel->m_name, name, sizeof(channel->m_name));
00284         channel->m_name[sizeof(channel->m_name)-1] = 0;
00285         channel->m_positionToOffsetMaskW = (blockSize-1);
00286         for (m=0; blockSize!=1; m++, blockSize>>=1);
00287         channel->m_positionToBlockShiftW = m;
00288         return (int)id;
00289 }
00290 
00291 int Cache::deleteChannel(const void *device, int id)
00292 {
00293         CacheMap::iterator it = m_cache.find(device);
00294         CacheEntry *entry;
00295 
00296         if (it == m_cache.end()) {
00297                 // device does not exist
00298                 return -1;
00299         }
00300         entry = it->second;
00301         if (id < 0 || id >= (int)entry->m_count || !entry->m_channelArray[id].m_busy)
00302                 return -1;
00303         entry->m_channelArray[id].clear();
00304         entry->m_channelArray[id].m_busy = 0;
00305         return 0;
00306 }
00307 
00308 int Cache::deleteDevice(const void *device)
00309 {
00310         CacheMap::iterator it = m_cache.find(device);
00311         CacheEntry *entry;
00312 
00313         if (it == m_cache.end()) {
00314                 // device does not exist
00315                 return -1;
00316         }
00317         entry = it->second;
00318         delete entry;
00319         m_cache.erase(it);
00320         return 0;
00321 }
00322 
00323 void Cache::clearCacheFrom(const void *device, CacheTS timestamp)
00324 {
00325         CacheMap::iterator it = (device) ? m_cache.find(device) : m_cache.begin();
00326         CacheEntry *entry;
00327         CacheChannel *channel;
00328         CacheBuffer *buffer, *nextBuffer, *prevBuffer;
00329         CacheItem *item, *prevItem, *nextItem;
00330         unsigned int positionW, block;
00331 
00332         while (it != m_cache.end()) {
00333                 entry = it->second;
00334                 for (unsigned int ch=0; ch<entry->m_count; ch++) {
00335                         channel = &entry->m_channelArray[ch];
00336                         if (channel->m_busy) {
00337                                 item = channel->findItemOrLater(timestamp, &buffer);
00338                                 if (item ) {
00339                                         if (!buffer) {
00340                                                 // this is possible if we return the special timestamp=0 item, delete all buffers
00341                                                 channel->clear();
00342                                         } else {
00343                                                 // this item and all later items will be removed, clear any later buffer
00344                                                 while ((nextBuffer = buffer->m_next) != NULL) {
00345                                                         buffer->m_next = nextBuffer->m_next;
00346                                                         free(nextBuffer);
00347                                                 }
00348                                                 positionW = CACHE_ITEM_POSITIONW(buffer,item);
00349                                                 if (positionW == 0) {
00350                                                         // this item is the first one of the buffer, remove the buffer completely
00351                                                         // first find the buffer just before it
00352                                                         nextBuffer = channel->m_firstBuffer;
00353                                                         prevBuffer = NULL;
00354                                                         while (nextBuffer != buffer) {
00355                                                                 prevBuffer = nextBuffer;
00356                                                                 nextBuffer = nextBuffer->m_next;
00357                                                                 // we must quit this loop before reaching the end of the list
00358                                                                 assert(nextBuffer);
00359                                                         }
00360                                                         free(buffer);
00361                                                         buffer = prevBuffer;
00362                                                         if (buffer == NULL)
00363                                                                 // this was also the first buffer
00364                                                                 channel->m_firstBuffer = NULL;
00365                                                 } else {
00366                                                         // removing this item means finding the previous item to make it the last one
00367                                                         block = positionW>>channel->m_positionToBlockShiftW;
00368                                                         if (block == 0) {
00369                                                                 // start from first item, we know it is not our item because positionW > 0
00370                                                                 prevItem = &buffer->m_firstItem;
00371                                                         } else {
00372                                                                 // no need to check the current block, it will point to our item or a later one
00373                                                                 // but the previous block will be a good start for sure.
00374                                                                 block--;
00375                                                                 prevItem = CACHE_BLOCK_ITEM_ADDR(channel,buffer,block);
00376                                                         }
00377                                                         while ((nextItem = CACHE_NEXT_ITEM(prevItem)) < item)
00378                                                                 prevItem = nextItem;
00379                                                         // we must have found our item
00380                                                         assert(nextItem==item);
00381                                                         // now set the buffer
00382                                                         buffer->m_lastItemPositionW = CACHE_ITEM_POSITIONW(buffer,prevItem);
00383                                                         buffer->m_firstFreePositionW = positionW;
00384                                                         buffer->m_lastTimestamp = buffer->m_firstTimestamp + prevItem->m_timeOffset;
00385                                                         block = buffer->m_lastItemPositionW>>channel->m_positionToBlockShiftW;
00386                                                         buffer->lookup[block].m_offsetW = buffer->m_lastItemPositionW&channel->m_positionToOffsetMaskW;
00387                                                         buffer->lookup[block].m_timeOffset = prevItem->m_timeOffset;
00388                                                 }
00389                                                 // set the channel
00390                                                 channel->m_lastBuffer = buffer;
00391                                                 if (buffer) {
00392                                                         channel->m_lastTimestamp = buffer->m_lastTimestamp;
00393                                                         channel->m_lastItemPositionW = buffer->m_lastItemPositionW;
00394                                                 }
00395                                         }
00396                                 }
00397                         }
00398                 }
00399                 if (device)
00400                         break;
00401                 ++it;
00402         }
00403 }
00404 
00405 void *Cache::addCacheItem(const void *device, int id, unsigned int timestamp, void *data, unsigned int length)
00406 {
00407         CacheMap::iterator it = m_cache.find(device);
00408         CacheEntry *entry;
00409         CacheChannel *channel;
00410         CacheBuffer *buffer, *next;
00411         CacheItem *item;
00412         unsigned int positionW, sizeW, block;
00413 
00414         if (it == m_cache.end()) {
00415                 // device does not exist
00416                 return NULL;
00417         }
00418         entry = it->second;
00419         if (id < 0 || id >= (int) entry->m_count || !entry->m_channelArray[id].m_busy)
00420                 return NULL;
00421         channel = &entry->m_channelArray[id];
00422         if (length > channel->m_maxItemSizeB)
00423                 return NULL;
00424         if (timestamp == 0) {
00425                 // initial item, delete all buffers
00426                 channel->clear();
00427                 // and create initial item
00428                 item = NULL;
00429                 // we will allocate the memory, which is always pointer aligned => compute size
00430                 // with NULL will give same result.
00431                 sizeW = CACHE_ITEM_SIZEW(item,length);
00432                 item = (CacheItem*)calloc(sizeW, 4);
00433                 item->m_sizeW = sizeW;
00434                 channel->initItem = item;
00435         } else {
00436                 if (!channel->m_lastBuffer) {
00437                         // no item in buffer, insert item at first position of first buffer
00438                         positionW = 0;
00439                         if ((buffer = channel->m_firstBuffer) == NULL) {
00440                                 buffer = channel->allocBuffer();
00441                                 channel->m_firstBuffer = buffer;
00442                         }
00443                 } else if (timestamp > channel->m_lastTimestamp) {
00444                         // this is the normal case: we are writing past lastest timestamp
00445                         buffer = channel->m_lastBuffer;
00446                         positionW = buffer->m_firstFreePositionW;
00447                 } else if (timestamp == channel->m_lastTimestamp) {
00448                         // common case, rewriting the last timestamp, just reuse the last position
00449                         buffer = channel->m_lastBuffer;
00450                         positionW = channel->m_lastItemPositionW;
00451                 } else {
00452                         // general case, write in the middle of the buffer, locate the timestamp
00453                         // (or the timestamp just after), clear this item and all future items,
00454                         // and write at that position
00455                         item = channel->findItemOrLater(timestamp, &buffer);
00456                         if (item == NULL) {
00457                                 // this should not happen
00458                                 return NULL;
00459                         }
00460                         // this item will become the last one of this channel, clear any later buffer
00461                         while ((next = buffer->m_next) != NULL) {
00462                                 buffer->m_next = next->m_next;
00463                                 free(next);
00464                         }
00465                         // no need to update the buffer, this will be done when the item is written
00466                         positionW = CACHE_ITEM_POSITIONW(buffer,item);
00467                 }
00468                 item = CACHE_ITEM_ADDR(buffer,positionW);
00469                 sizeW = CACHE_ITEM_SIZEW(item,length);
00470                 // we have positionW pointing where we can put the item
00471                 // before we do that we have to check if we can:
00472                 // - enough room
00473                 // - timestamp not too late
00474                 if ((positionW+sizeW > channel->m_bufferSizeW) ||
00475                         (positionW > 0 && timestamp >= buffer->m_firstTimestamp+0x10000)) {
00476                         // we must allocate a new buffer to store this item
00477                         // but before we must make sure that the current buffer is consistent
00478                         if (positionW != buffer->m_firstFreePositionW) {
00479                                 // This means that we were trying to write in the middle of the buffer.
00480                                 // We must set the buffer right with positionW being the last position
00481                                 // and find the item before positionW to make it the last.
00482                                 block = positionW>>channel->m_positionToBlockShiftW;
00483                                 CacheItem *previousItem, *nextItem;
00484                                 if (block == 0) {
00485                                         // start from first item, we know it is not our item because positionW > 0
00486                                         previousItem = &buffer->m_firstItem;
00487                                 } else {
00488                                         // no need to check the current block, it will point to our item or a later one
00489                                         // but the previous block will be a good start for sure.
00490                                         block--;
00491                                         previousItem = CACHE_BLOCK_ITEM_ADDR(channel,buffer,block);
00492                                 }
00493                                 while ((nextItem = CACHE_NEXT_ITEM(previousItem)) < item)
00494                                         previousItem = nextItem;
00495                                 // we must have found our item
00496                                 assert(nextItem==item);
00497                                 // now set the buffer
00498                                 buffer->m_lastItemPositionW = CACHE_ITEM_POSITIONW(buffer,previousItem);
00499                                 buffer->m_firstFreePositionW = positionW;
00500                                 buffer->m_lastTimestamp = buffer->m_firstTimestamp + previousItem->m_timeOffset;
00501                                 block = buffer->m_lastItemPositionW>>channel->m_positionToBlockShiftW;
00502                                 buffer->lookup[block].m_offsetW = buffer->m_lastItemPositionW&channel->m_positionToOffsetMaskW;
00503                                 buffer->lookup[block].m_timeOffset = previousItem->m_timeOffset;
00504                                 // and also the channel, just in case
00505                                 channel->m_lastBuffer = buffer;
00506                                 channel->m_lastTimestamp = buffer->m_lastTimestamp;
00507                                 channel->m_lastItemPositionW = buffer->m_lastItemPositionW;
00508                         }
00509                         // now allocate a new buffer
00510                         buffer->m_next = channel->allocBuffer();
00511                         if (buffer->m_next == NULL)
00512                                 return NULL;
00513                         buffer = buffer->m_next;
00514                         positionW = 0;
00515                         item = &buffer->m_firstItem;
00516                         sizeW = CACHE_ITEM_SIZEW(item,length);
00517                 }
00518                 // all check passed, ready to write the item
00519                 item->m_sizeW = sizeW;
00520                 if (positionW == 0) {
00521                         item->m_timeOffset = 0;
00522                         buffer->m_firstTimestamp = timestamp;
00523                 } else {
00524                         item->m_timeOffset = (unsigned short)(timestamp-buffer->m_firstTimestamp);
00525                 }
00526                 buffer->m_lastItemPositionW = positionW;
00527                 buffer->m_firstFreePositionW = positionW+sizeW;
00528                 buffer->m_lastTimestamp = timestamp;
00529                 block = positionW>>channel->m_positionToBlockShiftW;
00530                 buffer->lookup[block].m_offsetW = positionW&channel->m_positionToOffsetMaskW;
00531                 buffer->lookup[block].m_timeOffset = item->m_timeOffset;
00532                 buffer->m_lastItemPositionW = CACHE_ITEM_POSITIONW(buffer,item);
00533                 buffer->m_firstFreePositionW = buffer->m_lastItemPositionW+item->m_sizeW;
00534                 channel->m_lastBuffer = buffer;
00535                 channel->m_lastItemPositionW = positionW;
00536                 channel->m_lastTimestamp = timestamp;
00537         }
00538         // now copy the item
00539         void *itemData = CACHE_ITEM_DATA_POINTER(item);
00540         if (data)
00541                 memcpy(itemData, data, length);
00542         return itemData;
00543 }
00544 
00545 const void *Cache::getPreviousCacheItem(const void *device, int id, unsigned int *timestamp)
00546 {
00547         CacheMap::iterator it;
00548         CacheEntry *entry;
00549         CacheChannel *channel;
00550         CacheBuffer *buffer;
00551         CacheItem *item;
00552 
00553         if (device) {
00554                 it = m_cache.find(device);      
00555         } else {
00556                 it = m_cache.begin();
00557         }
00558         if (it == m_cache.end()) {
00559                 // device does not exist
00560                 return NULL;
00561         }
00562         entry = it->second;
00563         if (id < 0 || id >= (int) entry->m_count || !entry->m_channelArray[id].m_busy)
00564                 return NULL;
00565         channel = &entry->m_channelArray[id];
00566         if ((item = channel->findItemEarlier(*timestamp,&buffer)) == NULL)
00567                 return NULL;
00568         *timestamp = (buffer) ? buffer->m_firstTimestamp+item->m_timeOffset : 0;
00569         return CACHE_ITEM_DATA_POINTER(item);
00570 }
00571 
00572 const CacheItem *Cache::getCurrentCacheItemInternal(const void *device, int id, CacheTS timestamp)
00573 {
00574         CacheMap::iterator it = m_cache.find(device);
00575         CacheEntry *entry;
00576         CacheChannel *channel;
00577         CacheBuffer *buffer;
00578         CacheItem *item;
00579 
00580         if (it == m_cache.end()) {
00581                 // device does not exist
00582                 return NULL;
00583         }
00584         entry = it->second;
00585         if (id < 0 || id >= (int) entry->m_count || !entry->m_channelArray[id].m_busy)
00586                 return NULL;
00587         channel = &entry->m_channelArray[id];
00588         if ((item = channel->findItemOrLater(timestamp,&buffer)) == NULL)
00589                 return NULL;
00590         if (buffer && buffer->m_firstTimestamp+item->m_timeOffset != timestamp)
00591                 return NULL;
00592         return item;
00593 }
00594 
00595 const void *Cache::getCurrentCacheItem(const void *device, int channel, unsigned int timestamp)
00596 {
00597         const CacheItem *item = getCurrentCacheItemInternal(device, channel, timestamp);
00598         return (item) ? CACHE_ITEM_DATA_POINTER(item) : NULL;
00599 }
00600 
00601 double *Cache::addCacheVectorIfDifferent(const void *device, int channel, CacheTS timestamp, double *newdata, unsigned int length, double threshold)
00602 {
00603         const CacheItem *item = getCurrentCacheItemInternal(device, channel, timestamp);
00604         unsigned int sizeW = CACHE_ITEM_SIZEW(item,length*sizeof(double));
00605         if (!item || item->m_sizeW != sizeW)
00606                 return (double*)addCacheItem(device, channel, timestamp, newdata, length*sizeof(double));
00607         double *olddata = (double*)CACHE_ITEM_DATA_POINTER(item);
00608         if (!length)
00609                 return olddata;
00610         double *ref = olddata;
00611         double *v = newdata;
00612         unsigned int i;
00613         for (i=length; i>0; --i) {
00614                 if (fabs(*v-*ref) > threshold)
00615                         break;
00616                 *ref++ = *v++;
00617         }
00618         if (i) 
00619                 olddata = (double*)addCacheItem(device, channel, timestamp, newdata, length*sizeof(double));
00620         return olddata;
00621 }
00622 
00623 }