Cache.cpp 20 KB

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