/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.commons.compress.utils;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.SeekableByteChannel;
import java.util.Arrays;

/**
 * A {@link SeekableByteChannel} implementation that wraps a byte[].
 * @since 1.13
 */
public class SeekableInMemoryByteChannel implements SeekableByteChannel {

    private volatile byte[] data;
    private volatile boolean closed;
    private volatile int position, size;

    public SeekableInMemoryByteChannel(byte[] data) {
        this.data = data;
        size = data.length;
    }

    public SeekableInMemoryByteChannel() {
        this(new byte[0]);
    }

    @Override
    public long position() {
        return position;
    }

    @Override
    public SeekableByteChannel position(long newPosition) {
        if (newPosition > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("Position cannot exceed " + Integer.MAX_VALUE);
        }
        position = (int) newPosition;
        return this;
    }

    @Override
    public long size() {
        return size;
    }

    @Override
    public SeekableByteChannel truncate(long newSize) {
        if (size > newSize) {
            size = (int) newSize;
        }
        if (position > size) {
            position = size;
        }
        return this;
    }

    @Override
    public int read(ByteBuffer buf) throws IOException {
        if (!isOpen()) {
            throw new ClosedChannelException();
        }
        int pos = position;
        int sz = size;
        int wanted = buf.remaining();
        int possible = sz - pos;
        if (wanted > possible) {
            wanted = possible;
        }
        buf.put(data, pos, wanted);
        position = pos + wanted;
        return wanted;
    }

    @Override
    public void close() {
        closed = true;
    }

    @Override
    public boolean isOpen() {
        return !closed;
    }

    @Override
    public int write(ByteBuffer b) throws IOException {
        if (!isOpen()) {
            throw new ClosedChannelException();
        }
        int pos = position;
        int sz = data.length;
        int wanted = b.remaining();
        int possibleWithoutResize = sz - pos;
        if (wanted > possibleWithoutResize) {
            resize(pos + wanted);
        }
        b.get(data, pos, wanted);
        position = pos + wanted;
        if (size < position) {
            size = position;
        }
        return wanted;
    }

    /**
     * Obtains the array backing this channel.
     */
    public byte[] array() {
        return data;
    }

    private void resize(int newLength) {
        int len = data.length;
        if (len <= 0) {
            len = 1;
        }
        while (len < newLength) {
            len <<= 1;
        }
        data = Arrays.copyOf(data, len);
    }

}

@@ -1070,16 +1070,66 @@
 .Arrays;
+%0Aimport java.util.concurrent.atomic.AtomicBoolean;
 %0A%0A/**%0A *
@@ -1210,16 +1210,34 @@
 ce 1.13%0A
+ * @NotThreadSafe%0A
  */%0Apubl
@@ -1319,25 +1319,16 @@
 private 
-volatile 
 byte%5B%5D d
@@ -1348,31 +1348,56 @@
 ate 
-volatile boolean closed
+final AtomicBoolean closed = new AtomicBoolean()
 ;%0A  
@@ -1410,17 +1410,8 @@
 ate 
-volatile 
 int 
@@ -2453,59 +2453,8 @@
   %7D%0A
-        int pos = position;%0A        int sz = size;%0A
     
@@ -2507,31 +2507,38 @@
 possible = s
-z
+ize
  - pos
+ition
 ;%0A        if
@@ -2618,32 +2618,37 @@
 uf.put(data, pos
+ition
 , wanted);%0A     
@@ -2651,39 +2651,34 @@
        position 
-= pos 
 +
+=
  wanted;%0A       
@@ -2758,15 +2758,18 @@
 osed
- = 
+.set(
 true
+)
 ;%0A  
@@ -2839,16 +2839,22 @@
  !closed
+.get()
 ;%0A    %7D%0A
@@ -3011,66 +3011,8 @@
   %7D%0A
-        int pos = position;%0A        int sz = data.length;%0A
     
@@ -3084,15 +3084,22 @@
  = s
-z
+ize
  - pos
+ition
 ;%0A  
@@ -3164,16 +3164,21 @@
 size(pos
+ition
  + wante
@@ -3214,16 +3214,21 @@
 ata, pos
+ition
 , wanted
@@ -3251,15 +3251,10 @@
 ion 
-= pos 
 +
+=
  wan

modify SeekableInMemoryByteChannel based on feedback by Maciej and Bernd Eckenfels