1 /*
2  * LZMAEncoder
3  *
4  * Authors: Lasse Collin <lasse.collin@tukaani.org>
5  *          Igor Pavlov <http://7-zip.org/>
6  *
7  * This file has been put into the public domain.
8  * You can do whatever you want with this file.
9  */
10 
11 package org.tukaani.xz.lzma;
12 
13 import java.io.IOException;
14 import org.tukaani.xz.ArrayCache;
15 import org.tukaani.xz.lz.LZEncoder;
16 import org.tukaani.xz.lz.Matches;
17 import org.tukaani.xz.rangecoder.RangeEncoder;
18 
19 public abstract class LZMAEncoder extends LZMACoder {
20     public static final int MODE_FAST = 1;
21     public static final int MODE_NORMAL = 2;
22 
23     /**
24      * LZMA2 chunk is considered full when its uncompressed size exceeds
25      * <code>LZMA2_UNCOMPRESSED_LIMIT</code>.
26      * <p>
27      * A compressed LZMA2 chunk can hold 2 MiB of uncompressed data.
28      * A single LZMA symbol may indicate up to MATCH_LEN_MAX bytes
29      * of data, so the LZMA2 chunk is considered full when there is
30      * less space than MATCH_LEN_MAX bytes.
31      */
32     private static final int LZMA2_UNCOMPRESSED_LIMIT
33             = (2 << 20) - MATCH_LEN_MAX;
34 
35     /**
36      * LZMA2 chunk is considered full when its compressed size exceeds
37      * <code>LZMA2_COMPRESSED_LIMIT</code>.
38      * <p>
39      * The maximum compressed size of a LZMA2 chunk is 64 KiB.
40      * A single LZMA symbol might use 20 bytes of space even though
41      * it usually takes just one byte or so. Two more bytes are needed
42      * for LZMA2 uncompressed chunks (see LZMA2OutputStream.writeChunk).
43      * Leave a little safety margin and use 26 bytes.
44      */
45     private static final int LZMA2_COMPRESSED_LIMIT = (64 << 10) - 26;
46 
47     private static final int DIST_PRICE_UPDATE_INTERVAL = FULL_DISTANCES;
48     private static final int ALIGN_PRICE_UPDATE_INTERVAL = ALIGN_SIZE;
49 
50     private final RangeEncoder rc;
51     final LZEncoder lz;
52     final LiteralEncoder literalEncoder;
53     final LengthEncoder matchLenEncoder;
54     final LengthEncoder repLenEncoder;
55     final int niceLen;
56 
57     private int distPriceCount = 0;
58     private int alignPriceCount = 0;
59 
60     private final int distSlotPricesSize;
61     private final int[][] distSlotPrices;
62     private final int[][] fullDistPrices
63             = new int[DIST_STATES][FULL_DISTANCES];
64     private final int[] alignPrices = new int[ALIGN_SIZE];
65 
66     int back = 0;
67     int readAhead = -1;
68     private int uncompressedSize = 0;
69 
getMemoryUsage(int mode, int dictSize, int extraSizeBefore, int mf)70     public static int getMemoryUsage(int mode, int dictSize,
71                                      int extraSizeBefore, int mf) {
72         int m = 80;
73 
74         switch (mode) {
75             case MODE_FAST:
76                 m += LZMAEncoderFast.getMemoryUsage(
77                         dictSize, extraSizeBefore, mf);
78                 break;
79 
80             case MODE_NORMAL:
81                 m += LZMAEncoderNormal.getMemoryUsage(
82                         dictSize, extraSizeBefore, mf);
83                 break;
84 
85             default:
86                 throw new IllegalArgumentException();
87         }
88 
89         return m;
90     }
91 
getInstance( RangeEncoder rc, int lc, int lp, int pb, int mode, int dictSize, int extraSizeBefore, int niceLen, int mf, int depthLimit, ArrayCache arrayCache)92     public static LZMAEncoder getInstance(
93                 RangeEncoder rc, int lc, int lp, int pb, int mode,
94                 int dictSize, int extraSizeBefore,
95                 int niceLen, int mf, int depthLimit,
96                 ArrayCache arrayCache) {
97         switch (mode) {
98             case MODE_FAST:
99                 return new LZMAEncoderFast(rc, lc, lp, pb,
100                                            dictSize, extraSizeBefore,
101                                            niceLen, mf, depthLimit,
102                                            arrayCache);
103 
104             case MODE_NORMAL:
105                 return new LZMAEncoderNormal(rc, lc, lp, pb,
106                                              dictSize, extraSizeBefore,
107                                              niceLen, mf, depthLimit,
108                                              arrayCache);
109         }
110 
111         throw new IllegalArgumentException();
112     }
113 
putArraysToCache(ArrayCache arrayCache)114     public void putArraysToCache(ArrayCache arrayCache) {
115         lz.putArraysToCache(arrayCache);
116     }
117 
118     /**
119      * Gets an integer [0, 63] matching the highest two bits of an integer.
120      * This is like bit scan reverse (BSR) on x86 except that this also
121      * cares about the second highest bit.
122      */
getDistSlot(int dist)123     public static int getDistSlot(int dist) {
124         if (dist <= DIST_MODEL_START && dist >= 0)
125             return dist;
126 
127         int n = dist;
128         int i = 31;
129 
130         if ((n & 0xFFFF0000) == 0) {
131             n <<= 16;
132             i = 15;
133         }
134 
135         if ((n & 0xFF000000) == 0) {
136             n <<= 8;
137             i -= 8;
138         }
139 
140         if ((n & 0xF0000000) == 0) {
141             n <<= 4;
142             i -= 4;
143         }
144 
145         if ((n & 0xC0000000) == 0) {
146             n <<= 2;
147             i -= 2;
148         }
149 
150         if ((n & 0x80000000) == 0)
151             --i;
152 
153         return (i << 1) + ((dist >>> (i - 1)) & 1);
154     }
155 
156     /**
157      * Gets the next LZMA symbol.
158      * <p>
159      * There are three types of symbols: literal (a single byte),
160      * repeated match, and normal match. The symbol is indicated
161      * by the return value and by the variable <code>back</code>.
162      * <p>
163      * Literal: <code>back == -1</code> and return value is <code>1</code>.
164      * The literal itself needs to be read from <code>lz</code> separately.
165      * <p>
166      * Repeated match: <code>back</code> is in the range [0, 3] and
167      * the return value is the length of the repeated match.
168      * <p>
169      * Normal match: <code>back - REPS<code> (<code>back - 4</code>)
170      * is the distance of the match and the return value is the length
171      * of the match.
172      */
getNextSymbol()173     abstract int getNextSymbol();
174 
LZMAEncoder(RangeEncoder rc, LZEncoder lz, int lc, int lp, int pb, int dictSize, int niceLen)175     LZMAEncoder(RangeEncoder rc, LZEncoder lz,
176                 int lc, int lp, int pb, int dictSize, int niceLen) {
177         super(pb);
178         this.rc = rc;
179         this.lz = lz;
180         this.niceLen = niceLen;
181 
182         literalEncoder = new LiteralEncoder(lc, lp);
183         matchLenEncoder = new LengthEncoder(pb, niceLen);
184         repLenEncoder = new LengthEncoder(pb, niceLen);
185 
186         distSlotPricesSize = getDistSlot(dictSize - 1) + 1;
187         distSlotPrices = new int[DIST_STATES][distSlotPricesSize];
188 
189         reset();
190     }
191 
getLZEncoder()192     public LZEncoder getLZEncoder() {
193         return lz;
194     }
195 
reset()196     public void reset() {
197         super.reset();
198         literalEncoder.reset();
199         matchLenEncoder.reset();
200         repLenEncoder.reset();
201         distPriceCount = 0;
202         alignPriceCount = 0;
203 
204         uncompressedSize += readAhead + 1;
205         readAhead = -1;
206     }
207 
getUncompressedSize()208     public int getUncompressedSize() {
209         return uncompressedSize;
210     }
211 
resetUncompressedSize()212     public void resetUncompressedSize() {
213         uncompressedSize = 0;
214     }
215 
216     /**
217      * Compress for LZMA1.
218      */
encodeForLZMA1()219     public void encodeForLZMA1() throws IOException {
220         if (!lz.isStarted() && !encodeInit())
221             return;
222 
223         while (encodeSymbol()) {}
224     }
225 
encodeLZMA1EndMarker()226     public void encodeLZMA1EndMarker() throws IOException {
227         // End of stream marker is encoded as a match with the maximum
228         // possible distance. The length is ignored by the decoder,
229         // but the minimum length has been used by the LZMA SDK.
230         //
231         // Distance is a 32-bit unsigned integer in LZMA.
232         // With Java's signed int, UINT32_MAX becomes -1.
233         int posState = (lz.getPos() - readAhead) & posMask;
234         rc.encodeBit(isMatch[state.get()], posState, 1);
235         rc.encodeBit(isRep, state.get(), 0);
236         encodeMatch(-1, MATCH_LEN_MIN, posState);
237     }
238 
239     /**
240      * Compresses for LZMA2.
241      *
242      * @return      true if the LZMA2 chunk became full, false otherwise
243      */
encodeForLZMA2()244     public boolean encodeForLZMA2() {
245         // LZMA2 uses RangeEncoderToBuffer so IOExceptions aren't possible.
246         try {
247             if (!lz.isStarted() && !encodeInit())
248                 return false;
249 
250             while (uncompressedSize <= LZMA2_UNCOMPRESSED_LIMIT
251                     && rc.getPendingSize() <= LZMA2_COMPRESSED_LIMIT)
252                 if (!encodeSymbol())
253                     return false;
254         } catch (IOException e) {
255             throw new Error();
256         }
257 
258         return true;
259     }
260 
encodeInit()261     private boolean encodeInit() throws IOException {
262         assert readAhead == -1;
263         if (!lz.hasEnoughData(0))
264             return false;
265 
266         // The first symbol must be a literal unless using
267         // a preset dictionary. This code isn't run if using
268         // a preset dictionary.
269         skip(1);
270         rc.encodeBit(isMatch[state.get()], 0, 0);
271         literalEncoder.encodeInit();
272 
273         --readAhead;
274         assert readAhead == -1;
275 
276         ++uncompressedSize;
277         assert uncompressedSize == 1;
278 
279         return true;
280     }
281 
encodeSymbol()282     private boolean encodeSymbol() throws IOException {
283         if (!lz.hasEnoughData(readAhead + 1))
284             return false;
285 
286         int len = getNextSymbol();
287 
288         assert readAhead >= 0;
289         int posState = (lz.getPos() - readAhead) & posMask;
290 
291         if (back == -1) {
292             // Literal i.e. eight-bit byte
293             assert len == 1;
294             rc.encodeBit(isMatch[state.get()], posState, 0);
295             literalEncoder.encode();
296         } else {
297             // Some type of match
298             rc.encodeBit(isMatch[state.get()], posState, 1);
299             if (back < REPS) {
300                 // Repeated match i.e. the same distance
301                 // has been used earlier.
302                 assert lz.getMatchLen(-readAhead, reps[back], len) == len;
303                 rc.encodeBit(isRep, state.get(), 1);
304                 encodeRepMatch(back, len, posState);
305             } else {
306                 // Normal match
307                 assert lz.getMatchLen(-readAhead, back - REPS, len) == len;
308                 rc.encodeBit(isRep, state.get(), 0);
309                 encodeMatch(back - REPS, len, posState);
310             }
311         }
312 
313         readAhead -= len;
314         uncompressedSize += len;
315 
316         return true;
317     }
318 
encodeMatch(int dist, int len, int posState)319     private void encodeMatch(int dist, int len, int posState)
320             throws IOException {
321         state.updateMatch();
322         matchLenEncoder.encode(len, posState);
323 
324         int distSlot = getDistSlot(dist);
325         rc.encodeBitTree(distSlots[getDistState(len)], distSlot);
326 
327         if (distSlot >= DIST_MODEL_START) {
328             int footerBits = (distSlot >>> 1) - 1;
329             int base = (2 | (distSlot & 1)) << footerBits;
330             int distReduced = dist - base;
331 
332             if (distSlot < DIST_MODEL_END) {
333                 rc.encodeReverseBitTree(
334                         distSpecial[distSlot - DIST_MODEL_START],
335                         distReduced);
336             } else {
337                 rc.encodeDirectBits(distReduced >>> ALIGN_BITS,
338                                     footerBits - ALIGN_BITS);
339                 rc.encodeReverseBitTree(distAlign, distReduced & ALIGN_MASK);
340                 --alignPriceCount;
341             }
342         }
343 
344         reps[3] = reps[2];
345         reps[2] = reps[1];
346         reps[1] = reps[0];
347         reps[0] = dist;
348 
349         --distPriceCount;
350     }
351 
encodeRepMatch(int rep, int len, int posState)352     private void encodeRepMatch(int rep, int len, int posState)
353             throws IOException {
354         if (rep == 0) {
355             rc.encodeBit(isRep0, state.get(), 0);
356             rc.encodeBit(isRep0Long[state.get()], posState, len == 1 ? 0 : 1);
357         } else {
358             int dist = reps[rep];
359             rc.encodeBit(isRep0, state.get(), 1);
360 
361             if (rep == 1) {
362                 rc.encodeBit(isRep1, state.get(), 0);
363             } else {
364                 rc.encodeBit(isRep1, state.get(), 1);
365                 rc.encodeBit(isRep2, state.get(), rep - 2);
366 
367                 if (rep == 3)
368                     reps[3] = reps[2];
369 
370                 reps[2] = reps[1];
371             }
372 
373             reps[1] = reps[0];
374             reps[0] = dist;
375         }
376 
377         if (len == 1) {
378             state.updateShortRep();
379         } else {
380             repLenEncoder.encode(len, posState);
381             state.updateLongRep();
382         }
383     }
384 
getMatches()385     Matches getMatches() {
386         ++readAhead;
387         Matches matches = lz.getMatches();
388         assert lz.verifyMatches(matches);
389         return matches;
390     }
391 
skip(int len)392     void skip(int len) {
393         readAhead += len;
394         lz.skip(len);
395     }
396 
getAnyMatchPrice(State state, int posState)397     int getAnyMatchPrice(State state, int posState) {
398         return RangeEncoder.getBitPrice(isMatch[state.get()][posState], 1);
399     }
400 
getNormalMatchPrice(int anyMatchPrice, State state)401     int getNormalMatchPrice(int anyMatchPrice, State state) {
402         return anyMatchPrice
403                + RangeEncoder.getBitPrice(isRep[state.get()], 0);
404     }
405 
getAnyRepPrice(int anyMatchPrice, State state)406     int getAnyRepPrice(int anyMatchPrice, State state) {
407         return anyMatchPrice
408                + RangeEncoder.getBitPrice(isRep[state.get()], 1);
409     }
410 
getShortRepPrice(int anyRepPrice, State state, int posState)411     int getShortRepPrice(int anyRepPrice, State state, int posState) {
412         return anyRepPrice
413                + RangeEncoder.getBitPrice(isRep0[state.get()], 0)
414                + RangeEncoder.getBitPrice(isRep0Long[state.get()][posState],
415                                           0);
416     }
417 
getLongRepPrice(int anyRepPrice, int rep, State state, int posState)418     int getLongRepPrice(int anyRepPrice, int rep, State state, int posState) {
419         int price = anyRepPrice;
420 
421         if (rep == 0) {
422             price += RangeEncoder.getBitPrice(isRep0[state.get()], 0)
423                      + RangeEncoder.getBitPrice(
424                        isRep0Long[state.get()][posState], 1);
425         } else {
426             price += RangeEncoder.getBitPrice(isRep0[state.get()], 1);
427 
428             if (rep == 1)
429                 price += RangeEncoder.getBitPrice(isRep1[state.get()], 0);
430             else
431                 price += RangeEncoder.getBitPrice(isRep1[state.get()], 1)
432                          + RangeEncoder.getBitPrice(isRep2[state.get()],
433                                                     rep - 2);
434         }
435 
436         return price;
437     }
438 
getLongRepAndLenPrice(int rep, int len, State state, int posState)439     int getLongRepAndLenPrice(int rep, int len, State state, int posState) {
440         int anyMatchPrice = getAnyMatchPrice(state, posState);
441         int anyRepPrice = getAnyRepPrice(anyMatchPrice, state);
442         int longRepPrice = getLongRepPrice(anyRepPrice, rep, state, posState);
443         return longRepPrice + repLenEncoder.getPrice(len, posState);
444     }
445 
getMatchAndLenPrice(int normalMatchPrice, int dist, int len, int posState)446     int getMatchAndLenPrice(int normalMatchPrice,
447                             int dist, int len, int posState) {
448         int price = normalMatchPrice
449                     + matchLenEncoder.getPrice(len, posState);
450         int distState = getDistState(len);
451 
452         if (dist < FULL_DISTANCES) {
453             price += fullDistPrices[distState][dist];
454         } else {
455             // Note that distSlotPrices includes also
456             // the price of direct bits.
457             int distSlot = getDistSlot(dist);
458             price += distSlotPrices[distState][distSlot]
459                      + alignPrices[dist & ALIGN_MASK];
460         }
461 
462         return price;
463     }
464 
updateDistPrices()465     private void updateDistPrices() {
466         distPriceCount = DIST_PRICE_UPDATE_INTERVAL;
467 
468         for (int distState = 0; distState < DIST_STATES; ++distState) {
469             for (int distSlot = 0; distSlot < distSlotPricesSize; ++distSlot)
470                 distSlotPrices[distState][distSlot]
471                         = RangeEncoder.getBitTreePrice(
472                           distSlots[distState], distSlot);
473 
474             for (int distSlot = DIST_MODEL_END; distSlot < distSlotPricesSize;
475                     ++distSlot) {
476                 int count = (distSlot >>> 1) - 1 - ALIGN_BITS;
477                 distSlotPrices[distState][distSlot]
478                         += RangeEncoder.getDirectBitsPrice(count);
479             }
480 
481             for (int dist = 0; dist < DIST_MODEL_START; ++dist)
482                 fullDistPrices[distState][dist]
483                         = distSlotPrices[distState][dist];
484         }
485 
486         int dist = DIST_MODEL_START;
487         for (int distSlot = DIST_MODEL_START; distSlot < DIST_MODEL_END;
488                 ++distSlot) {
489             int footerBits = (distSlot >>> 1) - 1;
490             int base = (2 | (distSlot & 1)) << footerBits;
491 
492             int limit = distSpecial[distSlot - DIST_MODEL_START].length;
493             for (int i = 0; i < limit; ++i) {
494                 int distReduced = dist - base;
495                 int price = RangeEncoder.getReverseBitTreePrice(
496                         distSpecial[distSlot - DIST_MODEL_START],
497                         distReduced);
498 
499                 for (int distState = 0; distState < DIST_STATES; ++distState)
500                     fullDistPrices[distState][dist]
501                             = distSlotPrices[distState][distSlot] + price;
502 
503                 ++dist;
504             }
505         }
506 
507         assert dist == FULL_DISTANCES;
508     }
509 
updateAlignPrices()510     private void updateAlignPrices() {
511         alignPriceCount = ALIGN_PRICE_UPDATE_INTERVAL;
512 
513         for (int i = 0; i < ALIGN_SIZE; ++i)
514             alignPrices[i] = RangeEncoder.getReverseBitTreePrice(distAlign,
515                                                                  i);
516     }
517 
518     /**
519      * Updates the lookup tables used for calculating match distance
520      * and length prices. The updating is skipped for performance reasons
521      * if the tables haven't changed much since the previous update.
522      */
updatePrices()523     void updatePrices() {
524         if (distPriceCount <= 0)
525             updateDistPrices();
526 
527         if (alignPriceCount <= 0)
528             updateAlignPrices();
529 
530         matchLenEncoder.updatePrices();
531         repLenEncoder.updatePrices();
532     }
533 
534 
535     class LiteralEncoder extends LiteralCoder {
536         private final LiteralSubencoder[] subencoders;
537 
LiteralEncoder(int lc, int lp)538         LiteralEncoder(int lc, int lp) {
539             super(lc, lp);
540 
541             subencoders = new LiteralSubencoder[1 << (lc + lp)];
542             for (int i = 0; i < subencoders.length; ++i)
543                 subencoders[i] = new LiteralSubencoder();
544         }
545 
reset()546         void reset() {
547             for (int i = 0; i < subencoders.length; ++i)
548                 subencoders[i].reset();
549         }
550 
encodeInit()551         void encodeInit() throws IOException {
552             // When encoding the first byte of the stream, there is
553             // no previous byte in the dictionary so the encode function
554             // wouldn't work.
555             assert readAhead >= 0;
556             subencoders[0].encode();
557         }
558 
encode()559         void encode() throws IOException {
560             assert readAhead >= 0;
561             int i = getSubcoderIndex(lz.getByte(1 + readAhead),
562                                      lz.getPos() - readAhead);
563             subencoders[i].encode();
564         }
565 
getPrice(int curByte, int matchByte, int prevByte, int pos, State state)566         int getPrice(int curByte, int matchByte,
567                      int prevByte, int pos, State state) {
568             int price = RangeEncoder.getBitPrice(
569                     isMatch[state.get()][pos & posMask], 0);
570 
571             int i = getSubcoderIndex(prevByte, pos);
572             price += state.isLiteral()
573                    ? subencoders[i].getNormalPrice(curByte)
574                    : subencoders[i].getMatchedPrice(curByte, matchByte);
575 
576             return price;
577         }
578 
579         private class LiteralSubencoder extends LiteralSubcoder {
encode()580             void encode() throws IOException {
581                 int symbol = lz.getByte(readAhead) | 0x100;
582 
583                 if (state.isLiteral()) {
584                     int subencoderIndex;
585                     int bit;
586 
587                     do {
588                         subencoderIndex = symbol >>> 8;
589                         bit = (symbol >>> 7) & 1;
590                         rc.encodeBit(probs, subencoderIndex, bit);
591                         symbol <<= 1;
592                     } while (symbol < 0x10000);
593 
594                 } else {
595                     int matchByte = lz.getByte(reps[0] + 1 + readAhead);
596                     int offset = 0x100;
597                     int subencoderIndex;
598                     int matchBit;
599                     int bit;
600 
601                     do {
602                         matchByte <<= 1;
603                         matchBit = matchByte & offset;
604                         subencoderIndex = offset + matchBit + (symbol >>> 8);
605                         bit = (symbol >>> 7) & 1;
606                         rc.encodeBit(probs, subencoderIndex, bit);
607                         symbol <<= 1;
608                         offset &= ~(matchByte ^ symbol);
609                     } while (symbol < 0x10000);
610                 }
611 
612                 state.updateLiteral();
613             }
614 
getNormalPrice(int symbol)615             int getNormalPrice(int symbol) {
616                 int price = 0;
617                 int subencoderIndex;
618                 int bit;
619 
620                 symbol |= 0x100;
621 
622                 do {
623                     subencoderIndex = symbol >>> 8;
624                     bit = (symbol >>> 7) & 1;
625                     price += RangeEncoder.getBitPrice(probs[subencoderIndex],
626                                                       bit);
627                     symbol <<= 1;
628                 } while (symbol < (0x100 << 8));
629 
630                 return price;
631             }
632 
getMatchedPrice(int symbol, int matchByte)633             int getMatchedPrice(int symbol, int matchByte) {
634                 int price = 0;
635                 int offset = 0x100;
636                 int subencoderIndex;
637                 int matchBit;
638                 int bit;
639 
640                 symbol |= 0x100;
641 
642                 do {
643                     matchByte <<= 1;
644                     matchBit = matchByte & offset;
645                     subencoderIndex = offset + matchBit + (symbol >>> 8);
646                     bit = (symbol >>> 7) & 1;
647                     price += RangeEncoder.getBitPrice(probs[subencoderIndex],
648                                                       bit);
649                     symbol <<= 1;
650                     offset &= ~(matchByte ^ symbol);
651                 } while (symbol < (0x100 << 8));
652 
653                 return price;
654             }
655         }
656     }
657 
658 
659     class LengthEncoder extends LengthCoder {
660         /**
661          * The prices are updated after at least
662          * <code>PRICE_UPDATE_INTERVAL</code> many lengths
663          * have been encoded with the same posState.
664          */
665         private static final int PRICE_UPDATE_INTERVAL = 32; // FIXME?
666 
667         private final int[] counters;
668         private final int[][] prices;
669 
LengthEncoder(int pb, int niceLen)670         LengthEncoder(int pb, int niceLen) {
671             int posStates = 1 << pb;
672             counters = new int[posStates];
673 
674             // Always allocate at least LOW_SYMBOLS + MID_SYMBOLS because
675             // it makes updatePrices slightly simpler. The prices aren't
676             // usually needed anyway if niceLen < 18.
677             int lenSymbols = Math.max(niceLen - MATCH_LEN_MIN + 1,
678                                       LOW_SYMBOLS + MID_SYMBOLS);
679             prices = new int[posStates][lenSymbols];
680         }
681 
reset()682         void reset() {
683             super.reset();
684 
685             // Reset counters to zero to force price update before
686             // the prices are needed.
687             for (int i = 0; i < counters.length; ++i)
688                 counters[i] = 0;
689         }
690 
encode(int len, int posState)691         void encode(int len, int posState) throws IOException {
692             len -= MATCH_LEN_MIN;
693 
694             if (len < LOW_SYMBOLS) {
695                 rc.encodeBit(choice, 0, 0);
696                 rc.encodeBitTree(low[posState], len);
697             } else {
698                 rc.encodeBit(choice, 0, 1);
699                 len -= LOW_SYMBOLS;
700 
701                 if (len < MID_SYMBOLS) {
702                     rc.encodeBit(choice, 1, 0);
703                     rc.encodeBitTree(mid[posState], len);
704                 } else {
705                     rc.encodeBit(choice, 1, 1);
706                     rc.encodeBitTree(high, len - MID_SYMBOLS);
707                 }
708             }
709 
710             --counters[posState];
711         }
712 
getPrice(int len, int posState)713         int getPrice(int len, int posState) {
714             return prices[posState][len - MATCH_LEN_MIN];
715         }
716 
updatePrices()717         void updatePrices() {
718             for (int posState = 0; posState < counters.length; ++posState) {
719                 if (counters[posState] <= 0) {
720                     counters[posState] = PRICE_UPDATE_INTERVAL;
721                     updatePrices(posState);
722                 }
723             }
724         }
725 
updatePrices(int posState)726         private void updatePrices(int posState) {
727             int choice0Price = RangeEncoder.getBitPrice(choice[0], 0);
728 
729             int i = 0;
730             for (; i < LOW_SYMBOLS; ++i)
731                 prices[posState][i] = choice0Price
732                         + RangeEncoder.getBitTreePrice(low[posState], i);
733 
734             choice0Price = RangeEncoder.getBitPrice(choice[0], 1);
735             int choice1Price = RangeEncoder.getBitPrice(choice[1], 0);
736 
737             for (; i < LOW_SYMBOLS + MID_SYMBOLS; ++i)
738                 prices[posState][i] = choice0Price + choice1Price
739                          + RangeEncoder.getBitTreePrice(mid[posState],
740                                                         i - LOW_SYMBOLS);
741 
742             choice1Price = RangeEncoder.getBitPrice(choice[1], 1);
743 
744             for (; i < prices[posState].length; ++i)
745                 prices[posState][i] = choice0Price + choice1Price
746                          + RangeEncoder.getBitTreePrice(high, i - LOW_SYMBOLS
747                                                               - MID_SYMBOLS);
748         }
749     }
750 }
751