1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.hadoop.hbase.io.hfile;
18
19 import java.io.IOException;
20 import java.nio.ByteBuffer;
21
22 import org.apache.hadoop.classification.InterfaceAudience;
23 import org.apache.hadoop.hbase.HConstants;
24 import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;
25 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoder;
26 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
27 import org.apache.hadoop.hbase.io.encoding.HFileBlockDefaultDecodingContext;
28 import org.apache.hadoop.hbase.io.encoding.HFileBlockDefaultEncodingContext;
29 import org.apache.hadoop.hbase.io.encoding.HFileBlockEncodingContext;
30 import org.apache.hadoop.hbase.io.encoding.HFileBlockDecodingContext;
31 import org.apache.hadoop.hbase.io.hfile.HFile.FileInfo;
32 import org.apache.hadoop.hbase.util.Bytes;
33
34 import com.google.common.base.Preconditions;
35
36
37
38
39
40 @InterfaceAudience.Private
41 public class HFileDataBlockEncoderImpl implements HFileDataBlockEncoder {
42 private final DataBlockEncoding onDisk;
43 private final DataBlockEncoding inCache;
44 private final HFileBlockEncodingContext inCacheEncodeCtx;
45
46 public HFileDataBlockEncoderImpl(DataBlockEncoding encoding) {
47 this(encoding, encoding);
48 }
49
50
51
52
53
54
55
56
57
58 public HFileDataBlockEncoderImpl(DataBlockEncoding onDisk,
59 DataBlockEncoding inCache) {
60 this(onDisk, inCache, HConstants.HFILEBLOCK_DUMMY_HEADER);
61 }
62
63
64
65
66
67
68
69
70
71
72 public HFileDataBlockEncoderImpl(DataBlockEncoding onDisk,
73 DataBlockEncoding inCache, byte[] dummyHeader) {
74 this.onDisk = onDisk != null ?
75 onDisk : DataBlockEncoding.NONE;
76 this.inCache = inCache != null ?
77 inCache : DataBlockEncoding.NONE;
78 if (inCache != DataBlockEncoding.NONE) {
79 inCacheEncodeCtx =
80 this.inCache.getEncoder().newDataBlockEncodingContext(
81 Algorithm.NONE, this.inCache, dummyHeader);
82 } else {
83
84 inCacheEncodeCtx =
85 new HFileBlockDefaultEncodingContext(Algorithm.NONE,
86 this.inCache, dummyHeader);
87 }
88
89 Preconditions.checkArgument(onDisk == DataBlockEncoding.NONE ||
90 onDisk == inCache, "on-disk encoding (" + onDisk + ") must be " +
91 "either the same as in-cache encoding (" + inCache + ") or " +
92 DataBlockEncoding.NONE);
93 }
94
95 public static HFileDataBlockEncoder createFromFileInfo(
96 FileInfo fileInfo, DataBlockEncoding preferredEncodingInCache)
97 throws IOException {
98 boolean hasPreferredCacheEncoding = preferredEncodingInCache != null
99 && preferredEncodingInCache != DataBlockEncoding.NONE;
100
101 byte[] dataBlockEncodingType = fileInfo.get(DATA_BLOCK_ENCODING);
102 if (dataBlockEncodingType == null && !hasPreferredCacheEncoding) {
103 return NoOpDataBlockEncoder.INSTANCE;
104 }
105
106 DataBlockEncoding onDisk;
107 if (dataBlockEncodingType == null) {
108 onDisk = DataBlockEncoding.NONE;
109 } else {
110 String dataBlockEncodingStr = Bytes.toString(dataBlockEncodingType);
111 try {
112 onDisk = DataBlockEncoding.valueOf(dataBlockEncodingStr);
113 } catch (IllegalArgumentException ex) {
114 throw new IOException("Invalid data block encoding type in file info: "
115 + dataBlockEncodingStr, ex);
116 }
117 }
118
119 DataBlockEncoding inCache;
120 if (onDisk == DataBlockEncoding.NONE) {
121
122
123
124 inCache = preferredEncodingInCache;
125 } else {
126
127
128
129
130 inCache = onDisk;
131 }
132
133
134 return new HFileDataBlockEncoderImpl(onDisk, inCache);
135 }
136
137 @Override
138 public void saveMetadata(HFile.Writer writer) throws IOException {
139 writer.appendFileInfo(DATA_BLOCK_ENCODING, onDisk.getNameInBytes());
140 }
141
142 @Override
143 public DataBlockEncoding getEncodingOnDisk() {
144 return onDisk;
145 }
146
147 @Override
148 public DataBlockEncoding getEncodingInCache() {
149 return inCache;
150 }
151
152 @Override
153 public DataBlockEncoding getEffectiveEncodingInCache(boolean isCompaction) {
154 if (!useEncodedScanner(isCompaction)) {
155 return DataBlockEncoding.NONE;
156 }
157 return inCache;
158 }
159
160 @Override
161 public HFileBlock diskToCacheFormat(HFileBlock block, boolean isCompaction) {
162 if (block.getBlockType() == BlockType.DATA) {
163 if (!useEncodedScanner(isCompaction)) {
164
165 return block;
166 }
167
168 return encodeDataBlock(block, inCache, block.doesIncludeMemstoreTS(),
169 inCacheEncodeCtx);
170 }
171
172 if (block.getBlockType() == BlockType.ENCODED_DATA) {
173 if (block.getDataBlockEncodingId() == onDisk.getId()) {
174
175 return block;
176 }
177
178
179
180 throw new AssertionError("Expected on-disk data block encoding " +
181 onDisk + ", got " + block.getDataBlockEncoding());
182 }
183 return block;
184 }
185
186
187
188
189
190
191
192
193 @Override
194 public void beforeWriteToDisk(ByteBuffer in,
195 boolean includesMemstoreTS,
196 HFileBlockEncodingContext encodeCtx,
197 BlockType blockType) throws IOException {
198 if (onDisk == DataBlockEncoding.NONE) {
199
200 ((HFileBlockDefaultEncodingContext) encodeCtx).compressAfterEncodingWithBlockType(
201 in.array(), blockType);
202 return;
203 }
204 encodeBufferToHFileBlockBuffer(in, onDisk,
205 includesMemstoreTS, encodeCtx);
206 }
207
208 @Override
209 public boolean useEncodedScanner(boolean isCompaction) {
210 if (isCompaction && onDisk == DataBlockEncoding.NONE) {
211 return false;
212 }
213 return inCache != DataBlockEncoding.NONE;
214 }
215
216
217
218
219
220
221
222
223
224 private void encodeBufferToHFileBlockBuffer(ByteBuffer in,
225 DataBlockEncoding algo, boolean includesMemstoreTS,
226 HFileBlockEncodingContext encodeCtx) {
227 DataBlockEncoder encoder = algo.getEncoder();
228 try {
229 encoder.encodeKeyValues(in, includesMemstoreTS, encodeCtx);
230 } catch (IOException e) {
231 throw new RuntimeException(String.format(
232 "Bug in data block encoder "
233 + "'%s', it probably requested too much data, " +
234 "exception message: %s.",
235 algo.toString(), e.getMessage()), e);
236 }
237 }
238
239 private HFileBlock encodeDataBlock(HFileBlock block,
240 DataBlockEncoding algo, boolean includesMemstoreTS,
241 HFileBlockEncodingContext encodingCtx) {
242 encodingCtx.setDummyHeader(block.getDummyHeaderForVersion());
243 encodeBufferToHFileBlockBuffer(
244 block.getBufferWithoutHeader(), algo, includesMemstoreTS, encodingCtx);
245 byte[] encodedUncompressedBytes =
246 encodingCtx.getUncompressedBytesWithHeader();
247 ByteBuffer bufferWrapper = ByteBuffer.wrap(encodedUncompressedBytes);
248 int sizeWithoutHeader = bufferWrapper.limit() - block.headerSize();
249 HFileBlock encodedBlock = new HFileBlock(BlockType.ENCODED_DATA,
250 block.getOnDiskSizeWithoutHeader(),
251 sizeWithoutHeader, block.getPrevBlockOffset(),
252 bufferWrapper, HFileBlock.FILL_HEADER, block.getOffset(),
253 includesMemstoreTS, block.getMinorVersion(),
254 block.getBytesPerChecksum(), block.getChecksumType(),
255 block.getOnDiskDataSizeWithHeader());
256 return encodedBlock;
257 }
258
259 @Override
260 public String toString() {
261 return getClass().getSimpleName() + "(onDisk=" + onDisk + ", inCache=" +
262 inCache + ")";
263 }
264
265 @Override
266 public HFileBlockEncodingContext newOnDiskDataBlockEncodingContext(
267 Algorithm compressionAlgorithm, byte[] dummyHeader) {
268 if (onDisk != null) {
269 DataBlockEncoder encoder = onDisk.getEncoder();
270 if (encoder != null) {
271 return encoder.newDataBlockEncodingContext(
272 compressionAlgorithm, onDisk, dummyHeader);
273 }
274 }
275 return new HFileBlockDefaultEncodingContext(compressionAlgorithm,
276 null, dummyHeader);
277 }
278
279 @Override
280 public HFileBlockDecodingContext newOnDiskDataBlockDecodingContext(
281 Algorithm compressionAlgorithm) {
282 if (onDisk != null) {
283 DataBlockEncoder encoder = onDisk.getEncoder();
284 if (encoder != null) {
285 return encoder.newDataBlockDecodingContext(
286 compressionAlgorithm);
287 }
288 }
289 return new HFileBlockDefaultDecodingContext(compressionAlgorithm);
290 }
291
292 }