xdelta3-blkcache.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /* xdelta3 - delta compression tools and library
  2. Copyright 2016 Joshua MacDonald
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. #include "xdelta3-internal.h"
  14. typedef struct _main_blklru main_blklru;
  15. typedef struct _main_blklru_list main_blklru_list;
  16. #define XD3_INVALID_OFFSET XOFF_T_MAX
  17. struct _main_blklru_list
  18. {
  19. main_blklru_list *next;
  20. main_blklru_list *prev;
  21. };
  22. struct _main_blklru
  23. {
  24. uint8_t *blk;
  25. xoff_t blkno;
  26. usize_t size;
  27. main_blklru_list link;
  28. };
  29. XD3_MAKELIST(main_blklru_list,main_blklru,link);
  30. static usize_t lru_size = 0;
  31. static main_blklru *lru = NULL; /* array of lru_size elts */
  32. static main_blklru_list lru_list;
  33. static int do_src_fifo = 0; /* set to avoid lru */
  34. static int lru_hits = 0;
  35. static int lru_misses = 0;
  36. static int lru_filled = 0;
  37. static void main_lru_reset (void)
  38. {
  39. lru_size = 0;
  40. lru = NULL;
  41. do_src_fifo = 0;
  42. lru_hits = 0;
  43. lru_misses = 0;
  44. lru_filled = 0;
  45. }
  46. static void main_lru_cleanup (void)
  47. {
  48. if (lru != NULL)
  49. {
  50. main_buffree (lru[0].blk);
  51. }
  52. main_free (lru);
  53. lru = NULL;
  54. lru_hits = 0;
  55. lru_misses = 0;
  56. lru_filled = 0;
  57. }
  58. /* This is called at different times for encoding and decoding. The
  59. * encoder calls it immediately, the decoder delays until the
  60. * application header is received. */
  61. static int
  62. main_set_source (xd3_stream *stream, xd3_cmd cmd,
  63. main_file *sfile, xd3_source *source)
  64. {
  65. int ret = 0;
  66. usize_t i;
  67. xoff_t source_size = 0;
  68. usize_t blksize;
  69. XD3_ASSERT (lru == NULL);
  70. XD3_ASSERT (stream->src == NULL);
  71. XD3_ASSERT (option_srcwinsz >= XD3_MINSRCWINSZ);
  72. /* TODO: this code needs refactoring into FIFO, LRU, FAKE. Yuck!
  73. * This is simplified from 3.0z which had issues with sizing the
  74. * source buffer memory allocation and the source blocksize. */
  75. /* LRU-specific */
  76. main_blklru_list_init (& lru_list);
  77. if (allow_fake_source)
  78. {
  79. /* TODO: refactor
  80. * TOOLS/recode-specific: Check "allow_fake_source" mode looks
  81. * broken now. */
  82. sfile->mode = XO_READ;
  83. sfile->realname = sfile->filename;
  84. sfile->nread = 0;
  85. }
  86. else
  87. {
  88. /* Either a regular file (possibly compressed) or a FIFO
  89. * (possibly compressed). */
  90. if ((ret = main_file_open (sfile, sfile->filename, XO_READ)))
  91. {
  92. return ret;
  93. }
  94. /* If the file is regular we know it's size. If the file turns
  95. * out to be externally compressed, size_known may change. */
  96. sfile->size_known = (main_file_stat (sfile, &source_size) == 0);
  97. }
  98. /* Note: The API requires a power-of-two blocksize and srcwinsz
  99. * (-B). The logic here will use a single block if the entire file
  100. * is known to fit into srcwinsz. */
  101. option_srcwinsz = xd3_xoff_roundup (option_srcwinsz);
  102. /* Though called "lru", it is not LRU-specific. We always allocate
  103. * a maximum number of source block buffers. If the entire file
  104. * fits into srcwinsz, this buffer will stay as the only
  105. * (lru_size==1) source block. Otherwise, we know that at least
  106. * option_srcwinsz bytes are available. Split the source window
  107. * into buffers. */
  108. if ((lru = (main_blklru*) main_malloc (MAX_LRU_SIZE *
  109. sizeof (main_blklru))) == NULL)
  110. {
  111. ret = ENOMEM;
  112. return ret;
  113. }
  114. memset (lru, 0, sizeof(lru[0]) * MAX_LRU_SIZE);
  115. /* Allocate the entire buffer. */
  116. if ((lru[0].blk = (uint8_t*) main_bufalloc (option_srcwinsz)) == NULL)
  117. {
  118. ret = ENOMEM;
  119. return ret;
  120. }
  121. /* Main calls main_getblk_func() once before xd3_set_source(). This
  122. * is the point at which external decompression may begin. Set the
  123. * system for a single block. */
  124. lru_size = 1;
  125. lru[0].blkno = XD3_INVALID_OFFSET;
  126. blksize = option_srcwinsz;
  127. main_blklru_list_push_back (& lru_list, & lru[0]);
  128. XD3_ASSERT (blksize != 0);
  129. /* Initialize xd3_source. */
  130. source->blksize = blksize;
  131. source->name = sfile->filename;
  132. source->ioh = sfile;
  133. source->curblkno = XD3_INVALID_OFFSET;
  134. source->curblk = NULL;
  135. source->max_winsize = option_srcwinsz;
  136. if ((ret = main_getblk_func (stream, source, 0)) != 0)
  137. {
  138. XPR(NT "error reading source: %s: %s\n",
  139. sfile->filename,
  140. xd3_mainerror (ret));
  141. return ret;
  142. }
  143. source->onblk = lru[0].size; /* xd3 sets onblk */
  144. /* If the file is smaller than a block, size is known. */
  145. if (!sfile->size_known && source->onblk < blksize)
  146. {
  147. source_size = source->onblk;
  148. source->onlastblk = source_size;
  149. sfile->size_known = 1;
  150. }
  151. /* If the size is not known or is greater than the buffer size, we
  152. * split the buffer across MAX_LRU_SIZE blocks (already allocated in
  153. * "lru"). */
  154. if (!sfile->size_known || source_size > option_srcwinsz)
  155. {
  156. /* Modify block 0, change blocksize. */
  157. blksize = option_srcwinsz / MAX_LRU_SIZE;
  158. source->blksize = blksize;
  159. source->onblk = blksize;
  160. source->onlastblk = blksize;
  161. source->max_blkno = MAX_LRU_SIZE - 1;
  162. lru[0].size = blksize;
  163. lru_size = MAX_LRU_SIZE;
  164. /* Setup rest of blocks. */
  165. for (i = 1; i < lru_size; i += 1)
  166. {
  167. lru[i].blk = lru[0].blk + (blksize * i);
  168. lru[i].blkno = i;
  169. lru[i].size = blksize;
  170. main_blklru_list_push_back (& lru_list, & lru[i]);
  171. }
  172. }
  173. if (! sfile->size_known)
  174. {
  175. /* If the size is not know, we must use FIFO discipline. */
  176. do_src_fifo = 1;
  177. }
  178. /* Call the appropriate set_source method, handle errors, print
  179. * verbose message, etc. */
  180. if (sfile->size_known)
  181. {
  182. ret = xd3_set_source_and_size (stream, source, source_size);
  183. }
  184. else
  185. {
  186. ret = xd3_set_source (stream, source);
  187. }
  188. if (ret)
  189. {
  190. XPR(NT XD3_LIB_ERRMSG (stream, ret));
  191. return ret;
  192. }
  193. XD3_ASSERT (stream->src == source);
  194. XD3_ASSERT (source->blksize == blksize);
  195. if (option_verbose)
  196. {
  197. static shortbuf srcszbuf;
  198. static shortbuf srccntbuf;
  199. static shortbuf winszbuf;
  200. static shortbuf blkszbuf;
  201. static shortbuf nbufs;
  202. if (sfile->size_known)
  203. {
  204. short_sprintf (srcszbuf, "source size %s [%"Q"u]",
  205. main_format_bcnt (source_size, &srccntbuf),
  206. source_size);
  207. }
  208. else
  209. {
  210. short_sprintf (srcszbuf, "%s", "source size unknown");
  211. }
  212. nbufs.buf[0] = 0;
  213. if (option_verbose > 1)
  214. {
  215. short_sprintf (nbufs, " #bufs %"W"u", lru_size);
  216. }
  217. XPR(NT "source %s %s blksize %s window %s%s%s\n",
  218. sfile->filename,
  219. srcszbuf.buf,
  220. main_format_bcnt (blksize, &blkszbuf),
  221. main_format_bcnt (option_srcwinsz, &winszbuf),
  222. nbufs.buf,
  223. do_src_fifo ? " (FIFO)" : "");
  224. }
  225. return 0;
  226. }
  227. static int
  228. main_getblk_lru (xd3_source *source, xoff_t blkno,
  229. main_blklru** blrup, int *is_new)
  230. {
  231. main_blklru *blru = NULL;
  232. usize_t i;
  233. (*is_new) = 0;
  234. if (do_src_fifo)
  235. {
  236. /* Direct lookup assumes sequential scan w/o skipping blocks. */
  237. int idx = blkno % lru_size;
  238. blru = & lru[idx];
  239. if (blru->blkno == blkno)
  240. {
  241. (*blrup) = blru;
  242. return 0;
  243. }
  244. /* No going backwards in a sequential scan. */
  245. if (blru->blkno != XD3_INVALID_OFFSET && blru->blkno > blkno)
  246. {
  247. return XD3_TOOFARBACK;
  248. }
  249. }
  250. else
  251. {
  252. /* Sequential search through LRU. */
  253. for (i = 0; i < lru_size; i += 1)
  254. {
  255. blru = & lru[i];
  256. if (blru->blkno == blkno)
  257. {
  258. main_blklru_list_remove (blru);
  259. main_blklru_list_push_back (& lru_list, blru);
  260. (*blrup) = blru;
  261. IF_DEBUG1 (DP(RINT "[getblk_lru] HIT blkno = %"Q"u lru_size=%"W"u\n",
  262. blkno, lru_size));
  263. return 0;
  264. }
  265. }
  266. IF_DEBUG1 (DP(RINT "[getblk_lru] MISS blkno = %"Q"u lru_size=%"W"u\n",
  267. blkno, lru_size));
  268. }
  269. if (do_src_fifo)
  270. {
  271. int idx = blkno % lru_size;
  272. blru = & lru[idx];
  273. }
  274. else
  275. {
  276. XD3_ASSERT (! main_blklru_list_empty (& lru_list));
  277. blru = main_blklru_list_pop_front (& lru_list);
  278. main_blklru_list_push_back (& lru_list, blru);
  279. }
  280. lru_filled += 1;
  281. (*is_new) = 1;
  282. (*blrup) = blru;
  283. blru->blkno = XD3_INVALID_OFFSET;
  284. return 0;
  285. }
  286. static int
  287. main_read_seek_source (xd3_stream *stream,
  288. xd3_source *source,
  289. xoff_t blkno) {
  290. xoff_t pos = blkno * source->blksize;
  291. main_file *sfile = (main_file*) source->ioh;
  292. main_blklru *blru;
  293. int is_new;
  294. size_t nread = 0;
  295. int ret = 0;
  296. if (!sfile->seek_failed)
  297. {
  298. ret = main_file_seek (sfile, pos);
  299. if (ret == 0)
  300. {
  301. sfile->source_position = pos;
  302. }
  303. }
  304. if (sfile->seek_failed || ret != 0)
  305. {
  306. /* For an unseekable file (or other seek error, does it
  307. * matter?) */
  308. if (sfile->source_position > pos)
  309. {
  310. /* Could assert !IS_ENCODE(), this shouldn't happen
  311. * because of do_src_fifo during encode. */
  312. if (!option_quiet)
  313. {
  314. XPR(NT "source can't seek backwards; requested block offset "
  315. "%"Q"u source position is %"Q"u\n",
  316. pos, sfile->source_position);
  317. }
  318. sfile->seek_failed = 1;
  319. stream->msg = "non-seekable source: "
  320. "copy is too far back (try raising -B)";
  321. return XD3_TOOFARBACK;
  322. }
  323. /* There's a chance here, that an genuine lseek error will cause
  324. * xdelta3 to shift into non-seekable mode, entering a degraded
  325. * condition. */
  326. if (!sfile->seek_failed && option_verbose)
  327. {
  328. XPR(NT "source can't seek, will use FIFO for %s\n",
  329. sfile->filename);
  330. if (option_verbose > 1)
  331. {
  332. XPR(NT "seek error at offset %"Q"u: %s\n",
  333. pos, xd3_mainerror (ret));
  334. }
  335. }
  336. sfile->seek_failed = 1;
  337. if (option_verbose > 1 && pos != sfile->source_position)
  338. {
  339. XPR(NT "non-seekable source skipping %"Q"u bytes @ %"Q"u\n",
  340. pos - sfile->source_position,
  341. sfile->source_position);
  342. }
  343. while (sfile->source_position < pos)
  344. {
  345. xoff_t skip_blkno;
  346. usize_t skip_offset;
  347. xd3_blksize_div (sfile->source_position, source,
  348. &skip_blkno, &skip_offset);
  349. /* Read past unused data */
  350. XD3_ASSERT (pos - sfile->source_position >= source->blksize);
  351. XD3_ASSERT (skip_offset == 0);
  352. if ((ret = main_getblk_lru (source, skip_blkno,
  353. & blru, & is_new)))
  354. {
  355. return ret;
  356. }
  357. XD3_ASSERT (is_new);
  358. blru->blkno = skip_blkno;
  359. if ((ret = main_read_primary_input (sfile,
  360. (uint8_t*) blru->blk,
  361. source->blksize,
  362. & nread)))
  363. {
  364. return ret;
  365. }
  366. if (nread != source->blksize)
  367. {
  368. IF_DEBUG1 (DP(RINT "[getblk] short skip block nread = %"Z"u\n",
  369. nread));
  370. stream->msg = "non-seekable input is short";
  371. return XD3_INVALID_INPUT;
  372. }
  373. sfile->source_position += nread;
  374. blru->size = nread;
  375. IF_DEBUG1 (DP(RINT "[getblk] skip blkno %"Q"u size %"W"u\n",
  376. skip_blkno, blru->size));
  377. XD3_ASSERT (sfile->source_position <= pos);
  378. }
  379. }
  380. return 0;
  381. }
  382. /* This is the callback for reading a block of source. This function
  383. * is blocking and it implements a small LRU.
  384. *
  385. * Note that it is possible for main_input() to handle getblk requests
  386. * in a non-blocking manner. If the callback is NULL then the caller
  387. * of xd3_*_input() must handle the XD3_GETSRCBLK return value and
  388. * fill the source in the same way. See xd3_getblk for details. To
  389. * see an example of non-blocking getblk, see xdelta-test.h. */
  390. static int
  391. main_getblk_func (xd3_stream *stream,
  392. xd3_source *source,
  393. xoff_t blkno)
  394. {
  395. int ret = 0;
  396. xoff_t pos = blkno * source->blksize;
  397. main_file *sfile = (main_file*) source->ioh;
  398. main_blklru *blru;
  399. int is_new;
  400. size_t nread = 0;
  401. if (allow_fake_source)
  402. {
  403. source->curblkno = blkno;
  404. source->onblk = 0;
  405. source->curblk = lru[0].blk;
  406. lru[0].size = 0;
  407. return 0;
  408. }
  409. if ((ret = main_getblk_lru (source, blkno, & blru, & is_new)))
  410. {
  411. return ret;
  412. }
  413. if (!is_new)
  414. {
  415. source->curblkno = blkno;
  416. source->onblk = blru->size;
  417. source->curblk = blru->blk;
  418. lru_hits++;
  419. return 0;
  420. }
  421. lru_misses += 1;
  422. if (pos != sfile->source_position)
  423. {
  424. /* Only try to seek when the position is wrong. This means the
  425. * decoder will fail when the source buffer is too small, but
  426. * only when the input is non-seekable. */
  427. if ((ret = main_read_seek_source (stream, source, blkno)))
  428. {
  429. return ret;
  430. }
  431. }
  432. XD3_ASSERT (sfile->source_position == pos);
  433. if ((ret = main_read_primary_input (sfile,
  434. (uint8_t*) blru->blk,
  435. source->blksize,
  436. & nread)))
  437. {
  438. return ret;
  439. }
  440. /* Save the last block read, used to handle non-seekable files. */
  441. sfile->source_position = pos + nread;
  442. if (option_verbose > 3)
  443. {
  444. if (blru->blkno != XD3_INVALID_OFFSET)
  445. {
  446. if (blru->blkno != blkno)
  447. {
  448. XPR(NT "source block %"Q"u read %"Z"u ejects %"Q"u (lru_hits=%u, "
  449. "lru_misses=%u, lru_filled=%u)\n",
  450. blkno, nread, blru->blkno, lru_hits, lru_misses, lru_filled);
  451. }
  452. else
  453. {
  454. XPR(NT "source block %"Q"u read %"Z"u (lru_hits=%u, "
  455. "lru_misses=%u, lru_filled=%u)\n",
  456. blkno, nread, lru_hits, lru_misses, lru_filled);
  457. }
  458. }
  459. else
  460. {
  461. XPR(NT "source block %"Q"u read %"Z"u (lru_hits=%u, lru_misses=%u, "
  462. "lru_filled=%u)\n", blkno, nread,
  463. lru_hits, lru_misses, lru_filled);
  464. }
  465. }
  466. source->curblk = blru->blk;
  467. source->curblkno = blkno;
  468. source->onblk = nread;
  469. blru->size = nread;
  470. blru->blkno = blkno;
  471. IF_DEBUG1 (DP(RINT "[main_getblk] blkno %"Q"u onblk %"Z"u pos %"Q"u "
  472. "srcpos %"Q"u\n",
  473. blkno, nread, pos, sfile->source_position));
  474. return 0;
  475. }