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.hbase.io.compress.Compression;
26  import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;
27  import org.apache.hadoop.hbase.io.hfile.BlockType;
28  import org.apache.hadoop.io.compress.CompressionOutputStream;
29  import org.apache.hadoop.io.compress.Compressor;
30  
31  import com.google.common.base.Preconditions;
32  
33  /**
34   * A default implementation of {@link HFileBlockEncodingContext}. It will
35   * compress the data section as one continuous buffer.
36   *
37   * @see HFileBlockDefaultDecodingContext for the decompression part
38   *
39   */
40  public class HFileBlockDefaultEncodingContext implements
41      HFileBlockEncodingContext {
42  
43    private byte[] onDiskBytesWithHeader;
44    private byte[] uncompressedBytesWithHeader;
45    private BlockType blockType;
46    private final DataBlockEncoding encodingAlgo;
47  
48    /** Compressor, which is also reused between consecutive blocks. */
49    private Compressor compressor;
50  
51    /** Compression output stream */
52    private CompressionOutputStream compressionStream;
53  
54    /** Underlying stream to write compressed bytes to */
55    private ByteArrayOutputStream compressedByteStream;
56  
57    /** Compression algorithm for all blocks this instance writes. */
58    private final Compression.Algorithm compressionAlgorithm;
59  
60    private ByteArrayOutputStream encodedStream = new ByteArrayOutputStream();
61    private DataOutputStream dataOut = new DataOutputStream(encodedStream);
62  
63    private final byte[] dummyHeader;
64  
65    /**
66     * @param compressionAlgorithm compression algorithm used
67     * @param encoding encoding used
68     * @param headerBytes dummy header bytes
69     */
70    public HFileBlockDefaultEncodingContext(
71        Compression.Algorithm compressionAlgorithm,
72        DataBlockEncoding encoding, byte[] headerBytes) {
73      this.encodingAlgo = encoding;
74      this.compressionAlgorithm =
75          compressionAlgorithm == null ? NONE : compressionAlgorithm;
76      if (this.compressionAlgorithm != NONE) {
77        compressor = compressionAlgorithm.getCompressor();
78        compressedByteStream = new ByteArrayOutputStream();
79        try {
80          compressionStream =
81              compressionAlgorithm.createPlainCompressionStream(
82                  compressedByteStream, compressor);
83        } catch (IOException e) {
84          throw new RuntimeException(
85              "Could not create compression stream for algorithm "
86                  + compressionAlgorithm, e);
87        }
88      }
89      dummyHeader = Preconditions.checkNotNull(headerBytes, 
90        "Please pass HFileBlock.HFILEBLOCK_DUMMY_HEADER instead of null for param headerBytes");
91    }
92  
93    /**
94     * prepare to start a new encoding.
95     * @throws IOException
96     */
97    public void prepareEncoding() throws IOException {
98      encodedStream.reset();
99      dataOut.write(dummyHeader);
100     if (encodingAlgo != null
101         && encodingAlgo != DataBlockEncoding.NONE) {
102       encodingAlgo.writeIdInBytes(dataOut);
103     }
104   }
105 
106   @Override
107   public void postEncoding(BlockType blockType)
108       throws IOException {
109     dataOut.flush();
110     compressAfterEncoding(encodedStream.toByteArray(), blockType);
111     this.blockType = blockType;
112   }
113 
114   /**
115    * @param uncompressedBytesWithHeader
116    * @param blockType
117    * @throws IOException
118    */
119   public void compressAfterEncoding(byte[] uncompressedBytesWithHeader,
120       BlockType blockType) throws IOException {
121     compressAfterEncoding(uncompressedBytesWithHeader, blockType, dummyHeader);
122   }
123 
124   /**
125    * @param uncompressedBytesWithHeader
126    * @param blockType
127    * @param headerBytes
128    * @throws IOException
129    */
130   protected void compressAfterEncoding(byte[] uncompressedBytesWithHeader,
131       BlockType blockType, byte[] headerBytes) throws IOException {
132     this.uncompressedBytesWithHeader = uncompressedBytesWithHeader;
133     if (compressionAlgorithm != NONE) {
134       compressedByteStream.reset();
135       compressedByteStream.write(headerBytes);
136       compressionStream.resetState();
137       compressionStream.write(uncompressedBytesWithHeader,
138           headerBytes.length, uncompressedBytesWithHeader.length
139               - headerBytes.length);
140 
141       compressionStream.flush();
142       compressionStream.finish();
143       onDiskBytesWithHeader = compressedByteStream.toByteArray();
144     } else {
145       onDiskBytesWithHeader = uncompressedBytesWithHeader;
146     }
147     this.blockType = blockType;
148   }
149 
150   @Override
151   public byte[] getOnDiskBytesWithHeader() {
152     return onDiskBytesWithHeader;
153   }
154 
155   @Override
156   public byte[] getUncompressedBytesWithHeader() {
157     return uncompressedBytesWithHeader;
158   }
159 
160   @Override
161   public BlockType getBlockType() {
162     return blockType;
163   }
164 
165   /**
166    * Releases the compressor this writer uses to compress blocks into the
167    * compressor pool.
168    */
169   @Override
170   public void close() {
171     if (compressor != null) {
172       compressionAlgorithm.returnCompressor(compressor);
173       compressor = null;
174     }
175   }
176 
177   @Override
178   public Algorithm getCompression() {
179     return this.compressionAlgorithm;
180   }
181 
182   public DataOutputStream getOutputStreamForEncoder() {
183     return this.dataOut;
184   }
185 
186   @Override
187   public DataBlockEncoding getDataBlockEncoding() {
188     return this.encodingAlgo;
189   }
190 
191   @Override
192   public int getHeaderSize() {
193     return this.dummyHeader.length;
194   }
195 
196 }