Home | History | Annotate | Download | only in lzma
      1 /*
      2  * LZMAEncoder
      3  *
      4  * Authors: Lasse Collin <lasse.collin (at) 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 
     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 
     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 
    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      */
    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      */
    173     abstract int getNextSymbol();
    174 
    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 
    192     public LZEncoder getLZEncoder() {
    193         return lz;
    194     }
    195 
    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 
    208     public int getUncompressedSize() {
    209         return uncompressedSize;
    210     }
    211 
    212     public void resetUncompressedSize() {
    213         uncompressedSize = 0;
    214     }
    215 
    216     /**
    217      * Compress for LZMA1.
    218      */
    219     public void encodeForLZMA1() throws IOException {
    220         if (!lz.isStarted() && !encodeInit())
    221             return;
    222 
    223         while (encodeSymbol()) {}
    224     }
    225 
    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      */
    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 
    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 
    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 
    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 
    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 
    385     Matches getMatches() {
    386         ++readAhead;
    387         Matches matches = lz.getMatches();
    388         assert lz.verifyMatches(matches);
    389         return matches;
    390     }
    391 
    392     void skip(int len) {
    393         readAhead += len;
    394         lz.skip(len);
    395     }
    396 
    397     int getAnyMatchPrice(State state, int posState) {
    398         return RangeEncoder.getBitPrice(isMatch[state.get()][posState], 1);
    399     }
    400 
    401     int getNormalMatchPrice(int anyMatchPrice, State state) {
    402         return anyMatchPrice
    403                + RangeEncoder.getBitPrice(isRep[state.get()], 0);
    404     }
    405 
    406     int getAnyRepPrice(int anyMatchPrice, State state) {
    407         return anyMatchPrice
    408                + RangeEncoder.getBitPrice(isRep[state.get()], 1);
    409     }
    410 
    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 
    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 
    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 
    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 
    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 
    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      */
    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 
    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 
    546         void reset() {
    547             for (int i = 0; i < subencoders.length; ++i)
    548                 subencoders[i].reset();
    549         }
    550 
    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 
    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 
    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 {
    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 
    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 
    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 
    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 
    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 
    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 
    713         int getPrice(int len, int posState) {
    714             return prices[posState][len - MATCH_LEN_MIN];
    715         }
    716 
    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 
    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