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     */
018    
019    package org.apache.hadoop.io.compress;
020    
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.OutputStream;
024    
025    import org.apache.hadoop.conf.Configurable;
026    import org.apache.hadoop.conf.Configuration;
027    import org.apache.hadoop.io.compress.snappy.SnappyCompressor;
028    import org.apache.hadoop.io.compress.snappy.SnappyDecompressor;
029    import org.apache.hadoop.fs.CommonConfigurationKeys;
030    import org.apache.hadoop.util.NativeCodeLoader;
031    
032    /**
033     * This class creates snappy compressors/decompressors.
034     */
035    public class SnappyCodec implements Configurable, CompressionCodec {
036      Configuration conf;
037    
038      /**
039       * Set the configuration to be used by this object.
040       *
041       * @param conf the configuration object.
042       */
043      @Override
044      public void setConf(Configuration conf) {
045        this.conf = conf;
046      }
047    
048      /**
049       * Return the configuration used by this object.
050       *
051       * @return the configuration object used by this objec.
052       */
053      @Override
054      public Configuration getConf() {
055        return conf;
056      }
057    
058      /**
059       * Are the native snappy libraries loaded & initialized?
060       */
061      public static void checkNativeCodeLoaded() {
062          if (!NativeCodeLoader.buildSupportsSnappy()) {
063            throw new RuntimeException("native snappy library not available: " +
064                "this version of libhadoop was built without " +
065                "snappy support.");
066          }
067          if (!SnappyCompressor.isNativeCodeLoaded()) {
068            throw new RuntimeException("native snappy library not available: " +
069                "SnappyCompressor has not been loaded.");
070          }
071          if (!SnappyDecompressor.isNativeCodeLoaded()) {
072            throw new RuntimeException("native snappy library not available: " +
073                "SnappyDecompressor has not been loaded.");
074          }
075      }
076      
077      public static boolean isNativeCodeLoaded() {
078        return SnappyCompressor.isNativeCodeLoaded() && 
079            SnappyDecompressor.isNativeCodeLoaded();
080      }
081    
082      public static String getLibraryName() {
083        return SnappyCompressor.getLibraryName();
084      }
085    
086      /**
087       * Create a {@link CompressionOutputStream} that will write to the given
088       * {@link OutputStream}.
089       *
090       * @param out the location for the final output stream
091       * @return a stream the user can write uncompressed data to have it compressed
092       * @throws IOException
093       */
094      @Override
095      public CompressionOutputStream createOutputStream(OutputStream out)
096          throws IOException {
097        return createOutputStream(out, createCompressor());
098      }
099    
100      /**
101       * Create a {@link CompressionOutputStream} that will write to the given
102       * {@link OutputStream} with the given {@link Compressor}.
103       *
104       * @param out        the location for the final output stream
105       * @param compressor compressor to use
106       * @return a stream the user can write uncompressed data to have it compressed
107       * @throws IOException
108       */
109      @Override
110      public CompressionOutputStream createOutputStream(OutputStream out,
111                                                        Compressor compressor)
112          throws IOException {
113        checkNativeCodeLoaded();
114        int bufferSize = conf.getInt(
115            CommonConfigurationKeys.IO_COMPRESSION_CODEC_SNAPPY_BUFFERSIZE_KEY,
116            CommonConfigurationKeys.IO_COMPRESSION_CODEC_SNAPPY_BUFFERSIZE_DEFAULT);
117    
118        int compressionOverhead = (bufferSize / 6) + 32;
119    
120        return new BlockCompressorStream(out, compressor, bufferSize,
121            compressionOverhead);
122      }
123    
124      /**
125       * Get the type of {@link Compressor} needed by this {@link CompressionCodec}.
126       *
127       * @return the type of compressor needed by this codec.
128       */
129      @Override
130      public Class<? extends Compressor> getCompressorType() {
131        checkNativeCodeLoaded();
132        return SnappyCompressor.class;
133      }
134    
135      /**
136       * Create a new {@link Compressor} for use by this {@link CompressionCodec}.
137       *
138       * @return a new compressor for use by this codec
139       */
140      @Override
141      public Compressor createCompressor() {
142        checkNativeCodeLoaded();
143        int bufferSize = conf.getInt(
144            CommonConfigurationKeys.IO_COMPRESSION_CODEC_SNAPPY_BUFFERSIZE_KEY,
145            CommonConfigurationKeys.IO_COMPRESSION_CODEC_SNAPPY_BUFFERSIZE_DEFAULT);
146        return new SnappyCompressor(bufferSize);
147      }
148    
149      /**
150       * Create a {@link CompressionInputStream} that will read from the given
151       * input stream.
152       *
153       * @param in the stream to read compressed bytes from
154       * @return a stream to read uncompressed bytes from
155       * @throws IOException
156       */
157      @Override
158      public CompressionInputStream createInputStream(InputStream in)
159          throws IOException {
160        return createInputStream(in, createDecompressor());
161      }
162    
163      /**
164       * Create a {@link CompressionInputStream} that will read from the given
165       * {@link InputStream} with the given {@link Decompressor}.
166       *
167       * @param in           the stream to read compressed bytes from
168       * @param decompressor decompressor to use
169       * @return a stream to read uncompressed bytes from
170       * @throws IOException
171       */
172      @Override
173      public CompressionInputStream createInputStream(InputStream in,
174                                                      Decompressor decompressor)
175          throws IOException {
176        checkNativeCodeLoaded();
177        return new BlockDecompressorStream(in, decompressor, conf.getInt(
178            CommonConfigurationKeys.IO_COMPRESSION_CODEC_SNAPPY_BUFFERSIZE_KEY,
179            CommonConfigurationKeys.IO_COMPRESSION_CODEC_SNAPPY_BUFFERSIZE_DEFAULT));
180      }
181    
182      /**
183       * Get the type of {@link Decompressor} needed by this {@link CompressionCodec}.
184       *
185       * @return the type of decompressor needed by this codec.
186       */
187      @Override
188      public Class<? extends Decompressor> getDecompressorType() {
189        checkNativeCodeLoaded();
190        return SnappyDecompressor.class;
191      }
192    
193      /**
194       * Create a new {@link Decompressor} for use by this {@link CompressionCodec}.
195       *
196       * @return a new decompressor for use by this codec
197       */
198      @Override
199      public Decompressor createDecompressor() {
200        checkNativeCodeLoaded();
201        int bufferSize = conf.getInt(
202            CommonConfigurationKeys.IO_COMPRESSION_CODEC_SNAPPY_BUFFERSIZE_KEY,
203            CommonConfigurationKeys.IO_COMPRESSION_CODEC_SNAPPY_BUFFERSIZE_DEFAULT);
204        return new SnappyDecompressor(bufferSize);
205      }
206    
207      /**
208       * Get the default filename extension for this kind of compression.
209       *
210       * @return <code>.snappy</code>.
211       */
212      @Override
213      public String getDefaultExtension() {
214        return ".snappy";
215      }
216    }