/* * Copyright 2015 Terracotta, Inc., a Software AG company. * * 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.terracotta.offheapstore.paging; import org.junit.Assert; import org.junit.Test; import org.terracotta.offheapstore.WriteLockedOffHeapClockCache; import org.terracotta.offheapstore.buffersource.HeapBufferSource; import org.terracotta.offheapstore.buffersource.OffHeapBufferSource; import org.terracotta.offheapstore.storage.IntegerStorageEngine; import org.terracotta.offheapstore.storage.OffHeapBufferHalfStorageEngine; import org.terracotta.offheapstore.storage.SplitStorageEngine; import org.terracotta.offheapstore.storage.portability.ByteArrayPortability; import org.terracotta.offheapstore.util.PointerSizeParameterizedTest; import java.nio.ByteBuffer; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import static org.hamcrest.collection.IsArrayWithSize.arrayWithSize; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; public class OffHeapStorageAreaTest extends PointerSizeParameterizedTest { @Test public void testNonStraddlingReadBuffersReturnsSingleBuffer() { PageSource pageSource = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea osa = new OffHeapStorageArea(getPointerSize(), null, pageSource, 1024, false, false); assertThat(osa.readBuffers(osa.allocate(64), 64), arrayWithSize(1)); } @Test public void testNonStraddlingReadBuffersReturnsReadOnlyBuffer() { PageSource pageSource = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea osa = new OffHeapStorageArea(getPointerSize(), null, pageSource, 1024, false, false); assertThat(osa.readBuffers(osa.allocate(64), 64)[0].isReadOnly(), is(true)); } @Test public void testStraddlingReadBuffersReturnsMultipleBuffers() { PageSource pageSource = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea osa = new OffHeapStorageArea(getPointerSize(), null, pageSource, 128, false, false); assertThat(osa.readBuffers(osa.allocate(256), 256), arrayWithSize(3)); } @Test public void testStraddlingReadBuffersReturnsReadOnlyBuffers() { PageSource pageSource = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea osa = new OffHeapStorageArea(getPointerSize(), null, pageSource, 128, false, false); for (ByteBuffer buffer : osa.readBuffers(osa.allocate(256), 256)) { assertThat(buffer.isReadOnly(), is(true)); } } @Test public void testReadBuffersReturnsCorrectData() { PageSource pageSource = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea osa = new OffHeapStorageArea(getPointerSize(), null, pageSource, 128, false, false); long base = osa.allocate(1024); for (int i = 0; i < 1024; i++) { osa.writeByte(base + i, (byte) i); } for (int i = 1; i < 512; i <<= 1) { for (int o = -1; o <= 1; o++) { ByteBuffer[] buffers = osa.readBuffers(base, i + o); int j = 0; for (ByteBuffer buffer : buffers) { while (buffer.hasRemaining()) { assertThat(buffer.get(), is((byte) j)); j++; } } assertThat(j, is(i + o)); } } } @Test public void testNonStraddlingReadBufferReturnsReadOnlyBuffer() { PageSource pageSource = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea osa = new OffHeapStorageArea(getPointerSize(), null, pageSource, 1024, false, false); assertThat(osa.readBuffer(osa.allocate(64), 64).isReadOnly(), is(true)); } @Test public void testStraddlingReadBufferReturnsReadOnlyBuffer() { PageSource pageSource = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea osa = new OffHeapStorageArea(getPointerSize(), null, pageSource, 128, false, false); assertThat(osa.readBuffer(osa.allocate(256), 256).isReadOnly(), is(true)); } @Test public void testReadBufferReturnsCorrectData() { PageSource pageSource = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea osa = new OffHeapStorageArea(getPointerSize(), null, pageSource, 128, false, false); long base = osa.allocate(1024); for (int i = 0; i < 1024; i++) { osa.writeByte(base + i, (byte) i); } for (int i = 1; i < 512; i <<= 1) { for (int o = -1; o <= 1; o++) { ByteBuffer buffer = osa.readBuffer(base, i + o); int j = 0; while (buffer.hasRemaining()) { assertThat(buffer.get(), is((byte) j)); j++; } assertThat(j, is(i + o)); } } } @Test public void testRecoveryOfPages() { GettablePageSource source = new GettablePageSource(); Map<Integer, byte[]> test = new WriteLockedOffHeapClockCache<Integer, byte[]>(new UnlimitedPageSource(new OffHeapBufferSource()), new SplitStorageEngine<Integer, byte[]>(new IntegerStorageEngine(), new OffHeapBufferHalfStorageEngine<byte[]>(source, 1024, ByteArrayPortability.INSTANCE))); int put = 0; while (source.allocated.size() < 2) { test.put(put++, new byte[128]); } Assert.assertEquals(put, test.size()); source.release(); Assert.assertEquals(put - 1, test.size()); Assert.assertNull(test.get(put - 1)); for (int i = 0; i < put - 1; i++) { Assert.assertNotNull(test.get(i)); } source.release(); Assert.assertTrue(test.isEmpty()); for (int i = 0; i < put; i++) { Assert.assertNull(test.get(i)); } } @Test public void testVariablePageSize() { PageSource source = new UnlimitedPageSource(new HeapBufferSource()); OffHeapStorageArea storage = new OffHeapStorageArea(getPointerSize(), null, source, 1, 1024, false, false); Map<Integer, Long> locations = new HashMap<Integer, Long>(); for (int i = 0; i < 2048; i++) { long pointer = storage.allocate(Integer.SIZE / Byte.SIZE); storage.writeInt(pointer, i); locations.put(i, pointer); } System.err.println(storage); for (int i = 0; i < 2048; i++) { int pointer = locations.get(i).intValue(); Assert.assertEquals(i, storage.readInt(pointer)); } for (Long pointer : locations.values()) { storage.free(pointer); } } // @Test // public void testVariablePageSizeAddressLogic() { // PageSource source = new UnlimitedPageSource(new HeapBufferSource()); // // OffHeapStorageArea storage = new OffHeapStorageArea(null, source, 1, 1024, false, false); // // for (int i = 0, address = 0; i < 100; i++) { // int size = storage.pageSizeFor(i); // int base = storage.addressForPage(i); // Assert.assertTrue(size <= 1024); // Assert.assertEquals(address, base); // address += size; //// System.err.println("Page : " + i); //// System.err.println("Size : " + size); //// System.err.println("Base : " + base); //// System.err.println(); // } // // for (int i = 0; i < 10240; i++) { // int page = storage.pageIndexFor(i); // int pageAddress = storage.pageAddressFor(i); // Assert.assertEquals(i, pageAddress + storage.addressForPage(page)); //// System.err.println("Address : " + i); //// System.err.println("Page Index : " + page); //// System.err.println("Page Address : " + pageAddress); //// System.err.println(); // } // } static class GettablePageSource implements PageSource { final Random rndm = new Random(); final PageSource delegate = new UnlimitedPageSource(new OffHeapBufferSource()); final List<Page> allocated = new LinkedList<Page>(); @Override public Page allocate(int size, boolean thief, boolean victim, OffHeapStorageArea owner) { Page p = delegate.allocate(size, thief, victim, owner); allocated.add(p); return p; } @Override public void free(Page page) { delegate.free(page); allocated.remove(page); } public void release() { Page p = allocated.get(rndm.nextInt(allocated.size())); p.binding().release(new LinkedList<Page>(Collections.singleton(p))); free(p); } } }