View Javadoc
1   /* Copyright 2015 Google Inc. All Rights Reserved.
2   
3      Distributed under MIT license.
4      See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5   */
6   
7   package org.htmlunit.util.brotli;
8   
9   import java.nio.ByteBuffer;
10  
11  /**
12   * Collection of static dictionary words.
13   *
14   * <p>Dictionary content is loaded from binary resource when {@link #getData()} is executed for the
15   * first time. Consequently, it saves memory and CPU in case dictionary is not required.
16   *
17   * <p>One possible drawback is that multiple threads that need dictionary data may be blocked (only
18   * once in each classworld). To avoid this, it is enough to call {@link #getData()} proactively.
19   */
20  public final class Dictionary {
21    static final int MIN_DICTIONARY_WORD_LENGTH = 4;
22    static final int MAX_DICTIONARY_WORD_LENGTH = 31;
23  
24    private static ByteBuffer data = ByteBuffer.allocateDirect(0);
25    static final int[] offsets = new int[32];
26    static final int[] sizeBits = new int[32];
27  
28    private static class DataLoader {
29      static final boolean OK;
30  
31      static {
32        boolean ok = true;
33        try {
34          Class.forName(Dictionary.class.getPackage().getName() + ".DictionaryData");
35        } catch (Throwable ex) {
36          ok = false;
37        }
38        OK = ok;
39      }
40    }
41  
42    private static final int DICTIONARY_DEBUG = Utils.isDebugMode();
43  
44    public static void setData(ByteBuffer newData, int[] newSizeBits) {
45      if (DICTIONARY_DEBUG != 0) {
46        if ((Utils.isDirect(newData) == 0) || (Utils.isReadOnly(newData) == 0)) {
47          throw new BrotliRuntimeException("newData must be a direct read-only byte buffer");
48        }
49        // TODO: is that so?
50        if (newSizeBits.length > MAX_DICTIONARY_WORD_LENGTH) {
51          throw new BrotliRuntimeException(
52              "sizeBits length must be at most " + String.valueOf(MAX_DICTIONARY_WORD_LENGTH));
53        }
54        for (int i = 0; i < MIN_DICTIONARY_WORD_LENGTH; ++i) {
55          if (newSizeBits[i] != 0) {
56            throw new BrotliRuntimeException(
57                "first " + String.valueOf(MIN_DICTIONARY_WORD_LENGTH) + " must be 0");
58          }
59        }
60      }
61      final int[] dictionaryOffsets = Dictionary.offsets;
62      final int[] dictionarySizeBits = Dictionary.sizeBits;
63      for (int i = 0; i < newSizeBits.length; ++i) {
64        dictionarySizeBits[i] = newSizeBits[i];
65      }
66      int pos = 0;
67      for (int i = 0; i < newSizeBits.length; ++i) {
68        dictionaryOffsets[i] = pos;
69        final int bits = dictionarySizeBits[i];
70        if (bits != 0) {
71          pos += i << (bits & 31);
72          if (DICTIONARY_DEBUG != 0) {
73            if (bits >= 31) {
74              throw new BrotliRuntimeException("newSizeBits values must be less than 31");
75            }
76            if (pos <= 0 || pos > newData.capacity()) {
77              throw new BrotliRuntimeException("newSizeBits is inconsistent: overflow");
78            }
79          }
80        }
81      }
82      for (int i = newSizeBits.length; i < 32; ++i) {
83        dictionaryOffsets[i] = pos;
84      }
85      if (DICTIONARY_DEBUG != 0) {
86        if (pos != newData.capacity()) {
87          throw new BrotliRuntimeException("newSizeBits is inconsistent: underflow");
88        }
89      }
90      Dictionary.data = newData;
91    }
92  
93    public static ByteBuffer getData() {
94      if (data.capacity() != 0) {
95        return data;
96      }
97      if (!DataLoader.OK) {
98        throw new BrotliRuntimeException("brotli dictionary is not set");
99      }
100     /* Might have been set when {@link DictionaryData} was loaded.*/
101     return data;
102   }
103 }