All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mariadb.jdbc.client.socket.impl.CompressOutputStream Maven / Gradle / Ivy

// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2021 MariaDB Corporation Ab

package org.mariadb.jdbc.client.socket.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.DeflaterOutputStream;
import org.mariadb.jdbc.client.util.MutableByte;

/**
 * Compression writer handler Permit to wrap standard packet to compressed packet ( 7 byte header).
 * Driver will compress packet only if packet size is meaningful (1536 bytes) > to one TCP
 * packet.
 */
public class CompressOutputStream extends OutputStream {
  private static final int MIN_COMPRESSION_SIZE = 1536; // TCP-IP single packet
  private final OutputStream out;
  private final MutableByte sequence;
  private final byte[] header = new byte[7];
  private byte[] longPacketBuffer = null;

  /**
   * Constructor.
   *
   * @param out socket output stream
   * @param compressionSequence compression sequence
   */
  public CompressOutputStream(OutputStream out, MutableByte compressionSequence) {
    this.out = out;
    this.sequence = compressionSequence;
  }

  /**
   * Writes len bytes from the specified byte array starting at offset off
   * to this output stream. The general contract for write(b, off, len) is that some
   * bytes in the array b are written to the output stream in order; element 
   * b[off] is the first byte written and b[off+len-1] is the last byte written
   * by this operation.
   *
   * 

The write method of OutputStream calls the write method of one * argument on each of the bytes to be written out. Subclasses are encouraged to override this * method and provide a more efficient implementation. * *

If b is null, a NullPointerException is thrown. * *

If off is negative, or len is negative, or off+len is * greater than the length of the array b, then an IndexOutOfBoundsException is * thrown. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @throws IOException if an I/O error occurs. In particular, an IOException is * thrown if the output stream is closed. */ @Override public void write(byte[] b, int off, int len) throws IOException { if (len + ((longPacketBuffer != null) ? longPacketBuffer.length : 0) < MIN_COMPRESSION_SIZE) { // ******************************************************************************* // small packet, no compression // ******************************************************************************* if (longPacketBuffer != null) { header[0] = (byte) (len + longPacketBuffer.length); header[1] = (byte) ((len + longPacketBuffer.length) >>> 8); header[2] = 0; header[3] = sequence.incrementAndGet(); header[4] = 0; header[5] = 0; header[6] = 0; out.write(header, 0, 7); out.write(longPacketBuffer, 0, longPacketBuffer.length); out.write(b, off, len); longPacketBuffer = null; return; } header[0] = (byte) len; header[1] = (byte) (len >>> 8); header[2] = 0; header[3] = sequence.incrementAndGet(); header[4] = 0; header[5] = 0; header[6] = 0; out.write(header, 0, 7); out.write(b, off, len); } else { // ******************************************************************************* // compressing packet // ******************************************************************************* int sent = 0; try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { try (DeflaterOutputStream deflater = new DeflaterOutputStream(baos)) { /** * For multi packet, len will be 0x00ffffff + 4 bytes for header. but compression can only * compress up to 0x00ffffff bytes (header initial length size cannot be > 3 bytes) so, * for this specific case, a buffer will save remaining data */ if (longPacketBuffer != null) { deflater.write(longPacketBuffer, 0, longPacketBuffer.length); sent = longPacketBuffer.length; longPacketBuffer = null; } if (len + sent > 0x00ffffff) { int remaining = len + sent - 0x00ffffff; longPacketBuffer = new byte[remaining]; System.arraycopy(b, off + 0x00ffffff - sent, longPacketBuffer, 0, remaining); } int bufLenSent = Math.min(0x00ffffff - sent, len); deflater.write(b, off, bufLenSent); sent += bufLenSent; deflater.finish(); } byte[] compressedBytes = baos.toByteArray(); int compressLen = compressedBytes.length; header[0] = (byte) compressLen; header[1] = (byte) (compressLen >>> 8); header[2] = (byte) (compressLen >>> 16); header[3] = sequence.incrementAndGet(); header[4] = (byte) sent; header[5] = (byte) (sent >>> 8); header[6] = (byte) (sent >>> 16); out.write(header, 0, 7); out.write(compressedBytes, 0, compressLen); out.flush(); } } } /** * Flushes this output stream and forces any buffered output bytes to be written out. The general * contract of flush is that calling it is an indication that, if any bytes * previously written have been buffered by the implementation of the output stream, such bytes * should immediately be written to their intended destination. * *

If the intended destination of this stream is an abstraction provided by the underlying * operating system, for example a file, then flushing the stream guarantees only that bytes * previously written to the stream are passed to the operating system for writing; it does not * guarantee that they are actually written to a physical device such as a disk drive. * *

The flush method of OutputStream does nothing. * * @throws IOException if an I/O error occurs. */ @Override public void flush() throws IOException { if (longPacketBuffer != null) { byte[] b = longPacketBuffer; longPacketBuffer = null; write(b, 0, b.length); } out.flush(); sequence.set((byte) -1); } /** * Closes this output stream and releases any system resources associated with this stream. The * general contract of close is that it closes the output stream. A closed stream * cannot perform output operations and cannot be reopened. * *

The close method of OutputStream does nothing. * * @throws IOException if an I/O error occurs. */ @Override public void close() throws IOException { out.close(); } /** * Writes the specified byte to this output stream. The general contract for write is * that one byte is written to the output stream. The byte to be written is the eight low-order * bits of the argument b. The 24 high-order bits of b are ignored. * *

Subclasses of OutputStream must provide an implementation for this method. * * @param b the byte. * @throws IOException if an I/O error occurs. In particular, an IOException may be * thrown if the output stream has been closed. */ @Override public void write(int b) throws IOException { throw new IOException("NOT EXPECTED !"); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy