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        System.arraycopy(newSizeBits, 0, dictionarySizeBits, 0, newSizeBits.length);
64      int pos = 0;
65      for (int i = 0; i < newSizeBits.length; ++i) {
66        dictionaryOffsets[i] = pos;
67        final int bits = dictionarySizeBits[i];
68        if (bits != 0) {
69          pos += i << (bits & 31);
70          if (DICTIONARY_DEBUG != 0) {
71            if (bits >= 31) {
72              throw new BrotliRuntimeException("newSizeBits values must be less than 31");
73            }
74            if (pos <= 0 || pos > newData.capacity()) {
75              throw new BrotliRuntimeException("newSizeBits is inconsistent: overflow");
76            }
77          }
78        }
79      }
80      for (int i = newSizeBits.length; i < 32; ++i) {
81        dictionaryOffsets[i] = pos;
82      }
83      if (DICTIONARY_DEBUG != 0) {
84        if (pos != newData.capacity()) {
85          throw new BrotliRuntimeException("newSizeBits is inconsistent: underflow");
86        }
87      }
88      Dictionary.data = newData;
89    }
90  
91    public static ByteBuffer getData() {
92      if (data.capacity() != 0) {
93        return data;
94      }
95      if (!DataLoader.OK) {
96        throw new BrotliRuntimeException("brotli dictionary is not set");
97      }
98      /* Might have been set when {@link DictionaryData} was loaded.*/
99      return data;
100   }
101 }