1 /* Copyright 2017 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.wrapper.common; 8 9 import java.io.IOException; 10 import java.io.InputStream; 11 import java.nio.ByteBuffer; 12 import java.util.Arrays; 13 14 /** 15 * JNI wrapper for brotli common. 16 */ 17 public class BrotliCommon { 18 public static final int RFC_DICTIONARY_SIZE = 122784; 19 20 /* 96cecd2ee7a666d5aa3627d74735b32a */ 21 private static final byte[] RFC_DICTIONARY_MD5 = { 22 -106, -50, -51, 46, -25, -90, 102, -43, -86, 54, 39, -41, 71, 53, -77, 42 23 }; 24 25 /* 72b41051cb61a9281ba3c4414c289da50d9a7640 */ 26 private static final byte[] RFC_DICTIONARY_SHA_1 = { 27 114, -76, 16, 81, -53, 97, -87, 40, 27, -93, -60, 65, 76, 40, -99, -91, 13, -102, 118, 64 28 }; 29 30 /* 20e42eb1b511c21806d4d227d07e5dd06877d8ce7b3a817f378f313653f35c70 */ 31 private static final byte[] RFC_DICTIONARY_SHA_256 = { 32 32, -28, 46, -79, -75, 17, -62, 24, 6, -44, -46, 39, -48, 126, 93, -48, 33 104, 119, -40, -50, 123, 58, -127, 127, 55, -113, 49, 54, 83, -13, 92, 112 34 }; 35 36 private static boolean isDictionaryDataSet; 37 private static final Object mutex = new Object(); 38 39 /** 40 * Checks if the given checksum matches MD5 checksum of the RFC dictionary. 41 */ checkDictionaryDataMd5(byte[] digest)42 public static boolean checkDictionaryDataMd5(byte[] digest) { 43 return Arrays.equals(RFC_DICTIONARY_MD5, digest); 44 } 45 46 /** 47 * Checks if the given checksum matches SHA-1 checksum of the RFC dictionary. 48 */ checkDictionaryDataSha1(byte[] digest)49 public static boolean checkDictionaryDataSha1(byte[] digest) { 50 return Arrays.equals(RFC_DICTIONARY_SHA_1, digest); 51 } 52 53 /** 54 * Checks if the given checksum matches SHA-256 checksum of the RFC dictionary. 55 */ checkDictionaryDataSha256(byte[] digest)56 public static boolean checkDictionaryDataSha256(byte[] digest) { 57 return Arrays.equals(RFC_DICTIONARY_SHA_256, digest); 58 } 59 60 /** 61 * Copy bytes to a new direct ByteBuffer. 62 * 63 * Direct byte buffers are used to supply native code with large data chunks. 64 */ makeNative(byte[] data)65 public static ByteBuffer makeNative(byte[] data) { 66 ByteBuffer result = ByteBuffer.allocateDirect(data.length); 67 result.put(data); 68 return result; 69 } 70 71 /** 72 * Copies data and sets it to be brotli dictionary. 73 */ setDictionaryData(byte[] data)74 public static void setDictionaryData(byte[] data) { 75 if (data.length != RFC_DICTIONARY_SIZE) { 76 throw new IllegalArgumentException("invalid dictionary size"); 77 } 78 synchronized (mutex) { 79 if (isDictionaryDataSet) { 80 return; 81 } 82 setDictionaryData(makeNative(data)); 83 } 84 } 85 86 /** 87 * Reads data and sets it to be brotli dictionary. 88 */ setDictionaryData(InputStream src)89 public static void setDictionaryData(InputStream src) throws IOException { 90 synchronized (mutex) { 91 if (isDictionaryDataSet) { 92 return; 93 } 94 ByteBuffer copy = ByteBuffer.allocateDirect(RFC_DICTIONARY_SIZE); 95 byte[] buffer = new byte[4096]; 96 int readBytes; 97 while ((readBytes = src.read(buffer)) != -1) { 98 if (copy.remaining() < readBytes) { 99 throw new IllegalArgumentException("invalid dictionary size"); 100 } 101 copy.put(buffer, 0, readBytes); 102 } 103 if (copy.remaining() != 0) { 104 throw new IllegalArgumentException("invalid dictionary size " + copy.remaining()); 105 } 106 setDictionaryData(copy); 107 } 108 } 109 110 /** 111 * Sets data to be brotli dictionary. 112 */ setDictionaryData(ByteBuffer data)113 public static void setDictionaryData(ByteBuffer data) { 114 if (!data.isDirect()) { 115 throw new IllegalArgumentException("direct byte buffer is expected"); 116 } 117 if (data.capacity() != RFC_DICTIONARY_SIZE) { 118 throw new IllegalArgumentException("invalid dictionary size"); 119 } 120 synchronized (mutex) { 121 if (isDictionaryDataSet) { 122 return; 123 } 124 if (!CommonJNI.nativeSetDictionaryData(data)) { 125 throw new RuntimeException("setting dictionary failed"); 126 } 127 isDictionaryDataSet = true; 128 } 129 } 130 } 131