/**
 * Copyright 2010 - 2017 JetBrains s.r.o.
 *
 * 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 jetbrains.exodus.vfs;

import jetbrains.exodus.*;
import jetbrains.exodus.bindings.IntegerBinding;
import jetbrains.exodus.util.LightOutputStream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

class Cluster {

    @NotNull
    private final ByteIterable it;
    @Nullable
    private ByteIterator iterator;
    private long startingPosition;
    private long clusterNumber;
    private int size;

    Cluster(@NotNull final ByteIterable it) {
        this.it = it;
    }

    long getStartingPosition() {
        return startingPosition;
    }

    void setStartingPosition(final long startingPosition) {
        this.startingPosition = startingPosition;
    }

    long getClusterNumber() {
        return clusterNumber;
    }

    void setClusterNumber(final long clusterNumber) {
        this.clusterNumber = clusterNumber;
    }

    int getSize() {
        getIterator();
        return size;
    }

    boolean hasNext() {
        return getIterator().hasNext();
    }

    byte next() {
        final byte result = getIterator().next();
        --size;
        return result;
    }

    long skip(final long length) {
        final int size = getSize();
        final long skipped = length > size ? size : getIterator().skip(length);
        this.size -= (int) skipped;
        return skipped;
    }

    void copyTo(@NotNull final byte[] array) {
        int i = 0;
        while (hasNext()) {
            array[i++] = next();
        }
    }

    static ByteIterable writeCluster(@NotNull final byte[] cluster, final int size, final boolean accumulateInRAM) {
        if (accumulateInRAM) {
            final LightOutputStream output = new LightOutputStream(size + 5);
            IntegerBinding.writeCompressed(output, size);
            output.write(cluster, 0, size);
            return output.asArrayByteIterable();
        }
        final ByteIterable bi;
        try {
            final File file = File.createTempFile("~exodus-vfs-output-cluster", ".tmp");
            try (RandomAccessFile out = new RandomAccessFile(file, "rw")) {
                out.write(cluster, 0, size);
            }
            bi = new FileByteIterable(file);
            file.deleteOnExit();
        } catch (IOException e) {
            throw ExodusException.toExodusException(e);
        }
        return new CompoundByteIterable(new ByteIterable[]{IntegerBinding.intToCompressedEntry(size), bi});
    }

    @NotNull
    private ByteIterator getIterator() {
        if (iterator == null) {
            iterator = it.iterator();
            size = IntegerBinding.readCompressed(iterator);
        }
        return iterator;
    }
}

@@ -652,17 +652,157 @@
 .exodus.
-*
+ByteIterable;%0Aimport jetbrains.exodus.CompoundByteIterable;%0Aimport jetbrains.exodus.ExodusException;%0Aimport jetbrains.exodus.FileByteIterable
 ;%0Aimport
@@ -1156,29 +1156,46 @@
 ate 
-ByteIterator iterator
+byte%5B%5D bytes;%0A    private int position
 ;%0A  
@@ -1750,24 +1750,21 @@
      get
-Iterator
+Bytes
 ();%0A    
@@ -1833,28 +1833,18 @@
  get
-Iterator().hasNext()
+Size() %3E 0
 ;%0A  
@@ -1901,25 +1901,27 @@
  get
-Iterator().next()
+Bytes()%5Bposition++%5D
 ;%0A  
@@ -2049,20 +2049,19 @@
   final 
-long
+int
  skipped
@@ -2067,50 +2067,29 @@
 d = 
-length %3E size ? size : getIterator().skip(
+(int) Math.min(size, 
 leng
@@ -2114,21 +2114,44 @@
 size -= 
-(int)
+skipped;%0A        position +=
  skipped
@@ -2242,89 +2242,63 @@
     
-int i = 0;%0A        while (hasNext()) %7B%0A            array%5Bi++%5D = next();%0A        %7D
+System.arraycopy(getBytes(), position, array, 0, size);
 %0A   
@@ -3279,32 +3279,23 @@
 ate 
-ByteIterator getIterator
+byte%5B%5D getBytes
 () %7B
@@ -3307,24 +3307,21 @@
     if (
-iterator
+bytes
  == null
@@ -3340,30 +3340,33 @@
     
-iterator = it.iterator
+bytes = it.getBytesUnsafe
 ();%0A
@@ -3377,22 +3377,26 @@
         
-size =
+// inlined
  Integer
@@ -3421,18 +3421,293 @@
 ssed
-(iterator)
+%0A            final int firstByte = bytes%5B0%5D & 0xff;%0A            int size = firstByte & 0x1f;%0A            position = (firstByte %3E%3E 5) + 1;%0A            for (int i = 1; i %3C position; ++i) %7B%0A                size = (size %3C%3C 8) + (bytes%5Bi%5D & 0xff);%0A            %7D%0A            this.size = size
 ;%0A  
@@ -3733,16 +3733,13 @@
 urn 
-iterator
+bytes
 ;%0A  

use ByteIterable#getBytesUnsafe instead of iterator