View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  /*
22   * Licensed under the Apache License, Version 2.0 (the "License");
23   * you may not use this file except in compliance with the License.
24   * You may obtain a copy of the License at
25   *
26   *     http://www.apache.org/licenses/LICENSE-2.0
27   *
28   * Unless required by applicable law or agreed to in writing, software
29   * distributed under the License is distributed on an "AS IS" BASIS,
30   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31   * See the License for the specific language governing permissions and
32   * limitations under the License.
33   */
34  package org.waarp.compress.zstdsafe;
35  
36  import static org.waarp.compress.zstdsafe.Constants.*;
37  import static org.waarp.compress.zstdsafe.UnsafeUtil.*;
38  import static org.waarp.compress.zstdsafe.Util.*;
39  
40  /**
41   * Bit streams are encoded as a byte-aligned little-endian stream. Thus, bits are laid out
42   * in the following manner, and the stream is read from right to left.
43   * <p>
44   * <p>
45   * ... [16 17 18 19 20 21 22 23] [8 9 10 11 12 13 14 15] [0 1 2 3 4 5 6 7]
46   */
47  class BitInputStream {
48    private BitInputStream() {
49    }
50  
51    public static boolean isEndOfStream(final int startAddress,
52                                        final int currentAddress,
53                                        final int bitsConsumed) {
54      return startAddress == currentAddress && bitsConsumed == Long.SIZE;
55    }
56  
57    static long readTail(final byte[] inputBase, final int inputAddress,
58                         final int inputSize) {
59      long bits = inputBase[inputAddress] & 0xFF;
60  
61      switch (inputSize) {
62        case 7:
63          bits |= (inputBase[inputAddress + 6] & 0xFFL) << 48;
64        case 6:
65          bits |= (inputBase[inputAddress + 5] & 0xFFL) << 40;
66        case 5:
67          bits |= (inputBase[inputAddress + 4] & 0xFFL) << 32;
68        case 4:
69          bits |= (inputBase[inputAddress + 3] & 0xFFL) << 24;
70        case 3:
71          bits |= (inputBase[inputAddress + 2] & 0xFFL) << 16;
72        case 2:
73          bits |= (inputBase[inputAddress + 1] & 0xFFL) << 8;
74      }
75  
76      return bits;
77    }
78  
79    /**
80     * @return numberOfBits in the low order bits of a long
81     */
82    public static long peekBits(final int bitsConsumed, final long bitContainer,
83                                final int numberOfBits) {
84      return (((bitContainer << bitsConsumed) >>> 1) >>> (63 - numberOfBits));
85    }
86  
87    /**
88     * numberOfBits must be > 0
89     *
90     * @return numberOfBits in the low order bits of a long
91     */
92    public static long peekBitsFast(final int bitsConsumed,
93                                    final long bitContainer,
94                                    final int numberOfBits) {
95      return ((bitContainer << bitsConsumed) >>> (64 - numberOfBits));
96    }
97  
98    static class Initializer {
99      private final byte[] inputBase;
100     private final int startAddress;
101     private final int endAddress;
102     private long bits;
103     private int currentAddress;
104     private int bitsConsumed;
105 
106     public Initializer(final byte[] inputBase, final int startAddress,
107                        final int endAddress) {
108       this.inputBase = inputBase;
109       this.startAddress = startAddress;
110       this.endAddress = endAddress;
111     }
112 
113     public long getBits() {
114       return bits;
115     }
116 
117     public int getCurrentAddress() {
118       return currentAddress;
119     }
120 
121     public int getBitsConsumed() {
122       return bitsConsumed;
123     }
124 
125     public void initialize() {
126       verify(endAddress - startAddress >= 1, startAddress,
127              "Bitstream is empty");
128 
129       final int lastByte = inputBase[endAddress - 1] & 0xFF;
130       verify(lastByte != 0, endAddress, "Bitstream end mark not present");
131 
132       bitsConsumed = SIZE_OF_LONG - highestBit(lastByte);
133 
134       final int inputSize = endAddress - startAddress;
135       if (inputSize >= SIZE_OF_LONG) {  /* normal case */
136         currentAddress = endAddress - SIZE_OF_LONG;
137         bits = getLong(inputBase, currentAddress);
138       } else {
139         currentAddress = startAddress;
140         bits = readTail(inputBase, startAddress, inputSize);
141 
142         bitsConsumed += (SIZE_OF_LONG - inputSize) * 8;
143       }
144     }
145   }
146 
147   static final class Loader {
148     private final byte[] inputBase;
149     private final int startAddress;
150     private long bits;
151     private int currentAddress;
152     private int bitsConsumed;
153     private boolean overflow;
154 
155     public Loader(final byte[] inputBase, final int startAddress,
156                   final int currentAddress, final long bits,
157                   final int bitsConsumed) {
158       this.inputBase = inputBase;
159       this.startAddress = startAddress;
160       this.bits = bits;
161       this.currentAddress = currentAddress;
162       this.bitsConsumed = bitsConsumed;
163     }
164 
165     public long getBits() {
166       return bits;
167     }
168 
169     public int getCurrentAddress() {
170       return currentAddress;
171     }
172 
173     public int getBitsConsumed() {
174       return bitsConsumed;
175     }
176 
177     public boolean isOverflow() {
178       return overflow;
179     }
180 
181     public boolean load() {
182       if (bitsConsumed > 64) {
183         overflow = true;
184         return true;
185       } else if (currentAddress == startAddress) {
186         return true;
187       }
188 
189       int bytes = bitsConsumed >>> 3; // divide by 8
190       if (currentAddress >= startAddress + SIZE_OF_LONG) {
191         if (bytes > 0) {
192           currentAddress -= bytes;
193           bits = getLong(inputBase, currentAddress);
194         }
195         bitsConsumed &= 0x7;
196       } else if (currentAddress - bytes < startAddress) {
197         bytes = currentAddress - startAddress;
198         currentAddress = startAddress;
199         bitsConsumed -= bytes * SIZE_OF_LONG;
200         bits = getLong(inputBase, startAddress);
201         return true;
202       } else {
203         currentAddress -= bytes;
204         bitsConsumed -= bytes * SIZE_OF_LONG;
205         bits = getLong(inputBase, currentAddress);
206       }
207 
208       return false;
209     }
210   }
211 }