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

org.firebirdsql.gds.impl.wire.XdrInputStream Maven / Gradle / Ivy

There is a newer version: 6.0.0-beta-1
Show newest version
/*
 * Firebird Open Source JavaEE Connector - JDBC Driver
 *
 * Distributable under LGPL license.
 * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * LGPL License for more details.
 *
 * This file was created by members of the firebird development team.
 * All individual contributions remain the Copyright (C) of those
 * individuals.  Contributors to this file are either listed here or
 * can be obtained from a source control history command.
 *
 * All rights reserved.
 */
/*
 * The Original Code is the Firebird Java GDS implementation.
 *
 * The Initial Developer of the Original Code is Alejandro Alberola.
 * Portions created by Alejandro Alberola are Copyright (C) 2001
 * Boix i Oltra, S.L. All Rights Reserved.
 *
 */
package org.firebirdsql.gds.impl.wire;

import org.firebirdsql.encodings.Encoding;
import org.firebirdsql.util.InternalApi;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import java.io.*;

/**
 * {@code XdrInputStream} is an input stream for reading in data that
 * is in the XDR format. An {@code XdrInputStream} instance is wrapped
 * around an underlying {@code java.io.InputStream}.
 * 

* This class is not thread-safe. *

* * @author Alejandro Alberola * @author David Jencks * @author Mark Rotteveel * @version 1.0 */ public final class XdrInputStream extends FilterInputStream implements EncryptedStreamSupport { private static final int DEFAULT_BUFFER_SIZE = 16384; private boolean compressed; private boolean encrypted; /** * Create a new instance of {@code XdrInputStream}. * * @param in The underlying {@code InputStream} to read from */ public XdrInputStream(InputStream in) { super(new BufferedInputStream(in, DEFAULT_BUFFER_SIZE)); } /** * Skips the padding after a buffer of the specified length. The number of bytes to skip is calculated as * {@code (4 - length) & 3}. * * @param length * Length of the previously read buffer * @return Actual number of bytes skipped * @throws IOException * IOException if an error occurs while reading from the * underlying input stream * @see XdrOutputStream#writePadding(int, int) */ public int skipPadding(int length) throws IOException { int bytesToSkip = (4 - length) & 3; int actual = skipFully(bytesToSkip); assert actual == bytesToSkip : String.format("Unexpected number of bytes skipped: %d, expected: %d", actual, bytesToSkip); return actual; } /** * Skips the specified number of bytes. * * @param numbytes * Number of bytes to skip. * @return Actual number of bytes skipped (usually {@code numbytes}, unless the underlying input stream is closed). * @throws IOException * IOException if an error occurs while reading from the underlying input stream */ public int skipFully(int numbytes) throws IOException { // This is the skip from SocketInputStream, which will read all bytes unless the stream is closed. // We can't rely on InputStream.skip(int), because for example CipherInputStream will simply not skip beyond // its current buffer. if (numbytes <= 0) { return 0; } // TODO Switch the readNBytes in Java 9, and simplify as it's currently only used for 1 - 3 bytes. int n = numbytes; int buflen = Math.min(1024, n); byte[] data = new byte[buflen]; while (n > 0) { int r = read(data, 0, Math.min(buflen, n)); if (r < 0) { break; } n -= r; } return numbytes - n; } /** * Read in a byte buffer. * * @return The buffer that was read * @throws IOException if an error occurs while reading from the * underlying input stream */ public byte[] readBuffer() throws IOException { int len = readInt(); byte[] buffer = new byte[len]; readFully(buffer, 0, len); skipPadding(len); return buffer; } /** * Read in a raw array of bytes. * * @param len The number of bytes to read * @return The byte buffer that was read * @throws IOException if an error occurs while reading from the * underlying input stream */ public byte[] readRawBuffer(int len) throws IOException { byte[] buffer = new byte[len]; readFully(buffer, 0, len); return buffer; } /** * Read in a {@code String}. * * @return The {@code String} that was read * @throws IOException if an error occurs while reading from the * underlying input stream */ public String readString(Encoding encoding) throws IOException { byte[] buffer = readBuffer(); return encoding.decodeFromCharset(buffer); } private final byte[] readBuffer = new byte[8]; /** * Read in a {@code long}. * * @return The {@code long} that was read * @throws IOException if an error occurs while reading from the * underlying input stream */ public long readLong() throws IOException { readFully(readBuffer, 0, 8); return (((long) readBuffer[0] << 56) + ((long) (readBuffer[1] & 0xFF) << 48) + ((long) (readBuffer[2] & 0xFF) << 40) + ((long) (readBuffer[3] & 0xFF) << 32) + ((long) (readBuffer[4] & 0xFF) << 24) + ((readBuffer[5] & 0xFF) << 16) + ((readBuffer[6] & 0xFF) << 8) + ((readBuffer[7] & 0xFF))); } /** * Read in an {@code int}. * * @return The {@code int} that was read * @throws IOException if an error occurs while reading from the * underlying input stream */ public int readInt() throws IOException { int ch1 = read(); int ch2 = read(); int ch3 = read(); int ch4 = read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4)); } /** * Read in a {@code short}. * * @return The {@code short} that was read * @throws IOException if an error occurs while reading from the * underlying input stream */ public int readShort() throws IOException { int ch1 = read(); int ch2 = read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch1 << 8) + (ch2); } /** * Read a given amount of data from the underlying input stream. The data * that is read is stored in {@code b}, starting from offset * {@code off}. * * @param b The byte buffer to hold the data that is read * @param off The offset at which to start storing data in {@code b} * @param len The number of bytes to be read * @throws IOException if an error occurs while reading from the * underlying input stream */ public void readFully(byte[] b, int off, int len) throws IOException { if (len < 0) throw new IndexOutOfBoundsException(); int n = 0; while (n < len) { int count = read(b, off + n, len - n); if (count < 0) throw new EOFException(); n += count; } } /** * Wraps the underlying stream for zlib decompression. * * @throws IOException * If the underlying stream is already set up for decompression */ @InternalApi public void enableDecompression() throws IOException { if (compressed) { throw new IOException("Input stream already compressed"); } in = new FbInflaterInputStream(in); compressed = true; } @Override public void setCipher(Cipher cipher) throws IOException { if (encrypted) { throw new IOException("Input stream already encrypted"); } InputStream currentStream = in; if (currentStream instanceof EncryptedStreamSupport) { ((EncryptedStreamSupport) currentStream).setCipher(cipher); } else { in = new CipherInputStream(currentStream, cipher); } encrypted = true; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy