View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to you under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14   * License for the specific language governing permissions and limitations
15   * under the License.
16   */
17  package org.apache.hadoop.hbase.io.encoding;
18  
19  import static org.apache.hadoop.hbase.io.compress.Compression.Algorithm.NONE;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.DataOutputStream;
23  import java.io.IOException;
24  
25  import org.apache.hadoop.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.io.compress.Compression;
27  import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;
28  import org.apache.hadoop.hbase.io.hfile.BlockType;
29  import org.apache.hadoop.io.compress.CompressionOutputStream;
30  import org.apache.hadoop.io.compress.Compressor;
31  
32  import com.google.common.base.Preconditions;
33  
34  /**
35   * A default implementation of {@link HFileBlockEncodingContext}. It will
36   * compress the data section as one continuous buffer.
37   *
38   * @see HFileBlockDefaultDecodingContext for the decompression part
39   *
40   */
41  @InterfaceAudience.Private
42  public class HFileBlockDefaultEncodingContext implements
43      HFileBlockEncodingContext {
44  
45    private byte[] onDiskBytesWithHeader;
46    private byte[] uncompressedBytesWithHeader;
47    private BlockType blockType;
48    private final DataBlockEncoding encodingAlgo;
49  
50    /** Compressor, which is also reused between consecutive blocks. */
51    private Compressor compressor;
52  
53    /** Compression output stream */
54    private CompressionOutputStream compressionStream;
55  
56    /** Underlying stream to write compressed bytes to */
57    private ByteArrayOutputStream compressedByteStream;
58  
59    /** Compression algorithm for all blocks this instance writes. */
60    private final Compression.Algorithm compressionAlgorithm;
61  
62    private ByteArrayOutputStream encodedStream = new ByteArrayOutputStream();
63    private DataOutputStream dataOut = new DataOutputStream(encodedStream);
64  
65    private byte[] dummyHeader;
66  
67    /**
68     * @param compressionAlgorithm compression algorithm used
69     * @param encoding encoding used
70     * @param headerBytes dummy header bytes
71     */
72    public HFileBlockDefaultEncodingContext(
73        Compression.Algorithm compressionAlgorithm,
74        DataBlockEncoding encoding, byte[] headerBytes) {
75      this.encodingAlgo = encoding;
76      this.compressionAlgorithm =
77          compressionAlgorithm == null ? NONE : compressionAlgorithm;
78      if (this.compressionAlgorithm != NONE) {
79        compressor = compressionAlgorithm.getCompressor();
80        compressedByteStream = new ByteArrayOutputStream();
81        try {
82          compressionStream =
83              compressionAlgorithm.createPlainCompressionStream(
84                  compressedByteStream, compressor);
85        } catch (IOException e) {
86          throw new RuntimeException(
87              "Could not create compression stream for algorithm "
88                  + compressionAlgorithm, e);
89        }
90      }
91      dummyHeader = Preconditions.checkNotNull(headerBytes,
92        "Please pass HConstants.HFILEBLOCK_DUMMY_HEADER instead of null for param headerBytes");
93    }
94  
95    @Override
96    public void setDummyHeader(byte[] headerBytes) {
97      dummyHeader = headerBytes;
98    }
99  
100   /**
101    * prepare to start a new encoding.
102    * @throws IOException
103    */
104   public void prepareEncoding() throws IOException {
105     encodedStream.reset();
106     dataOut.write(dummyHeader);
107     if (encodingAlgo != null
108         && encodingAlgo != DataBlockEncoding.NONE) {
109       encodingAlgo.writeIdInBytes(dataOut);
110     }
111   }
112 
113   @Override
114   public void postEncoding(BlockType blockType)
115       throws IOException {
116     dataOut.flush();
117     compressAfterEncodingWithBlockType(encodedStream.toByteArray(), blockType);
118     this.blockType = blockType;
119   }
120 
121   /**
122    * @param uncompressedBytesWithHeader
123    * @param blockType
124    * @throws IOException
125    */
126   public void compressAfterEncodingWithBlockType(byte[] uncompressedBytesWithHeader,
127       BlockType blockType) throws IOException {
128     compressAfterEncoding(uncompressedBytesWithHeader, blockType, dummyHeader);
129   }
130 
131   /**
132    * @param uncompressedBytesWithHeader
133    * @param blockType
134    * @param headerBytes
135    * @throws IOException
136    */
137   protected void compressAfterEncoding(byte[] uncompressedBytesWithHeader,
138       BlockType blockType, byte[] headerBytes) throws IOException {
139     this.uncompressedBytesWithHeader = uncompressedBytesWithHeader;
140     if (compressionAlgorithm != NONE) {
141       compressedByteStream.reset();
142       compressedByteStream.write(headerBytes);
143       compressionStream.resetState();
144       compressionStream.write(uncompressedBytesWithHeader,
145           headerBytes.length, uncompressedBytesWithHeader.length
146               - headerBytes.length);
147 
148       compressionStream.flush();
149       compressionStream.finish();
150       onDiskBytesWithHeader = compressedByteStream.toByteArray();
151     } else {
152       onDiskBytesWithHeader = uncompressedBytesWithHeader;
153     }
154     this.blockType = blockType;
155   }
156 
157   @Override
158   public byte[] getOnDiskBytesWithHeader() {
159     return onDiskBytesWithHeader;
160   }
161 
162   @Override
163   public byte[] getUncompressedBytesWithHeader() {
164     return uncompressedBytesWithHeader;
165   }
166 
167   @Override
168   public BlockType getBlockType() {
169     return blockType;
170   }
171 
172   /**
173    * Releases the compressor this writer uses to compress blocks into the
174    * compressor pool.
175    */
176   @Override
177   public void close() {
178     if (compressor != null) {
179       compressionAlgorithm.returnCompressor(compressor);
180       compressor = null;
181     }
182   }
183 
184   @Override
185   public Algorithm getCompression() {
186     return this.compressionAlgorithm;
187   }
188 
189   public DataOutputStream getOutputStreamForEncoder() {
190     return this.dataOut;
191   }
192 
193   @Override
194   public DataBlockEncoding getDataBlockEncoding() {
195     return this.encodingAlgo;
196   }
197 }