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

org.xnio.conduits.PushBackStreamSourceConduit Maven / Gradle / Ivy

There is a newer version: 3.8.16.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 *
 * Copyright 2013 Red Hat, Inc. and/or its affiliates.
 *
 * 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 org.xnio.conduits;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.TimeUnit;
import org.xnio.Buffers;
import org.xnio.Pooled;
import org.xnio.channels.StreamSinkChannel;

/**
 * A stream source conduit which allows buffers to be "pushed back" to the head of the stream.
 *
 * @author David M. Lloyd
 */
public final class PushBackStreamSourceConduit extends AbstractStreamSourceConduit implements StreamSourceConduit {
    private StreamSourceConduit current = next;
    private boolean shutdown;

    /**
     * Construct a new instance.
     *
     * @param next the delegate conduit to set
     */
    public PushBackStreamSourceConduit(final StreamSourceConduit next) {
        super(next);
    }

    public void resumeReads() {
        current.resumeReads();
    }

    public int read(final ByteBuffer dst) throws IOException {
        return current.read(dst);
    }

    public long read(final ByteBuffer[] dsts, final int offs, final int len) throws IOException {
        return current.read(dsts, offs, len);
    }

    public long transferTo(final long position, final long count, final FileChannel target) throws IOException {
        return current.transferTo(position, count, target);
    }

    public long transferTo(final long count, final ByteBuffer throughBuffer, final StreamSinkChannel target) throws IOException {
        return current.transferTo(count, throughBuffer, target);
    }

    public void awaitReadable() throws IOException {
        current.awaitReadable();
    }

    public void awaitReadable(final long time, final TimeUnit timeUnit) throws IOException {
        current.awaitReadable(time, timeUnit);
    }

    public void terminateReads() throws IOException {
        shutdown = true;
        current.terminateReads();
    }

    public void setReadReadyHandler(final ReadReadyHandler handler) {
        current.setReadReadyHandler(handler);
    }

    /**
     * Push a buffer back to the head of the stream.  Once the buffer data is consumed, it will
     * be released back to its original pool (if any).
     *
     * @param pooledBuffer the buffer to push back
     */
    public void pushBack(Pooled pooledBuffer) {
        if (pooledBuffer == null) {
            return;
        }
        if (shutdown || ! pooledBuffer.getResource().hasRemaining()) {
            pooledBuffer.free();
        } else {
            current = new BufferConduit(current, pooledBuffer);
        }
    }

    class BufferConduit extends AbstractStreamSourceConduit implements StreamSourceConduit {

        private final Pooled pooledBuffer;

        BufferConduit(final StreamSourceConduit next, final Pooled pooledBuffer) {
            super(next);
            this.pooledBuffer = pooledBuffer;
        }

        public void resumeReads() {
            next.wakeupReads();
        }

        public void terminateReads() throws IOException {
            try {
                super.terminateReads();
            } finally {
                if (pooledBuffer != null) {
                    pooledBuffer.free();
                }
                next.terminateReads();
            }
        }

        public void awaitReadable(final long time, final TimeUnit timeUnit) throws IOException {
            // readable
        }

        public void awaitReadable() throws IOException {
            // readable
        }

        public int read(final ByteBuffer dst) throws IOException {
            int cnt;
            if (! dst.hasRemaining()) {
                return 0;
            }
            final StreamSourceConduit next = this.next;
            try {
                final ByteBuffer src = pooledBuffer.getResource();
                cnt = Buffers.copy(dst, src);
                if (src.hasRemaining()) {
                    return cnt;
                }
                moveToNext();
                if (cnt > 0 && next == PushBackStreamSourceConduit.this.next) {
                    // don't hit the main channel until the user wants to
                    return cnt;
                }
            } catch (IllegalStateException ignored) {
                moveToNext();
                cnt = 0;
            }
            final int res = next.read(dst);
            return res > 0 ? res + cnt : cnt > 0 ? cnt : res;
        }

        public long read(final ByteBuffer[] dsts, final int offs, final int len) throws IOException {
            long cnt;
            final StreamSourceConduit next = this.next;
            try {
                final ByteBuffer src = pooledBuffer.getResource();
                cnt = Buffers.copy(dsts, offs, len, src);
                if (src.hasRemaining()) {
                    return cnt;
                }
                moveToNext();
                if (cnt > 0L && next == PushBackStreamSourceConduit.this.next) {
                    // don't hit the main channel until the user wants to
                    return cnt;
                }
            } catch (IllegalStateException ignored) {
                moveToNext();
                cnt = 0;
            }
            final long res = next.read(dsts, offs, len);
            return res > 0 ? res + cnt : cnt > 0 ? cnt : res;
        }

        public long transferTo(long position, long count, final FileChannel target) throws IOException {
            long cnt;
            final ByteBuffer src;
            try {
                src = pooledBuffer.getResource();
                final int pos = src.position();
                final int rem = src.remaining();
                if (rem > count) try {
                    // partial empty of our buffer
                    src.limit(pos + (int) count);
                    return target.write(src, position);
                } finally {
                    src.limit(pos + rem);
                } else {
                    // full empty of our buffer
                    cnt = target.write(src, position);
                    if (cnt == rem) {
                        // we emptied our buffer
                        moveToNext();
                    } else {
                        return cnt;
                    }
                    position += cnt;
                    count -= cnt;
                }
            } catch (IllegalStateException ignored) {
                moveToNext();
                cnt = 0L;
            }
            return cnt + next.transferTo(position, count, target);
        }

        public long transferTo(final long count, final ByteBuffer throughBuffer, final StreamSinkChannel target) throws IOException {
            long cnt;
            final ByteBuffer src;
            try {
                src = pooledBuffer.getResource();
                final int pos = src.position();
                final int rem = src.remaining();
                if (rem > count) try {
                    // partial empty of our buffer
                    src.limit(pos + (int) count);
                    int res = target.write(src);
                    if(res == 0) {
                        //a bit yuck, but if we have filed to copy anything we need to transfer data into the throughbuffer
                        //this signals to the called that it was not the read that did not succeed, but the write
                        throughBuffer.clear();
                        Buffers.copy(throughBuffer, src);
                        throughBuffer.flip();
                    } else {
                        //make sure throughbuffer is empty
                        throughBuffer.clear();
                        throughBuffer.flip();
                    }
                    return res;
                } finally {
                    src.limit(pos + rem);
                } else {
                    // full empty of our buffer
                    cnt = target.write(src);
                    if (cnt == rem) {
                        // we emptied our buffer
                        moveToNext();
                    } else {
                        if (cnt == 0) {
                            //a bit yuck, but if we have filed to copy anything we need to transfer data into the throughbuffer
                            //this signals to the called that it was not the read that did not succeed, but the write
                            throughBuffer.clear();
                            Buffers.copy(throughBuffer, src);
                            throughBuffer.flip();
                        } else {
                            //make sure throughbuffer is empty
                            throughBuffer.clear();
                            throughBuffer.flip();
                        }
                        return cnt;
                    }
                }
            } catch (IllegalStateException ignored) {
                moveToNext();
                cnt = 0L;
            }
            final long res = next.transferTo(count - cnt, throughBuffer, target);
            return res > 0L ? cnt + res : cnt > 0L ? cnt : res;
        }

        private final void moveToNext() {
            current = next;
            pooledBuffer.free();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy