View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.filter.support;
    21
    22  import java.io.IOException;
    23
    24  import org.apache.mina.common.ByteBuffer;
    25
    26  import com.jcraft.jzlib.JZlib;
    27  import com.jcraft.jzlib.ZStream;
    28
    29  /**
    30   * A helper class for interfacing with the JZlib library. This class acts both
    31   * as a compressor and decompressor, but only as one at a time.  The only
    32   * flush method supported is <tt>Z_SYNC_FLUSH</tt> also known as <tt>Z_PARTIAL_FLUSH</tt>
    33   *
    34   * @author The Apache Directory Project (mina-dev@directory.apache.org)
    35   * @version $Rev: 555855 $, $Date: 2007-07-13 05:19:00 +0200 (Fri, 13 Jul 2007) $
    36   */
    37  public class Zlib {
    38      public static final int COMPRESSION_MAX = JZlib.Z_BEST_COMPRESSION;
    39
    40      public static final int COMPRESSION_MIN = JZlib.Z_BEST_SPEED;
    41
    42      public static final int COMPRESSION_NONE = JZlib.Z_NO_COMPRESSION;
    43
    44      public static final int COMPRESSION_DEFAULT = JZlib.Z_DEFAULT_COMPRESSION;
    45
    46      public static final int MODE_DEFLATER = 1;
    47
    48      public static final int MODE_INFLATER = 2;
    49
    50      private int compressionLevel;
    51
    52      private ZStream zStream = null;
    53
    54      private int mode = -1;
    55
    56      /**
    57       * @param compressionLevel the level of compression that should be used
    58       * @param mode the mode in which the instance will operate. Can be either
    59       * of <tt>MODE_DEFLATER</tt> or <tt>MODE_INFLATER</tt>
    60       */
    61      public Zlib(int compressionLevel, int mode) {
    62          switch (compressionLevel) {
    63          case COMPRESSION_MAX:
    64          case COMPRESSION_MIN:
    65          case COMPRESSION_NONE:
    66          case COMPRESSION_DEFAULT:
    67              this.compressionLevel = compressionLevel;
    68              break;
    69          default:
    70              throw new IllegalArgumentException(
    71                      "invalid compression level specified");
    72          }
    73
    74          // create a new instance of ZStream. This will be done only once.
    75          zStream = new ZStream();
    76
    77          switch (mode) {
    78          case MODE_DEFLATER:
    79              zStream.deflateInit(this.compressionLevel);
    80              break;
    81          case MODE_INFLATER:
    82              zStream.inflateInit();
    83              break;
    84          default:
    85              throw new IllegalArgumentException("invalid mode specified");
    86          }
    87          this.mode = mode;
    88      }
    89
    90      /**
    91       * @param inBuffer the {@link ByteBuffer} to be decompressed. The contents
    92       * of the buffer are transferred into a local byte array and the buffer is
    93       * flipped and returned intact.
    94       * @return the decompressed data. If not passed to the MINA methods that
    95       * release the buffer automatically, the buffer has to be manually released
    96       * @throws IOException if the decompression of the data failed for some reason.
    97       */
    98      public ByteBuffer inflate(ByteBuffer inBuffer) throws IOException {
    99          if (mode == MODE_DEFLATER) {
    100             throw new IllegalStateException("not initialized as INFLATER");
    101         }
    102
    103         byte[] inBytes = new byte[inBuffer.limit()];
    104         inBuffer.get(inBytes).flip();
    105
    106         byte[] outBytes = new byte[inBytes.length * 2];
    107         ByteBuffer outBuffer = ByteBuffer.allocate(outBytes.length);
    108         outBuffer.setAutoExpand(true);
    109
    110         zStream.next_in = inBytes;
    111         zStream.next_in_index = 0;
    112         zStream.avail_in = inBytes.length;
    113         zStream.next_out = outBytes;
    114         zStream.next_out_index = 0;
    115         zStream.avail_out = outBytes.length;
    116         int retval = 0;
    117
    118         do {
    119             retval = zStream.inflate(JZlib.Z_SYNC_FLUSH);
    120             switch (retval) {
    121             case JZlib.Z_OK:
    122                 // completed decompression, lets copy data and get out
    123             case JZlib.Z_BUF_ERROR:
    124                 // need more space for output. store current output and get more
    125                 outBuffer.put(outBytes, 0, zStream.next_out_index);
    126                 zStream.next_out_index = 0;
    127                 zStream.avail_out = outBytes.length;
    128                 break;
    129             default:
    130                 // unknown error
    131                 outBuffer.release();
    132                 outBuffer = null;
    133                 if (zStream.msg == null)
    134                     throw new IOException("Unknown error. Error code : "
    135                             + retval);
    136                 else
    137                     throw new IOException("Unknown error. Error code : "
    138                             + retval + " and message : " + zStream.msg);
    139             }
    140         } while (zStream.avail_in > 0);
    141
    142         return outBuffer.flip();
    143     }
    144
    145     /**
    146      * @param inBuffer the buffer to be compressed. The contents are transferred
    147      * into a local byte array and the buffer is flipped and returned intact.
    148      * @return the buffer with the compressed data. If not passed to any of the
    149      * MINA methods that automatically release the buffer, the buffer has to be
    150      * released manually.
    151      * @throws IOException if the compression of teh buffer failed for some reason
    152      */
    153     public ByteBuffer deflate(ByteBuffer inBuffer) throws IOException {
    154         if (mode == MODE_INFLATER) {
    155             throw new IllegalStateException("not initialized as DEFLATER");
    156         }
    157
    158         byte[] inBytes = new byte[inBuffer.limit()];
    159         inBuffer.get(inBytes).flip();
    160
    161         // according to spec, destination buffer should be 0.1% larger
    162         // than source length plus 12 bytes. We add a single byte to safeguard
    163         // against rounds that round down to the smaller value
    164         int outLen = (int) Math.round(inBytes.length * 1.001) + 1 + 12;
    165         byte[] outBytes = new byte[outLen];
    166
    167         zStream.next_in = inBytes;
    168         zStream.next_in_index = 0;
    169         zStream.avail_in = inBytes.length;
    170         zStream.next_out = outBytes;
    171         zStream.next_out_index = 0;
    172         zStream.avail_out = outBytes.length;
    173
    174         int retval = zStream.deflate(JZlib.Z_SYNC_FLUSH);
    175         if (retval != JZlib.Z_OK) {
    176             outBytes = null;
    177             inBytes = null;
    178             throw new IOException("Compression failed with return value : "
    179                     + retval);
    180         }
    181
    182         ByteBuffer outBuf = ByteBuffer
    183                 .wrap(outBytes, 0, zStream.next_out_index);
    184
    185         return outBuf;
    186     }
    187
    188     /**
    189      * Cleans up the resources used by the compression library.
    190      */
    191     public void cleanUp() {
    192         if (zStream != null)
    193             zStream.free();
    194     }
    195 }