Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1340 lines
52 KiB

  1. /*
  2. * optenc.c
  3. *
  4. * Encoder for optimal parser
  5. *
  6. *
  7. * Future Improvements:
  8. *
  9. * When two estimations are equal, for example, "should I output a
  10. * character or a match?" there should be some way of deciding
  11. * which to take. Right now we force it to output a match, but
  12. * for text files, outputting a character results in a small
  13. * savings. Even when comparing two matches, we might want to
  14. * force it to take one type of match over another.
  15. */
  16. #include "encoder.h"
  17. #define copymem(src,dst,size) memcpy(dst,src,size)
  18. static bool redo_first_block(t_encoder_context *context, long *bufpos_ptr);
  19. static void block_end(t_encoder_context *context, long BufPos);
  20. /*
  21. * encode a match of length <len> (where <len> >=2), and position <pos>
  22. */
  23. #ifdef EXTRALONGMATCHES
  24. #define OUT_MATCH(len,pos) \
  25. { \
  26. ULONG enclen = (len); \
  27. ULONG extlen = 0; \
  28. if ( enclen > MAX_MATCH ) { \
  29. extlen = enclen - MAX_MATCH; \
  30. enclen = MAX_MATCH; \
  31. } \
  32. context->enc_ItemType [context->enc_literals >> 3] |= (1 << (context->enc_literals & 7)); \
  33. context->enc_LitData [context->enc_literals ] = (byte)(enclen-MIN_MATCH); \
  34. context->enc_ExtraLength[context->enc_literals++ ] = (ushort)(extlen); \
  35. context->enc_DistData [context->enc_distances++ ] = (pos); \
  36. }
  37. #else
  38. #define OUT_MATCH(len,pos) \
  39. { \
  40. context->enc_ItemType[(context->enc_literals >> 3)] |= (1 << (context->enc_literals & 7)); \
  41. context->enc_LitData [context->enc_literals++] = (byte)(len-2); \
  42. context->enc_DistData[context->enc_distances++] = pos; \
  43. }
  44. #endif
  45. /* encode a character */
  46. #define OUT_CHAR(ch) \
  47. context->enc_LitData [context->enc_literals++] = ch;
  48. #define TREE_CREATE_CHECK() \
  49. if (context->enc_literals >= context->enc_next_tree_create) \
  50. { \
  51. update_tree_estimates(context);\
  52. context->enc_next_tree_create += TREE_CREATE_INTERVAL; \
  53. }
  54. /*
  55. * Returns an estimation of how many bits it would take to output
  56. * a given character
  57. */
  58. #define CHAR_EST(c) (numbits_t) (context->enc_main_tree_len[(c)])
  59. /*
  60. * Returns an estimation of how many bits it would take to output
  61. * a given match.
  62. *
  63. * <ml> is the match length, where ml >= 2
  64. * <mp> is the match position
  65. *
  66. * The result is stored in <result>
  67. */
  68. #define MATCH_EST(ml,mp,result) \
  69. { \
  70. ulong mp_slot; \
  71. mp_slot = MP_SLOT(mp); \
  72. if (ml < (NUM_PRIMARY_LENGTHS+2)) \
  73. { \
  74. result = (numbits_t) \
  75. (context->enc_main_tree_len[(NUM_CHARS-2)+(mp_slot<<NL_SHIFT)+ml] + \
  76. enc_extra_bits[mp_slot]); \
  77. } \
  78. else \
  79. { \
  80. result = (numbits_t) \
  81. (context->enc_main_tree_len[(NUM_CHARS+NUM_PRIMARY_LENGTHS)+(mp_slot<<NL_SHIFT)] + \
  82. context->enc_secondary_tree_len[ml-(NUM_PRIMARY_LENGTHS+2)] + \
  83. enc_extra_bits[mp_slot]); \
  84. } \
  85. }
  86. #ifdef _DEBUG
  87. static void VERIFY_MATCH(
  88. t_encoder_context *context,
  89. long bufpos,
  90. int largest_match_len
  91. )
  92. {
  93. int i, j;
  94. ulong match_pos;
  95. /*
  96. * Ensure match does not cross boundary
  97. */
  98. _ASSERTE(
  99. largest_match_len <=
  100. (CHUNK_SIZE-1) - (bufpos & (CHUNK_SIZE-1))
  101. );
  102. for (i = MIN_MATCH; i <= largest_match_len; i++)
  103. {
  104. match_pos = context->enc_matchpos_table[i];
  105. if (match_pos < NUM_REPEATED_OFFSETS)
  106. match_pos = context->enc_last_matchpos_offset[match_pos];
  107. else
  108. match_pos -= (NUM_REPEATED_OFFSETS-1);
  109. _ASSERTE (match_pos <= context->enc_window_size-4);
  110. for (j = 0; j < i; j++)
  111. {
  112. _ASSERTE (
  113. context->enc_MemWindow[bufpos+j] ==
  114. context->enc_MemWindow[bufpos-match_pos+j]
  115. );
  116. }
  117. }
  118. }
  119. #else
  120. #define VERIFY_MATCH(a,b,c) ;
  121. #endif
  122. void flush_all_pending_blocks(t_encoder_context *context)
  123. {
  124. /*
  125. * Force all blocks to be output
  126. */
  127. while (context->enc_literals > 0)
  128. output_block(context);
  129. /*
  130. * Flush compressed data out to the caller
  131. */
  132. perform_flush_output_callback(context);
  133. }
  134. void encoder_start(t_encoder_context *context)
  135. {
  136. long BytesRead, RealBufPos;
  137. /*
  138. * RealBufPos is our position in the window,
  139. * and equals [0...window_size + second_partition_size - 1]
  140. */
  141. RealBufPos = context->enc_BufPos - (ulong)(context->enc_RealMemWindow - context->enc_MemWindow);
  142. BytesRead = comp_read_input(context, RealBufPos, CHUNK_SIZE);
  143. if (BytesRead > 0)
  144. opt_encode_top(context, BytesRead);
  145. }
  146. static void update_tree_estimates(t_encoder_context *context)
  147. {
  148. if (context->enc_literals)
  149. {
  150. /*
  151. * Get stats on literals from 0...context->enc_literals
  152. */
  153. if (context->enc_need_to_recalc_stats)
  154. {
  155. /*
  156. * Cumulative total was destroyed, so need to
  157. * recalculate
  158. */
  159. get_block_stats(
  160. context,
  161. 0,
  162. 0,
  163. context->enc_literals
  164. );
  165. context->enc_need_to_recalc_stats = false;
  166. }
  167. else
  168. {
  169. /*
  170. * Add stats from last_literals...context->enc_literals
  171. * to cumulative total
  172. */
  173. update_cumulative_block_stats(
  174. context,
  175. context->enc_last_literals,
  176. context->enc_last_distances,
  177. context->enc_literals
  178. );
  179. }
  180. create_trees(context, false); /* don't generate codes */
  181. fix_tree_cost_estimates(context);
  182. /*
  183. * For cumulative total
  184. */
  185. context->enc_last_literals = context->enc_literals;
  186. context->enc_last_distances = context->enc_distances;
  187. }
  188. }
  189. void opt_encode_top(t_encoder_context *context, long BytesRead)
  190. {
  191. ulong BufPos;
  192. ulong RealBufPos;
  193. ulong BufPosEnd;
  194. ulong BufPosEndThisChunk;
  195. ulong MatchPos;
  196. ulong i;
  197. ulong end_pos;
  198. long EncMatchLength; /* must be a signed number */
  199. long ExMatchOff = -1; /* initialize to prevent compiler warning */
  200. /*
  201. * Current position in encoding window
  202. */
  203. BufPos = context->enc_BufPos;
  204. /*
  205. * Stop encoding when we reach here
  206. */
  207. BufPosEnd = context->enc_BufPos + BytesRead;
  208. /*
  209. * If this is our first time in here (since a new group), then
  210. * when we reach this many literals, update our tree cost
  211. * estimates.
  212. *
  213. * Also, output the file size we're using for translation
  214. * (0 means no translation at all, which will speed things up
  215. * for the decoder).
  216. */
  217. if (context->enc_first_time_this_group)
  218. {
  219. context->enc_first_time_this_group = false;
  220. /*
  221. * Recreate trees when we reach this many literals
  222. */
  223. context->enc_next_tree_create = 10000;
  224. if (context->enc_file_size_for_translation)
  225. {
  226. output_bits(context, 1, 1); /* translation */
  227. output_bits(context, 16, context->enc_file_size_for_translation >> 16);
  228. output_bits(context, 16, context->enc_file_size_for_translation & 65535);
  229. }
  230. else
  231. {
  232. output_bits(context, 1, 0); /* no translation */
  233. }
  234. }
  235. else
  236. {
  237. /*
  238. * If this is our second or later time in here, then add in the
  239. * strings we removed last time.
  240. *
  241. * We have to be careful here, though, because end_pos is
  242. * equal to our current BufPos - window_size, not
  243. * BufPos - i - window_size; we don't have that much history
  244. * around.
  245. */
  246. for (i = BREAK_LENGTH; i > 0; i--)
  247. quick_insert_bsearch_findmatch(
  248. context,
  249. BufPos - (long) i,
  250. BufPos - context->enc_window_size+4
  251. );
  252. }
  253. while (1)
  254. {
  255. top_of_main_loop:
  256. /*
  257. * While we haven't reached the end of the data
  258. */
  259. while (BufPos < BufPosEnd)
  260. {
  261. BufPosEndThisChunk = ( BufPos + CHUNK_SIZE ) & ~( CHUNK_SIZE - 1 );
  262. if ( BufPosEndThisChunk > BufPosEnd ) {
  263. BufPosEndThisChunk = BufPosEnd;
  264. }
  265. /*
  266. * Search for matches of all different possible lengths, at BufPos
  267. */
  268. EncMatchLength = binary_search_findmatch(context, BufPos);
  269. if (EncMatchLength < MIN_MATCH)
  270. {
  271. output_literal:
  272. /*
  273. * No match longer than 1 character exists in the history
  274. * window, so output the character at BufPos as a symbol.
  275. */
  276. OUT_CHAR(context->enc_MemWindow[BufPos]);
  277. #ifdef TRACING
  278. EncTracingLiteral( BufPos, context->enc_MemWindow[BufPos] );
  279. #endif
  280. BufPos++;
  281. /*
  282. * Check for exceeding literal buffer
  283. */
  284. if (context->enc_literals >= (MAX_LITERAL_ITEMS-8))
  285. block_end(context, BufPos);
  286. continue;
  287. }
  288. /*
  289. * Found a match.
  290. *
  291. * Make sure it cannot exceed the end of the buffer.
  292. */
  293. if ( EncMatchLength > (long)( BufPosEndThisChunk - BufPos ))
  294. {
  295. EncMatchLength = (long)( BufPosEndThisChunk - BufPos );
  296. /*
  297. * Oops, not enough for even a small match, so we
  298. * have to output a literal
  299. */
  300. if (EncMatchLength < MIN_MATCH)
  301. goto output_literal;
  302. }
  303. VERIFY_MATCH(context, BufPos, EncMatchLength);
  304. if (EncMatchLength < FAST_DECISION_THRESHOLD)
  305. {
  306. /*
  307. * A match has been found that is between MIN_MATCH and
  308. * FAST_DECISION_THRESHOLD bytes in length. The following
  309. * algorithm is the optimal encoder that will determine the
  310. * most efficient order of matches and unmatched characters
  311. * over a span area defined by LOOK.
  312. *
  313. * The code is essentially a shortest path determination
  314. * algorithm. A stream of data can be encoded in a vast number
  315. * of different ways depending on the match lengths and offsets
  316. * chosen. The key to good compression ratios is to chose the
  317. * least expensive path.
  318. */
  319. ulong span;
  320. ulong epos, bpos, NextPrevPos;
  321. decision_node *decision_node_ptr;
  322. long iterations;
  323. /*
  324. * Points to the end of the area covered by this match; the span
  325. * will continually be extended whenever we find more matches
  326. * later on. It will stop being extended when we reach a spot
  327. * where there are no matches, which is when we decide which
  328. * path to take to output the matches.
  329. */
  330. span = BufPos + EncMatchLength;
  331. /*
  332. * The furthest position into which we will do our lookahead parsing
  333. */
  334. epos = BufPos + LOOK;
  335. /*
  336. * Temporary BufPos variable
  337. */
  338. bpos = BufPos;
  339. /*
  340. * Calculate the path to the next character if we output
  341. * an unmatched symbol.
  342. */
  343. /* bits required to get here */
  344. context->enc_decision_node[1].numbits = CHAR_EST(context->enc_MemWindow[BufPos]);
  345. /* where we came from */
  346. context->enc_decision_node[1].path = BufPos;
  347. /*
  348. * For the match found, estimate the cost of encoding the match
  349. * for each possible match length, shortest offset combination.
  350. *
  351. * The cost, path and offset is stored at BufPos + Length.
  352. */
  353. for (i = MIN_MATCH; i <= (ulong)EncMatchLength; i++)
  354. {
  355. /*
  356. * Get estimation of match cost given match length = i,
  357. * match position = context->enc_matchpos_table[i], and store
  358. * the result in context->enc_numbits[i]
  359. */
  360. MATCH_EST(i, context->enc_matchpos_table[i], context->enc_decision_node[i].numbits);
  361. /*
  362. * Where we came from
  363. */
  364. context->enc_decision_node[i].path = BufPos;
  365. /*
  366. * Associated match position with this path
  367. */
  368. context->enc_decision_node[i].link = context->enc_matchpos_table[i];
  369. #ifdef TRACING
  370. {
  371. ULONG TrMatchPos = context->enc_matchpos_table[i];
  372. ULONG TrMatchOff;
  373. if ( TrMatchPos < NUM_REPEATED_OFFSETS ) {
  374. TrMatchOff = context->enc_last_matchpos_offset[ TrMatchPos ];
  375. }
  376. else {
  377. TrMatchOff = TrMatchPos - ( NUM_REPEATED_OFFSETS - 1 );
  378. }
  379. context->enc_decision_node[i].matchoff = TrMatchOff;
  380. }
  381. #endif
  382. }
  383. /*
  384. * Set bit counter to zero at the start
  385. */
  386. context->enc_decision_node[0].numbits = 0;
  387. /*
  388. * Initialise relative match position tables
  389. *
  390. * Really context->enc_repeated_offset_table[BufPos-bpos][x], but here
  391. * BufPos == bpos
  392. */
  393. context->enc_decision_node[0].repeated_offset[0] = context->enc_last_matchpos_offset[0];
  394. context->enc_decision_node[0].repeated_offset[1] = context->enc_last_matchpos_offset[1];
  395. context->enc_decision_node[0].repeated_offset[2] = context->enc_last_matchpos_offset[2];
  396. decision_node_ptr = &context->enc_decision_node[-(long) bpos];
  397. #define rpt_offset_ptr(where,which_offset) decision_node_ptr[(where)].repeated_offset[(which_offset)]
  398. while (1)
  399. {
  400. numbits_t est, cum_numbits;
  401. BufPos++;
  402. /*
  403. * Set the proper repeated offset locations depending on the
  404. * shortest path to the location prior to searching for a
  405. * match.
  406. */
  407. /*
  408. * If this is a match (i.e. path skips over more
  409. * than one character).
  410. */
  411. if (decision_node_ptr[BufPos].path != (ulong) (BufPos-1))
  412. {
  413. ulong LastPos = decision_node_ptr[BufPos].path;
  414. /*
  415. * link_ptr[BufPos] is the match position for this
  416. * location
  417. */
  418. if (decision_node_ptr[BufPos].link >= NUM_REPEATED_OFFSETS)
  419. {
  420. context->enc_last_matchpos_offset[0] = decision_node_ptr[BufPos].link-(NUM_REPEATED_OFFSETS-1);
  421. context->enc_last_matchpos_offset[1] = rpt_offset_ptr(LastPos,0);
  422. context->enc_last_matchpos_offset[2] = rpt_offset_ptr(LastPos,1);
  423. }
  424. else if (decision_node_ptr[BufPos].link == 0)
  425. {
  426. context->enc_last_matchpos_offset[0] = rpt_offset_ptr(LastPos,0);
  427. context->enc_last_matchpos_offset[1] = rpt_offset_ptr(LastPos,1);
  428. context->enc_last_matchpos_offset[2] = rpt_offset_ptr(LastPos,2);
  429. }
  430. else if (decision_node_ptr[BufPos].link == 1)
  431. {
  432. context->enc_last_matchpos_offset[0] = rpt_offset_ptr(LastPos,1);
  433. context->enc_last_matchpos_offset[1] = rpt_offset_ptr(LastPos,0);
  434. context->enc_last_matchpos_offset[2] = rpt_offset_ptr(LastPos,2);
  435. }
  436. else /* == 2 */
  437. {
  438. context->enc_last_matchpos_offset[0] = rpt_offset_ptr(LastPos,2);
  439. context->enc_last_matchpos_offset[1] = rpt_offset_ptr(LastPos,1);
  440. context->enc_last_matchpos_offset[2] = rpt_offset_ptr(LastPos,0);
  441. }
  442. }
  443. rpt_offset_ptr(BufPos,0) = context->enc_last_matchpos_offset[0];
  444. rpt_offset_ptr(BufPos,1) = context->enc_last_matchpos_offset[1];
  445. rpt_offset_ptr(BufPos,2) = context->enc_last_matchpos_offset[2];
  446. /*
  447. * The following is one of the two possible break points from
  448. * the inner encoding loop. This break will exit the loop if
  449. * a point is reached that no match can incorporate; i.e. a
  450. * character that does not match back to anything is a point
  451. * where all possible paths will converge and the longest one
  452. * can be chosen.
  453. */
  454. if (span == BufPos)
  455. break;
  456. /*
  457. * Search for matches at BufPos
  458. */
  459. EncMatchLength = binary_search_findmatch(context, BufPos);
  460. /*
  461. * Make sure that the match does not exceed the stop point
  462. */
  463. if ((ulong) EncMatchLength + BufPos > BufPosEndThisChunk)
  464. {
  465. EncMatchLength = BufPosEndThisChunk - BufPos;
  466. if (EncMatchLength < MIN_MATCH)
  467. EncMatchLength = 0;
  468. }
  469. VERIFY_MATCH(context, BufPos, EncMatchLength);
  470. /*
  471. * If the match is very long or it exceeds epos (either
  472. * surpassing the LOOK area, or exceeding past the end of the
  473. * input buffer), then break the loop and output the path.
  474. */
  475. if (EncMatchLength > FAST_DECISION_THRESHOLD ||
  476. BufPos + (ulong) EncMatchLength >= epos)
  477. {
  478. MatchPos = context->enc_matchpos_table[EncMatchLength];
  479. #ifdef EXTRALONGMATCHES
  480. if ( EncMatchLength == MAX_MATCH ) {
  481. if ( MatchPos < NUM_REPEATED_OFFSETS ) {
  482. ExMatchOff = context->enc_last_matchpos_offset[ MatchPos ];
  483. }
  484. else {
  485. ExMatchOff = MatchPos - ( NUM_REPEATED_OFFSETS - 1 );
  486. }
  487. }
  488. #endif
  489. #ifdef TRACING
  490. {
  491. ULONG TrMatchOff;
  492. if ( MatchPos < NUM_REPEATED_OFFSETS ) {
  493. TrMatchOff = context->enc_last_matchpos_offset[ MatchPos ];
  494. }
  495. else {
  496. TrMatchOff = MatchPos - ( NUM_REPEATED_OFFSETS - 1 );
  497. }
  498. decision_node_ptr[BufPos+EncMatchLength].matchoff = TrMatchOff;
  499. }
  500. #endif
  501. decision_node_ptr[BufPos+EncMatchLength].link = MatchPos;
  502. decision_node_ptr[BufPos+EncMatchLength].path = BufPos;
  503. /*
  504. * Quickly insert data into the search tree without
  505. * returning match positions/lengths
  506. */
  507. #ifndef INSERT_NEAR_LONG_MATCHES
  508. if (MatchPos == 3 && EncMatchLength > 16)
  509. {
  510. /*
  511. * If we found a match 1 character away and it's
  512. * length 16 or more, it's probably a string of
  513. * zeroes, so don't insert that into the search
  514. * engine, since doing so can slow things down
  515. * significantly!
  516. */
  517. quick_insert_bsearch_findmatch(
  518. context,
  519. BufPos + 1,
  520. BufPos - context->enc_window_size + (1 + 4) /* bp+1 -(ws-4) */
  521. );
  522. }
  523. else
  524. #endif
  525. {
  526. for (i = 1; i < (ulong) EncMatchLength; i++)
  527. quick_insert_bsearch_findmatch(
  528. context,
  529. BufPos + i,
  530. BufPos + i - context->enc_window_size + 4
  531. );
  532. }
  533. BufPos += EncMatchLength;
  534. /*
  535. * Update the relative match positions
  536. */
  537. if (MatchPos >= NUM_REPEATED_OFFSETS)
  538. {
  539. context->enc_last_matchpos_offset[2] = context->enc_last_matchpos_offset[1];
  540. context->enc_last_matchpos_offset[1] = context->enc_last_matchpos_offset[0];
  541. context->enc_last_matchpos_offset[0] = MatchPos-(NUM_REPEATED_OFFSETS-1);
  542. }
  543. else if (MatchPos)
  544. {
  545. ulong t = context->enc_last_matchpos_offset[0];
  546. context->enc_last_matchpos_offset[0] = context->enc_last_matchpos_offset[MatchPos];
  547. context->enc_last_matchpos_offset[MatchPos] = t;
  548. }
  549. break;
  550. }
  551. /*
  552. * The following code will extend the area spanned by the
  553. * set of matches if the current match surpasses the end of
  554. * the span. A match of length two that is far is not
  555. * accepted, since it would normally be encoded as characters,
  556. * thus allowing the paths to converge.
  557. */
  558. if (EncMatchLength > 2 ||
  559. (EncMatchLength == 2 && context->enc_matchpos_table[2] < BREAK_MAX_LENGTH_TWO_OFFSET))
  560. {
  561. if (span < (ulong) (BufPos + EncMatchLength))
  562. {
  563. long end;
  564. long _i;
  565. end = min(BufPos+EncMatchLength-bpos, LOOK-1);
  566. /*
  567. * These new positions are undefined for now, since we haven't
  568. * gone there yet, so put in the costliest value
  569. */
  570. for (_i = span-bpos+1; _i <= end; _i++)
  571. context->enc_decision_node[_i].numbits = (numbits_t) -1;
  572. span = BufPos + EncMatchLength;
  573. }
  574. }
  575. /*
  576. * The following code will iterate through all combinations
  577. * of match lengths for the current match. It will estimate
  578. * the cost of the path from the beginning of LOOK to
  579. * BufPos and to every locations spanned by the current
  580. * match. If the path through BufPos with the found matches
  581. * is estimated to take fewer number of bits to encode than
  582. * the previously found match, then the path to the location
  583. * is altered.
  584. *
  585. * The code relies on accurate estimation of the cost of
  586. * encoding a character or a match. Furthermore, it requires
  587. * a search engine that will store the smallest match offset
  588. * of each possible match length.
  589. *
  590. * A match of length one is simply treated as an unmatched
  591. * character.
  592. */
  593. /*
  594. * Get the estimated number of bits required to encode the
  595. * path leading up to BufPos.
  596. */
  597. cum_numbits = decision_node_ptr[BufPos].numbits;
  598. /*
  599. * Calculate the estimated cost of outputting the path through
  600. * BufPos and outputting the next character as an unmatched byte
  601. */
  602. est = cum_numbits + CHAR_EST(context->enc_MemWindow[BufPos]);
  603. /*
  604. * Check if it is more efficient to encode the next character
  605. * as an unmatched character rather than the previously found
  606. * match. If so, then update the cheapest path to BufPos + 1.
  607. *
  608. * What happens if est == numbits[BufPos-bpos+1]; i.e. it
  609. * works out as well to output a character as to output a
  610. * match? It's a tough call; however, we will push the
  611. * encoder to use matches where possible.
  612. */
  613. if (est < decision_node_ptr[BufPos+1].numbits)
  614. {
  615. decision_node_ptr[BufPos+1].numbits = est;
  616. decision_node_ptr[BufPos+1].path = BufPos;
  617. }
  618. /*
  619. * Now, iterate through the remaining match lengths and
  620. * compare the new path to the existing. Change the path
  621. * if it is found to be more cost effective to go through
  622. * BufPos.
  623. */
  624. for (i = MIN_MATCH; i <= (ulong) EncMatchLength; i++)
  625. {
  626. MATCH_EST(i, context->enc_matchpos_table[i], est);
  627. est += cum_numbits;
  628. /*
  629. * If est == numbits[BufPos+i] we want to leave things
  630. * alone, since this will tend to force the matches
  631. * to be smaller in size, which is beneficial for most
  632. * data.
  633. */
  634. if (est < decision_node_ptr[BufPos+i].numbits)
  635. {
  636. decision_node_ptr[BufPos+i].numbits = est;
  637. decision_node_ptr[BufPos+i].path = BufPos;
  638. decision_node_ptr[BufPos+i].link = context->enc_matchpos_table[i];
  639. #ifdef TRACING
  640. {
  641. ULONG TrMatchPos = context->enc_matchpos_table[i];
  642. ULONG TrMatchOff;
  643. if ( TrMatchPos < NUM_REPEATED_OFFSETS ) {
  644. TrMatchOff = context->enc_last_matchpos_offset[ TrMatchPos ];
  645. }
  646. else {
  647. TrMatchOff = TrMatchPos - ( NUM_REPEATED_OFFSETS - 1 );
  648. }
  649. decision_node_ptr[BufPos+i].matchoff = TrMatchOff;
  650. }
  651. #endif
  652. }
  653. }
  654. } /* continue to loop through span of matches */
  655. /*
  656. * Here BufPos == span, ie. a non-matchable character found. The
  657. * following code will output the path properly.
  658. */
  659. /*
  660. * Unfortunately the path is stored in reverse; how to get from
  661. * where we are now, to get back to where it all started.
  662. *
  663. * Traverse the path back to the original starting position
  664. * of the LOOK span. Invert the path pointers in order to be
  665. * able to traverse back to the current position from the start.
  666. */
  667. /*
  668. * Count the number of iterations we did, so when we go forwards
  669. * we'll do the same amount
  670. */
  671. iterations = 0;
  672. NextPrevPos = decision_node_ptr[BufPos].path;
  673. do
  674. {
  675. ulong PrevPos;
  676. PrevPos = NextPrevPos;
  677. NextPrevPos = decision_node_ptr[PrevPos].path;
  678. decision_node_ptr[PrevPos].path = BufPos;
  679. BufPos = PrevPos;
  680. iterations++;
  681. } while (BufPos != bpos);
  682. if (context->enc_literals + iterations >= (MAX_LITERAL_ITEMS-8) ||
  683. context->enc_distances + iterations >= (MAX_DIST_ITEMS-8))
  684. {
  685. block_end(context, BufPos);
  686. }
  687. /*
  688. * Traverse from the beginning of the LOOK span to the end of
  689. * the span along the stored path, outputting matches and
  690. * characters appropriately.
  691. */
  692. do
  693. {
  694. if (decision_node_ptr[BufPos].path > BufPos+1)
  695. {
  696. /*
  697. * Path skips over more than 1 character; therefore it's a match
  698. */
  699. #ifdef EXTRALONGMATCHES
  700. //
  701. // If the match length to output here is MAX_MATCH,
  702. // this must be the last entry in the decision chain,
  703. // and we can extend the match as far as it will go.
  704. //
  705. long ExMatchPos = decision_node_ptr[ decision_node_ptr[BufPos].path ].link;
  706. long ExMatchLength = decision_node_ptr[BufPos].path - BufPos;
  707. if ( ExMatchLength == MAX_MATCH ) {
  708. ulong ExBufPtr = BufPos + MAX_MATCH;
  709. #ifdef TRACING
  710. ASSERT( ExMatchOff == (long)decision_node_ptr[ decision_node_ptr[BufPos].path ].matchoff );
  711. #endif /* TRACING */
  712. while (( ExBufPtr < BufPosEndThisChunk ) &&
  713. ( context->enc_MemWindow[ ExBufPtr ] == context->enc_MemWindow[ ExBufPtr - ExMatchOff ] )) {
  714. ++ExBufPtr;
  715. ++ExMatchLength;
  716. }
  717. }
  718. OUT_MATCH( ExMatchLength, ExMatchPos );
  719. #ifdef TRACING
  720. EncTracingMatch(
  721. BufPos,
  722. ExMatchLength,
  723. ExMatchPos,
  724. decision_node_ptr[ decision_node_ptr[BufPos].path ].matchoff
  725. );
  726. #endif // TRACING
  727. BufPos += ExMatchLength;
  728. #else /* ! EXTRALONGMATCHES */
  729. OUT_MATCH(
  730. decision_node_ptr[BufPos].path - BufPos,
  731. decision_node_ptr[ decision_node_ptr[BufPos].path ].link
  732. );
  733. #ifdef TRACING
  734. EncTracingMatch(
  735. BufPos,
  736. decision_node_ptr[BufPos].path - BufPos,
  737. decision_node_ptr[ decision_node_ptr[BufPos].path ].link,
  738. decision_node_ptr[ decision_node_ptr[BufPos].path ].matchpos
  739. );
  740. #endif // TRACING
  741. BufPos = decision_node_ptr[BufPos].path;
  742. #endif /* ! EXTRALONGMATCHES */
  743. }
  744. else
  745. {
  746. /*
  747. * Path goes to the next character; therefore it's a symbol
  748. */
  749. OUT_CHAR(context->enc_MemWindow[BufPos]);
  750. #ifdef TRACING
  751. EncTracingLiteral( BufPos, context->enc_MemWindow[BufPos] );
  752. #endif
  753. BufPos++;
  754. }
  755. } while (--iterations != 0);
  756. TREE_CREATE_CHECK();
  757. /*
  758. * If we're filling up, and are close to outputting a block,
  759. * and it's the first block, then recompress the first N
  760. * literals using our accumulated stats.
  761. */
  762. if (context->enc_first_block &&
  763. (context->enc_literals >= (MAX_LITERAL_ITEMS-512)
  764. || context->enc_distances >= (MAX_DIST_ITEMS-512)))
  765. {
  766. if (redo_first_block(context, &BufPos))
  767. goto top_of_main_loop;
  768. /*
  769. * Unable to redo, so output the block
  770. */
  771. block_end(context, BufPos);
  772. }
  773. }
  774. else /* EncMatchLength >= FAST_DECISION_THRESHOLD */
  775. {
  776. /*
  777. * This code reflects a speed optimization that will always take
  778. * a match of length >= FAST_DECISION_THRESHOLD characters.
  779. */
  780. /*
  781. * The position associated with the match we found
  782. */
  783. MatchPos = context->enc_matchpos_table[EncMatchLength];
  784. #ifdef EXTRALONGMATCHES
  785. if ( EncMatchLength == MAX_MATCH ) {
  786. //
  787. // Extend the match length up to end of input buffer
  788. // or the current position in the history buffer.
  789. //
  790. ulong BufPtr = BufPos + MAX_MATCH;
  791. long MatchOff;
  792. if ( MatchPos < NUM_REPEATED_OFFSETS ) {
  793. MatchOff = context->enc_last_matchpos_offset[ MatchPos ];
  794. }
  795. else {
  796. MatchOff = MatchPos - ( NUM_REPEATED_OFFSETS - 1 );
  797. }
  798. while (( BufPtr < BufPosEndThisChunk ) &&
  799. ( context->enc_MemWindow[ BufPtr ] == context->enc_MemWindow[ BufPtr - MatchOff ] )) {
  800. ++BufPtr;
  801. ++EncMatchLength;
  802. }
  803. }
  804. #endif
  805. /*
  806. * Quickly insert match substrings into search tree
  807. * (don't look for new matches; just insert the strings)
  808. */
  809. #ifndef INSERT_NEAR_LONG_MATCHES
  810. if (MatchPos == 3 && EncMatchLength > 16)
  811. {
  812. quick_insert_bsearch_findmatch(
  813. context,
  814. BufPos + 1,
  815. BufPos - context->enc_window_size + 5 /* bp+1 -(ws-4) */
  816. );
  817. }
  818. else
  819. #endif
  820. {
  821. for (i = 1; i < (ulong) EncMatchLength; i++)
  822. quick_insert_bsearch_findmatch(
  823. context,
  824. BufPos + i,
  825. BufPos + i - context->enc_window_size + 4
  826. );
  827. }
  828. /*
  829. * Output the match
  830. */
  831. OUT_MATCH(EncMatchLength, MatchPos);
  832. #ifdef TRACING
  833. {
  834. ULONG TrMatchOff;
  835. if ( MatchPos < NUM_REPEATED_OFFSETS ) {
  836. TrMatchOff = context->enc_last_matchpos_offset[ MatchPos ];
  837. }
  838. else {
  839. TrMatchOff = MatchPos - ( NUM_REPEATED_OFFSETS - 1 );
  840. }
  841. EncTracingMatch(
  842. BufPos,
  843. EncMatchLength,
  844. MatchPos,
  845. TrMatchOff
  846. );
  847. }
  848. #endif // TRACING
  849. /*
  850. * Advance our position in the window
  851. */
  852. BufPos += EncMatchLength;
  853. if (MatchPos >= NUM_REPEATED_OFFSETS)
  854. {
  855. context->enc_last_matchpos_offset[2] = context->enc_last_matchpos_offset[1];
  856. context->enc_last_matchpos_offset[1] = context->enc_last_matchpos_offset[0];
  857. context->enc_last_matchpos_offset[0] = MatchPos-(NUM_REPEATED_OFFSETS-1);
  858. }
  859. else if (MatchPos)
  860. {
  861. ulong t = context->enc_last_matchpos_offset[0];
  862. context->enc_last_matchpos_offset[0] = context->enc_last_matchpos_offset[MatchPos];
  863. context->enc_last_matchpos_offset[MatchPos] = t;
  864. }
  865. /*
  866. * Check to see if we're close to overflowing our output arrays, and
  867. * output a block if this is the case
  868. */
  869. if (context->enc_literals >= (MAX_LITERAL_ITEMS-8) ||
  870. context->enc_distances >= (MAX_DIST_ITEMS-8))
  871. block_end(context, BufPos);
  872. } /* EncMatchLength >= FAST_DECISION_THRESHOLD */
  873. } /* end while ... BufPos < BufPosEnd */
  874. /*
  875. * Value of BufPos corresponding to earliest window data
  876. */
  877. context->enc_earliest_window_data_remaining = BufPos - context->enc_window_size;
  878. /*
  879. * We didn't read 32K, so we know for sure that
  880. * this was our last block of data.
  881. */
  882. if (BytesRead < CHUNK_SIZE)
  883. {
  884. /*
  885. * If we have never output a block, and we haven't
  886. * recalculated the stats already, then recalculate
  887. * the stats and recompress.
  888. */
  889. if (context->enc_first_block)
  890. {
  891. if (redo_first_block(context, &BufPos))
  892. goto top_of_main_loop;
  893. }
  894. break;
  895. }
  896. /*
  897. * Remove the last BREAK_LENGTH nodes from the binary search tree,
  898. * since we have been inserting strings which contain undefined
  899. * data at the end.
  900. */
  901. end_pos = BufPos - (context->enc_window_size-4-BREAK_LENGTH);
  902. for (i = 1; (i <= BREAK_LENGTH); i++)
  903. binary_search_remove_node(context, BufPos-i, end_pos);
  904. /*
  905. * If we're still in the first window_size + second partition size
  906. * bytes in the file then we don't need to copymem() yet.
  907. *
  908. * RealBufPos is the real position in the file.
  909. */
  910. RealBufPos = BufPos - (ulong)(context->enc_RealMemWindow - context->enc_MemWindow);
  911. if (RealBufPos < context->enc_window_size + context->enc_encoder_second_partition_size)
  912. break;
  913. /*
  914. * We're about to trash a whole bunch of history with our copymem,
  915. * so we'd better redo the first block now if we are ever going to.
  916. */
  917. if (context->enc_first_block)
  918. {
  919. if (redo_first_block(context, &BufPos))
  920. goto top_of_main_loop;
  921. }
  922. /*
  923. * We're about to remove a large number of symbols from the window.
  924. * Test to see whether, if we were to output a block now, our compressed
  925. * output size would be larger than our uncompressed data. If so, then
  926. * we will output an uncompressed block.
  927. *
  928. * The reason we have to do this check here, is that data in the
  929. * window is about to be destroyed. We can't simply put this check in
  930. * the block outputting code, since there is no guarantee that the
  931. * memory window contents corresponding to everything in that block,
  932. * are still around - all we'd have would be a set of literals and
  933. * distances, when we need all the uncompressed literals to output
  934. * an uncompressed block.
  935. */
  936. /*
  937. * What value of bufpos corresponds to the oldest data we have in the
  938. * buffer?
  939. *
  940. * After the memory copy, that will be the current buffer position,
  941. * minus window_size.
  942. */
  943. /*
  944. * The end of the data buffer is reached, more data needs to be read
  945. * and the existing data must be shifted into the history window.
  946. *
  947. * MSVC 4.x generates code which does REP MOVSD so no need to
  948. * write this in assembly.
  949. */
  950. copymem(
  951. &context->enc_RealMemWindow[context->enc_encoder_second_partition_size],
  952. &context->enc_RealMemWindow[0],
  953. context->enc_window_size
  954. );
  955. copymem(
  956. &context->enc_RealLeft[context->enc_encoder_second_partition_size],
  957. &context->enc_RealLeft[0],
  958. sizeof(ulong)*context->enc_window_size
  959. );
  960. copymem(
  961. &context->enc_RealRight[context->enc_encoder_second_partition_size],
  962. &context->enc_RealRight[0],
  963. sizeof(ulong)*context->enc_window_size
  964. );
  965. context->enc_earliest_window_data_remaining = BufPos - context->enc_window_size;
  966. /*
  967. * The following bit of code is CRUCIAL yet unorthodox in function
  968. * and serves as a speed and syntax optimization and makes the code
  969. * easier to understand once grasped.
  970. *
  971. * The three main buffers, context->enc_MemWindow, context->enc_Left and context->enc_Right,
  972. * are referensed by BufPos and SearchPos relative to the current
  973. * compression window locations. When the encoder reaches the end
  974. * of its block of input memory, the data in the input buffer is
  975. * shifted into the compression history window and the new input
  976. * stream is loaded. Typically the BufPos pointer would be reduced
  977. * to signify the replaced data. However, this code reduces the
  978. * base pointers to reflect the shift of data, and leaves the BufPos
  979. * pointer in its current state. Therefore, the BufPos pointer is
  980. * an absolute pointer reflecting the position in the input stream,
  981. * and NOT the position in the buffer. The base pointers will point
  982. * to invalid memory locations with addresses smaller than the
  983. * actual array base pointers. However, when the two pointers are
  984. * added together, &(context->enc_MemWindow+BufPos), it will point to the
  985. * correct and valid position in the buffer.
  986. */
  987. context->enc_MemWindow -= context->enc_encoder_second_partition_size;
  988. context->enc_Left -= context->enc_encoder_second_partition_size;
  989. context->enc_Right -= context->enc_encoder_second_partition_size;
  990. break;
  991. }
  992. /*
  993. * Store BufPos in global variable
  994. */
  995. context->enc_BufPos = BufPos;
  996. }
  997. static void block_end(t_encoder_context *context, long BufPos)
  998. {
  999. context->enc_first_block = false;
  1000. context->enc_need_to_recalc_stats = true;
  1001. output_block(context);
  1002. if (context->enc_literals < TREE_CREATE_INTERVAL)
  1003. {
  1004. context->enc_next_tree_create = TREE_CREATE_INTERVAL;
  1005. }
  1006. else
  1007. {
  1008. context->enc_next_tree_create = context->enc_literals + TREE_CREATE_INTERVAL; /* recreate right away */
  1009. }
  1010. context->enc_bufpos_last_output_block = BufPos;
  1011. }
  1012. static bool redo_first_block(t_encoder_context *context, long *bufpos_ptr)
  1013. {
  1014. long start_at;
  1015. long earliest_can_start_at;
  1016. long pos_in_file;
  1017. long history_needed;
  1018. long history_avail;
  1019. long BufPos;
  1020. long split_at_literal;
  1021. context->enc_first_block = false;
  1022. BufPos = *bufpos_ptr;
  1023. /*
  1024. * For the first context->enc_window size bytes in the file, we don't
  1025. * need to have context->enc_window size bytes around.
  1026. *
  1027. * For anything after that, though, we do need to have window_size
  1028. * previous bytes to look into.
  1029. */
  1030. /*
  1031. * How many bytes are we into the file?
  1032. */
  1033. pos_in_file = BufPos - context->enc_window_size;
  1034. /*
  1035. * First let's figure out the total history required from
  1036. * BufPos backwards. For starters, we need all the bytes
  1037. * we're going to recompress. We get that by seeing the
  1038. * last time we output a block.
  1039. */
  1040. history_needed = BufPos - context->enc_bufpos_last_output_block;
  1041. /*
  1042. * Plus we will need window_size bytes before that (for matching
  1043. * into) unless we're looking within the first window_size
  1044. * bytes of the file.
  1045. */
  1046. if (context->enc_bufpos_last_output_block-context->enc_window_size < context->enc_window_size)
  1047. history_needed += context->enc_bufpos_last_output_block - context->enc_window_size;
  1048. else
  1049. history_needed += context->enc_window_size;
  1050. history_avail = (ulong)(&context->enc_MemWindow[BufPos] - &context->enc_RealMemWindow[0]);
  1051. if (history_needed <= history_avail)
  1052. {
  1053. earliest_can_start_at = context->enc_bufpos_last_output_block;
  1054. }
  1055. else
  1056. {
  1057. /*
  1058. * Not enough history available
  1059. */
  1060. return false;
  1061. }
  1062. start_at = earliest_can_start_at;
  1063. split_block(
  1064. context,
  1065. 0,
  1066. context->enc_literals,
  1067. context->enc_distances,
  1068. &split_at_literal,
  1069. NULL /* don't need # distances returned */
  1070. );
  1071. get_block_stats(
  1072. context,
  1073. 0,
  1074. 0,
  1075. split_at_literal
  1076. );
  1077. create_trees(context, false); /* don't generate codes */
  1078. fix_tree_cost_estimates(context);
  1079. #ifdef MULTIPLE_SEARCH_TREES
  1080. /*
  1081. * Now set all the tree root pointers to NULL
  1082. * (don't need to reset the left/right pointers).
  1083. */
  1084. memset(context->enc_tree_root, 0, NUM_SEARCH_TREES * sizeof(ulong));
  1085. #else
  1086. context->enc_single_tree_root = 0;
  1087. #endif
  1088. /*
  1089. * Clear item array and reset literal and distance
  1090. * counters
  1091. */
  1092. memset(context->enc_ItemType, 0, (MAX_LITERAL_ITEMS/8));
  1093. /*
  1094. * Reset encoder state
  1095. */
  1096. context->enc_last_matchpos_offset[0] = 1;
  1097. context->enc_last_matchpos_offset[1] = 1;
  1098. context->enc_last_matchpos_offset[2] = 1;
  1099. context->enc_repeated_offset_at_literal_zero[0] = 1;
  1100. context->enc_repeated_offset_at_literal_zero[1] = 1;
  1101. context->enc_repeated_offset_at_literal_zero[2] = 1;
  1102. context->enc_input_running_total = 0;
  1103. context->enc_literals = 0;
  1104. context->enc_distances = 0;
  1105. context->enc_need_to_recalc_stats = true;
  1106. context->enc_next_tree_create = split_at_literal;
  1107. *bufpos_ptr = start_at;
  1108. return true;
  1109. }