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

com.epam.deltix.util.vsocket.VSocketOutputStream Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2023 EPAM Systems, Inc
 *
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership. Licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.epam.deltix.util.vsocket;

import com.epam.deltix.util.collections.ByteQueue;

import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Level;

/**
 *
 */
public class VSocketOutputStream extends OutputStream {
    private final String socketIdStr;

    public static int           CAPACITY = 1024 * 512;
    public static int           INCREMENT = CAPACITY / 4;

    private final ByteQueue     buffer;
    private final OutputStream  out;
    long                        confirmed;

    // How many bytes we need to skip from the output buffer to make it in-sync with last "confirmed" position
    private int bufferDebt = 0;

    // Set to true if we got any IOException from output stream
    private volatile boolean broken = false;

    public VSocketOutputStream(OutputStream out, String socketIdStr) {
        this.out = out;
        this.socketIdStr = socketIdStr;
        this.buffer = new ByteQueue(CAPACITY);
    }

    @Override
    public void     write(byte[] b) {
        write(b, 0, b.length);
    }

    @Override
    public void     write(int b) {
        try {
            synchronized (out) {
                out.write(b);
            }
        } catch (IOException e) {
            broken = true;
            //throw new com.epam.deltix.util.io.UncheckedIOException(e);
        } finally {
            dump(b);
        }
    }

    @Override
    public void     write(byte[] b, int off, int len) {
        try {
            synchronized (out) {
                out.write(b, off, len);
            }
        } catch (IOException e) {
            broken = true;
            //throw new com.epam.deltix.util.io.UncheckedIOException(e);
        } finally {
            dump(b, off, len);
        }
    }

    /**
     * Same as calling {@link #write(byte[], int, int)} two times but a little bit more effective.
     */
    @SuppressWarnings("SameParameterValue")
    public void     writeTwoArrays(byte[] b1, int off1, int len1, byte[] b2, int off2, int len2) {
        try {
            synchronized (out) {
                out.write(b1, off1, len1);
                out.write(b2, off2, len2);
            }
        } catch (IOException e) {
            broken = true;
            //throw new com.epam.deltix.util.io.UncheckedIOException(e);
        } finally {
            dumpTwoArrays(b1, off1, len1, b2, off2, len2);
        }
    }

    private void    dump(byte[] b, int off, int len) {
        synchronized (buffer) {
            dumpInternal(b, off, len);
        }
    }

    /**
     * Same as calling {@link #dump(byte[], int, int)} two times but a little bit more effective.
     */
    private void    dumpTwoArrays(byte[] b1, int off1, int len1, byte[] b2, int off2, int len2) {
        synchronized (buffer) {
            dumpInternal(b1, off1, len1);
            dumpInternal(b2, off2, len2);
        }
    }

    private void dumpInternal(byte[] b, int off, int len) {
        // assert Thread.holdsLock(buffer);
        int overflow = buffer.size() + len - buffer.capacity();
        if (overflow > 0) {
            int incrementsToAdd = divideRoundUp(overflow, INCREMENT);
            buffer.addCapacity(INCREMENT * incrementsToAdd);
        }

        buffer.offer(b, off, len);
    }

    static int divideRoundUp(int val, int divisor) {
        int result = val / divisor;
        if (val > result * divisor) {
            result += 1;
        }
        return result;
    }

    private void    dump(int b) {
        synchronized (buffer) {
            if (buffer.size() + 1 > buffer.capacity())
                buffer.addCapacity(INCREMENT);

            buffer.offer(b);
        }
    }

    @Override
    public void     flush() throws IOException {
        try {
            synchronized (out) {
                out.flush();
            }
        } catch (IOException e) {
            broken = true;
            throw e;
        }
    }

    @Override
    public void     close() throws IOException {
        synchronized (out) {
            out.close();
        }
    }

    /*
        Returns number of bytes written to stream
     */
    public long             getBytesWritten() {
        return confirmed + buffer.size();
    }

    public void     confirm(long size) {

        synchronized (buffer) {
            int length = (int) (size - confirmed);
            int bufferSize = buffer.size();
            if (length > bufferSize) {
                bufferDebt = length - bufferSize;
                length = bufferSize;
            } else {
                bufferDebt = 0;
            }

            // buffer is not synchronized with out, so
            // confirmed bytes signal maybe processed before dump() occurred
            if (length > 0) {
                buffer.skip(length);
                confirmed += length;
            }
        }
    }

    boolean hasUnconfirmedData() {
        return buffer.size() - bufferDebt > 0;
    }

    public void     writeTo(VSocketOutputStream out) throws IOException {
        synchronized (buffer) {
            if (bufferDebt > buffer.size()) {
                throw new IllegalStateException("We confirmed more than we have in buffer");
            } else {
                buffer.skip(bufferDebt);
            }
            if (VSProtocol.LOGGER.isLoggable(Level.FINE)) {
                VSProtocol.LOGGER.fine("Sending output buffer of " + out.socketIdStr + " with size " + buffer.size());
            }
            buffer.poll(out);
        }
    }

    public boolean isBroken() {
        return broken;
    }

    @Override
    public String toString() {
        return getClass().getName() + socketIdStr;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy