001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.io.compress.brotli; 019 020import com.aayushatharva.brotli4j.Brotli4jLoader; 021import com.aayushatharva.brotli4j.decoder.Decoder; 022import com.aayushatharva.brotli4j.decoder.DirectDecompress; 023import java.io.IOException; 024import java.nio.ByteBuffer; 025import org.apache.hadoop.hbase.io.compress.CompressionUtil; 026import org.apache.hadoop.io.compress.Decompressor; 027import org.apache.yetus.audience.InterfaceAudience; 028 029/** 030 * Hadoop decompressor glue for Brotli4j 031 */ 032@InterfaceAudience.Private 033public class BrotliDecompressor implements Decompressor { 034 035 protected ByteBuffer inBuf, outBuf; 036 protected int inLen; 037 protected boolean finished; 038 039 static { 040 Brotli4jLoader.ensureAvailability(); 041 } 042 043 BrotliDecompressor(int bufferSize) { 044 this.inBuf = ByteBuffer.allocate(bufferSize); 045 this.outBuf = ByteBuffer.allocate(bufferSize); 046 this.outBuf.position(bufferSize); 047 } 048 049 @Override 050 public int decompress(byte[] b, int off, int len) throws IOException { 051 if (outBuf.hasRemaining()) { 052 int remaining = outBuf.remaining(), n = Math.min(remaining, len); 053 outBuf.get(b, off, n); 054 return n; 055 } 056 if (inBuf.position() > 0) { 057 inBuf.flip(); 058 int remaining = inBuf.remaining(); 059 inLen -= remaining; 060 outBuf.rewind(); 061 outBuf.limit(outBuf.capacity()); 062 // TODO: More inefficient than it could be, but it doesn't impact decompression speed 063 // terribly and the brotli4j API alternatives do not seem to work correctly. 064 // Maybe something more clever can be done as a future improvement. 065 final byte[] inb = new byte[remaining]; 066 inBuf.get(inb); 067 DirectDecompress result = Decoder.decompress(inb); 068 outBuf.put(result.getDecompressedDataByteBuf().nioBuffer()); 069 final int written = outBuf.position(); 070 inBuf.rewind(); 071 inBuf.limit(inBuf.capacity()); 072 outBuf.flip(); 073 int n = Math.min(written, len); 074 outBuf.get(b, off, n); 075 return n; 076 } 077 finished = true; 078 return 0; 079 } 080 081 @Override 082 public void end() { 083 } 084 085 @Override 086 public boolean finished() { 087 return finished; 088 } 089 090 @Override 091 public int getRemaining() { 092 return inLen; 093 } 094 095 @Override 096 public boolean needsDictionary() { 097 return false; 098 } 099 100 @Override 101 public void reset() { 102 inBuf.clear(); 103 inLen = 0; 104 outBuf.clear(); 105 outBuf.position(outBuf.capacity()); 106 finished = false; 107 } 108 109 @Override 110 public boolean needsInput() { 111 return inBuf.position() == 0; 112 } 113 114 @Override 115 public void setDictionary(byte[] b, int off, int len) { 116 throw new UnsupportedOperationException("setDictionary is not supported"); 117 } 118 119 @Override 120 public void setInput(byte[] b, int off, int len) { 121 if (inBuf.remaining() < len) { 122 // Get a new buffer that can accomodate the accumulated input plus the additional 123 // input that would cause a buffer overflow without reallocation. 124 // This condition should be fortunately rare, because it is expensive. 125 int needed = CompressionUtil.roundInt2(inBuf.capacity() + len); 126 ByteBuffer newBuf = ByteBuffer.allocate(needed); 127 inBuf.flip(); 128 newBuf.put(inBuf); 129 inBuf = newBuf; 130 } 131 inBuf.put(b, off, len); 132 inLen += len; 133 finished = false; 134 } 135 136}