--- /dev/null 2016-04-22 22:42:07.067228853 +0100 +++ new/src/java.httpclient/share/classes/java/net/http/Shared.java 2016-04-25 23:11:06.253374165 +0100 @@ -0,0 +1,202 @@ +/* + * 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 Shared implements Disposable { + + protected final AtomicBoolean disposed = new AtomicBoolean(); + protected final T buffer; + + protected Shared(T buffer) { + this.buffer = Objects.requireNonNull(buffer); + } + + static Shared wrap(T buffer) { + return new Shared<>(buffer) { + @Override + Shared 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 Shared share(final int pos, final int limit); + + Shared 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(); + } + + Shared limit(int newLimit) { + buffer().limit(newLimit); + return this; + } + + int position() { + return buffer().position(); + } + + Shared position(int newPosition) { + buffer().position(newPosition); + return this; + } + + int remaining() { + return buffer().remaining(); + } + + boolean hasRemaining() { + return buffer().hasRemaining(); + } + + Shared flip() { + buffer().flip(); + return this; + } + + Shared rewind() { + buffer().rewind(); + return this; + } + + Shared put(Shared 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 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 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 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() + "[" + Utils.toString(buffer()) + "]"; + } + + private void checkDisposed() { + if (disposed.get()) { + throw new IllegalStateException("Has been disposed previously"); + } + } +}