jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java
author prappo
Mon, 09 May 2016 23:33:09 +0100
changeset 37874 02589df0999a
child 39730 196f4e25d9f5
permissions -rw-r--r--
8087113: Websocket API and implementation Reviewed-by: chegar

/*
 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General  License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 GNU General  License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General  License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.net.http;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

//
//  +-----------+---------------+------------ ~ ------+
//  |  shared#1 |    shared#2   | non-shared          |
//  +-----------+---------------+------------ ~ ------+
//  |                                                 |
//  |<------------------  shared0  ---------- ~ ----->|
//
//
// Objects of the type are not thread-safe. It's the responsibility of the
// client to access shared buffers safely between threads.
//
// It would be perfect if we could extend java.nio.Buffer, but it's not an
// option since Buffer and all its descendants have package-private
// constructors.
//
abstract class WSShared<T extends Buffer> implements WSDisposable {

    protected final AtomicBoolean disposed = new AtomicBoolean();
    protected final T buffer;

    protected WSShared(T buffer) {
        this.buffer = Objects.requireNonNull(buffer);
    }

    static <T extends Buffer> WSShared<T> wrap(T buffer) {
        return new WSShared<>(buffer) {
            @Override
            WSShared<T> share(int pos, int limit) {
                throw new UnsupportedOperationException();
            }
        };
    }

    // TODO: should be a terminal operation as after it returns the buffer might
    //       have escaped (we can't protect it any more)
    public T buffer() {
        checkDisposed();
        return buffer;
    }

    abstract WSShared<T> share(final int pos, final int limit);

    WSShared<T> select(final int pos, final int limit) {
        checkRegion(pos, limit, buffer());
        select(pos, limit, buffer());
        return this;
    }

    @Override
    public void dispose() {
        if (!disposed.compareAndSet(false, true)) {
            throw new IllegalStateException("Has been disposed previously");
        }
    }

    int limit() {
        return buffer().limit();
    }

    WSShared<T> limit(int newLimit) {
        buffer().limit(newLimit);
        return this;
    }

    int position() {
        return buffer().position();
    }

    WSShared<T> position(int newPosition) {
        buffer().position(newPosition);
        return this;
    }

    int remaining() {
        return buffer().remaining();
    }

    boolean hasRemaining() {
        return buffer().hasRemaining();
    }

    WSShared<T> flip() {
        buffer().flip();
        return this;
    }

    WSShared<T> rewind() {
        buffer().rewind();
        return this;
    }

    WSShared<T> put(WSShared<? extends T> src) {
        put(this.buffer(), src.buffer());
        return this;
    }

    static void checkRegion(int position, int limit, Buffer buffer) {
        if (position < 0 || position > buffer.capacity()) {
            throw new IllegalArgumentException("position: " + position);
        }
        if (limit < 0 || limit > buffer.capacity()) {
            throw new IllegalArgumentException("limit: " + limit);
        }
        if (limit < position) {
            throw new IllegalArgumentException
                    ("limit < position: limit=" + limit + ", position=" + position);
        }
    }

    void select(int newPos, int newLim, Buffer buffer) {
        int oldPos = buffer.position();
        int oldLim = buffer.limit();
        assert 0 <= oldPos && oldPos <= oldLim && oldLim <= buffer.capacity();
        if (oldLim <= newPos) {
            buffer().limit(newLim).position(newPos);
        } else {
            buffer.position(newPos).limit(newLim);
        }
    }

    // The same as dst.put(src)
    static <T extends Buffer> T put(T dst, T src) {
        if (dst instanceof ByteBuffer) {
            ((ByteBuffer) dst).put((ByteBuffer) src);
        } else if (dst instanceof CharBuffer) {
            ((CharBuffer) dst).put((CharBuffer) src);
        } else {
            // We don't work with buffers of other types
            throw new IllegalArgumentException();
        }
        return dst;
    }

    // TODO: Remove when JDK-8150785 has been done
    @SuppressWarnings("unchecked")
    static <T extends Buffer> T slice(T buffer) {
        if (buffer instanceof ByteBuffer) {
            return (T) ((ByteBuffer) buffer).slice();
        } else if (buffer instanceof CharBuffer) {
            return (T) ((CharBuffer) buffer).slice();
        } else {
            // We don't work with buffers of other types
            throw new IllegalArgumentException();
        }
    }

    // TODO: Remove when JDK-8150785 has been done
    @SuppressWarnings("unchecked")
    static <T extends Buffer> T duplicate(T buffer) {
        if (buffer instanceof ByteBuffer) {
            return (T) ((ByteBuffer) buffer).duplicate();
        } else if (buffer instanceof CharBuffer) {
            return (T) ((CharBuffer) buffer).duplicate();
        } else {
            // We don't work with buffers of other types
            throw new IllegalArgumentException();
        }
    }

    @Override
    public String toString() {
        return super.toString() + "[" + WSUtils.toString(buffer()) + "]";
    }

    private void checkDisposed() {
        if (disposed.get()) {
            throw new IllegalStateException("Has been disposed previously");
        }
    }
}