/*
 * Copyright (C) 2017-2019 Dremio Corporation
 *
 * 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 org.apache.hadoop.fs;

import static org.junit.Assert.assertEquals;

import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;

import org.apache.hadoop.io.ByteBufferPool;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * Test for the locally fixed ByteBufferUtil
 */
public class TestByteBufferUtil {
  private static final int TEST_DATA_SIZE = ByteBufferUtil.MAX_BUFFERED_READ * 7 / 3;

  private static byte[] testData = new byte[TEST_DATA_SIZE];

  @BeforeClass
  public static void populateData() {
    for (int i = 0; i < TEST_DATA_SIZE; i++) {
      testData[i] = (byte)(i & 0x7f);
    }
  }

  public class LimitedByteArrayInputStream extends ByteArrayInputStream {
    private final int limit;

    public LimitedByteArrayInputStream(byte buf[], int limit) {
      super(buf);
      this.limit = limit;
    }

    @Override
    public int read(byte b[], int off, int len) {
      return super.read(b, off, Math.min(len, limit));
    }
  }

  private void testReadHelper(final int readSize, final int readLimit) throws Exception {
    ByteArrayInputStream inputStream = new LimitedByteArrayInputStream(testData, readLimit);
    ByteBufferPool adapterPool = new TestByteBufferPool();
    int currOffset = 0;
    while (currOffset < TEST_DATA_SIZE) {
      ByteBuffer byteBuffer = ByteBufferUtil.fallbackRead(inputStream, adapterPool, readSize);
      final int length = byteBuffer.remaining();
      for (int i = 0; i < length; i++) {
        assertEquals(testData[currOffset + i], byteBuffer.get());
      }
      adapterPool.putBuffer(byteBuffer);
      currOffset += length;
    }
  }

  @Test
  public void testReadManySmall() throws Exception {
    final int smallReadSize = 17;
    testReadHelper(smallReadSize, smallReadSize);
  }

  @Test
  public void testReadOneLarge() throws Exception {
    testReadHelper(TEST_DATA_SIZE, TEST_DATA_SIZE);
  }

  @Test
  public void testReadChunkedLarge() throws Exception {
    // Single read that will internally be served as several small reads
    testReadHelper(TEST_DATA_SIZE, TEST_DATA_SIZE * 7 / 117);
  }

  private static class TestByteBufferPool implements ByteBufferPool {

    @Override
    public ByteBuffer getBuffer(boolean direct, int length) {
      return direct ? ByteBuffer.allocateDirect(length) : ByteBuffer.allocate(length);
    }

    @Override
    public void putBuffer(ByteBuffer buffer) {
      // NB: Garbage collection frees the underlying buffer memory
    }
  }
}