/* * 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"); } } }