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.brotli.dec; 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; 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 setData(ByteBuffer data, int[] sizeBits)42 public static void setData(ByteBuffer data, int[] sizeBits) { 43 if (!data.isDirect() || !data.isReadOnly()) { 44 throw new BrotliRuntimeException("data must be a direct read-only byte buffer"); 45 } 46 // TODO: is that so? 47 if (sizeBits.length > MAX_DICTIONARY_WORD_LENGTH) { 48 throw new BrotliRuntimeException( 49 "sizeBits length must be at most " + MAX_DICTIONARY_WORD_LENGTH); 50 } 51 for (int i = 0; i < MIN_DICTIONARY_WORD_LENGTH; ++i) { 52 if (sizeBits[i] != 0) { 53 throw new BrotliRuntimeException("first " + MIN_DICTIONARY_WORD_LENGTH + " must be 0"); 54 } 55 } 56 int[] dictionaryOffsets = Dictionary.offsets; 57 int[] dictionarySizeBits = Dictionary.sizeBits; 58 System.arraycopy(sizeBits, 0, dictionarySizeBits, 0, sizeBits.length); 59 int pos = 0; 60 int limit = data.capacity(); 61 for (int i = 0; i < sizeBits.length; ++i) { 62 dictionaryOffsets[i] = pos; 63 int bits = dictionarySizeBits[i]; 64 if (bits != 0) { 65 if (bits >= 31) { 66 throw new BrotliRuntimeException("sizeBits values must be less than 31"); 67 } 68 pos += i << bits; 69 if (pos <= 0 || pos > limit) { 70 throw new BrotliRuntimeException("sizeBits is inconsistent: overflow"); 71 } 72 } 73 } 74 for (int i = sizeBits.length; i < 32; ++i) { 75 dictionaryOffsets[i] = pos; 76 } 77 if (pos != limit) { 78 throw new BrotliRuntimeException("sizeBits is inconsistent: underflow"); 79 } 80 Dictionary.data = data; 81 } 82 getData()83 public static ByteBuffer getData() { 84 if (data != null) { 85 return data; 86 } 87 if (!DataLoader.OK) { 88 throw new BrotliRuntimeException("brotli dictionary is not set"); 89 } 90 /* Might have been set when {@link DictionaryData} was loaded.*/ 91 return data; 92 } 93 } 94