// Copyright 2017 JanusGraph Authors // // 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.janusgraph.diskstorage.keycolumnvalue; import com.google.common.base.Function; import com.google.common.collect.Iterables; import org.janusgraph.diskstorage.*; import org.janusgraph.diskstorage.util.StaticArrayBuffer; import org.janusgraph.diskstorage.util.StaticArrayEntry; import org.janusgraph.diskstorage.util.StaticArrayEntryList; import org.janusgraph.diskstorage.util.WriteByteBuffer; import org.janusgraph.graphdb.relations.RelationCache; import org.apache.tinkerpop.gremlin.structure.Direction; import org.junit.Test; import javax.annotation.Nullable; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import static org.junit.Assert.*; /** * @author Matthias Broecheler (me@matthiasb.com) */ public class StaticArrayEntryTest { private static final RelationCache cache = new RelationCache(Direction.OUT,5,105,"Hello"); private static final EntryMetaData[] metaSchema = { EntryMetaData.TIMESTAMP, EntryMetaData.TTL, EntryMetaData.VISIBILITY}; private static final Map<EntryMetaData,Object> metaData = new EntryMetaData.Map() {{ put(EntryMetaData.TIMESTAMP,Long.valueOf(101)); put(EntryMetaData.TTL, 42); put(EntryMetaData.VISIBILITY,"SOS/K5a-89 SOS/sdf3"); }}; @Test public void testArrayBuffer() { WriteBuffer wb = new WriteByteBuffer(128); wb.putInt(1).putInt(2).putInt(3).putInt(4); int valuePos = wb.getPosition(); wb.putInt(5).putInt(6); Entry entry = new StaticArrayEntry(wb.getStaticBuffer(),valuePos); assertEquals(4*4,entry.getValuePosition()); assertEquals(6*4,entry.length()); assertTrue(entry.hasValue()); for (int i=1;i<=6;i++) assertEquals(i,entry.getInt((i-1)*4)); ReadBuffer rb = entry.asReadBuffer(); for (int i=1;i<=6;i++) assertEquals(i,rb.getInt()); assertFalse(rb.hasRemaining()); assertNull(entry.getCache()); entry.setCache(cache); assertEquals(cache,entry.getCache()); rb = entry.getColumnAs(StaticBuffer.STATIC_FACTORY).asReadBuffer(); for (int i=1;i<=4;i++) assertEquals(i,rb.getInt()); assertFalse(rb.hasRemaining()); rb = entry.getValueAs(StaticBuffer.STATIC_FACTORY).asReadBuffer(); for (int i=5;i<=6;i++) assertEquals(i,rb.getInt()); assertFalse(rb.hasRemaining()); } @Test public void testReadWrite() { WriteBuffer b = new WriteByteBuffer(10); for (int i=1;i<4;i++) b.putByte((byte) i); for (int i=1;i<4;i++) b.putShort((short) i); for (int i=1;i<4;i++) b.putInt(i); for (int i=1;i<4;i++) b.putLong(i); for (int i=1;i<4;i++) b.putFloat(i); for (int i=1;i<4;i++) b.putDouble(i); for (int i=101;i<104;i++) b.putChar((char) i); ReadBuffer r = b.getStaticBuffer().asReadBuffer(); assertEquals(1,r.getByte()); assertTrue(Arrays.equals(new byte[]{2,3},r.getBytes(2))); assertEquals(1,r.getShort()); assertTrue(Arrays.equals(new short[]{2,3},r.getShorts(2))); assertEquals(1,r.getInt()); assertEquals(2,r.getInt()); assertTrue(Arrays.equals(new int[]{3},r.getInts(1))); assertEquals(1,r.getLong()); assertTrue(Arrays.equals(new long[]{2,3},r.getLongs(2))); assertEquals(1.0,r.getFloat(),0.00001); assertTrue(Arrays.equals(new float[]{2.0f,3.0f},r.getFloats(2))); assertEquals(1,r.getDouble(),0.0001); assertTrue(Arrays.equals(new double[]{2.0,3.0},r.getDoubles(2))); assertEquals((char)101,r.getChar()); assertEquals((char)102,r.getChar()); assertTrue(Arrays.equals(new char[]{(char)103},r.getChars(1))); } @Test public void testInversion() { WriteBuffer wb = new WriteByteBuffer(20); wb.putInt(1).putInt(2).putInt(3).putInt(4); Entry entry = new StaticArrayEntry(wb.getStaticBufferFlipBytes(4,2*4),3*4); ReadBuffer rb = entry.asReadBuffer(); assertEquals(1, rb.getInt()); assertEquals(2,rb.subrange(4,true).getInt()); assertEquals(~2, rb.getInt()); assertEquals(3, rb.getInt()); assertEquals(4,rb.getInt()); rb.movePositionTo(entry.getValuePosition()); assertEquals(4,rb.getInt()); } @Test public void testEntryList() { Map<Integer,Long> entries = new HashMap<Integer,Long>(); for (int i=0;i<50;i++) entries.put(i*2+7,Math.round(Math.random()/2*Long.MAX_VALUE)); EntryList[] el = new EntryList[7]; el[0] = StaticArrayEntryList.ofBytes(entries.entrySet(), ByteEntryGetter.INSTANCE); el[1] = StaticArrayEntryList.ofByteBuffer(entries.entrySet(), BBEntryGetter.INSTANCE); el[2] = StaticArrayEntryList.ofStaticBuffer(entries.entrySet(), StaticEntryGetter.INSTANCE); el[3] = StaticArrayEntryList.ofByteBuffer(entries.entrySet().iterator(), BBEntryGetter.INSTANCE); el[4] = StaticArrayEntryList.ofStaticBuffer(entries.entrySet().iterator(), StaticEntryGetter.INSTANCE); el[5] = StaticArrayEntryList.of(Iterables.transform(entries.entrySet(),new Function<Map.Entry<Integer, Long>, Entry>() { @Nullable @Override public Entry apply(@Nullable Map.Entry<Integer, Long> entry) { return StaticArrayEntry.ofByteBuffer(entry, BBEntryGetter.INSTANCE); } })); el[6] = StaticArrayEntryList.of(Iterables.transform(entries.entrySet(),new Function<Map.Entry<Integer, Long>, Entry>() { @Nullable @Override public Entry apply(@Nullable Map.Entry<Integer, Long> entry) { return StaticArrayEntry.ofBytes(entry, ByteEntryGetter.INSTANCE); } })); for (int i = 0; i < el.length; i++) { assertEquals(entries.size(),el[i].size()); int num=0; for (Entry e : el[i]) { checkEntry(e,entries); assertFalse(e.hasMetaData()); assertTrue(e.getMetaData().isEmpty()); assertNull(e.getCache()); e.setCache(cache); num++; } assertEquals(entries.size(),num); Iterator<Entry> iter = el[i].reuseIterator(); num=0; while (iter.hasNext()) { Entry e = iter.next(); checkEntry(e, entries); assertFalse(e.hasMetaData()); assertTrue(e.getMetaData().isEmpty()); assertEquals(cache,e.getCache()); num++; } assertEquals(entries.size(),num); } } /** * Copied from above - the only difference is using schema instances and checking the schema */ @Test public void testEntryListWithMetaSchema() { Map<Integer,Long> entries = new HashMap<Integer,Long>(); for (int i=0;i<50;i++) entries.put(i*2+7,Math.round(Math.random()/2*Long.MAX_VALUE)); EntryList[] el = new EntryList[7]; el[0] = StaticArrayEntryList.ofBytes(entries.entrySet(), ByteEntryGetter.SCHEMA_INSTANCE); el[1] = StaticArrayEntryList.ofByteBuffer(entries.entrySet(), BBEntryGetter.SCHEMA_INSTANCE); el[2] = StaticArrayEntryList.ofStaticBuffer(entries.entrySet(), StaticEntryGetter.SCHEMA_INSTANCE); el[3] = StaticArrayEntryList.ofByteBuffer(entries.entrySet().iterator(), BBEntryGetter.SCHEMA_INSTANCE); el[4] = StaticArrayEntryList.ofStaticBuffer(entries.entrySet().iterator(), StaticEntryGetter.SCHEMA_INSTANCE); el[5] = StaticArrayEntryList.of(Iterables.transform(entries.entrySet(),new Function<Map.Entry<Integer, Long>, Entry>() { @Nullable @Override public Entry apply(@Nullable Map.Entry<Integer, Long> entry) { return StaticArrayEntry.ofByteBuffer(entry, BBEntryGetter.SCHEMA_INSTANCE); } })); el[6] = StaticArrayEntryList.of(Iterables.transform(entries.entrySet(),new Function<Map.Entry<Integer, Long>, Entry>() { @Nullable @Override public Entry apply(@Nullable Map.Entry<Integer, Long> entry) { return StaticArrayEntry.ofBytes(entry, ByteEntryGetter.SCHEMA_INSTANCE); } })); for (int i = 0; i < el.length; i++) { //System.out.println("Iteration: " + i); assertEquals(entries.size(),el[i].size()); int num=0; for (Entry e : el[i]) { checkEntry(e,entries); assertTrue(e.hasMetaData()); assertFalse(e.getMetaData().isEmpty()); assertEquals(metaData,e.getMetaData()); assertNull(e.getCache()); e.setCache(cache); num++; } assertEquals(entries.size(),num); Iterator<Entry> iter = el[i].reuseIterator(); num=0; while (iter.hasNext()) { Entry e = iter.next(); assertTrue(e.hasMetaData()); assertFalse(e.getMetaData().isEmpty()); assertEquals(metaData,e.getMetaData()); assertEquals(cache,e.getCache()); checkEntry(e, entries); num++; } assertEquals(entries.size(),num); } } @Test public void testTTLMetadata() throws Exception { WriteBuffer wb = new WriteByteBuffer(128); wb.putInt(1).putInt(2).putInt(3).putInt(4); int valuePos = wb.getPosition(); wb.putInt(5).putInt(6); StaticArrayEntry entry = new StaticArrayEntry(wb.getStaticBuffer(),valuePos); entry.setMetaData(EntryMetaData.TTL, 42); assertEquals(42, entry.getMetaData().get(EntryMetaData.TTL)); } private static void checkEntry(Entry e, Map<Integer,Long> entries) { ReadBuffer rb = e.asReadBuffer(); int key = rb.getInt(); assertEquals(e.getValuePosition(),rb.getPosition()); assertTrue(e.hasValue()); long value = rb.getLong(); assertFalse(rb.hasRemaining()); assertEquals((long)entries.get(key),value); rb = e.getColumnAs(StaticBuffer.STATIC_FACTORY).asReadBuffer(); assertEquals(key,rb.getInt()); assertFalse(rb.hasRemaining()); rb = e.getValueAs(StaticBuffer.STATIC_FACTORY).asReadBuffer(); assertEquals(value,rb.getLong()); assertFalse(rb.hasRemaining()); } private static enum BBEntryGetter implements StaticArrayEntry.GetColVal<Map.Entry<Integer, Long>, ByteBuffer> { INSTANCE, SCHEMA_INSTANCE; @Override public ByteBuffer getColumn(Map.Entry<Integer, Long> element) { ByteBuffer b = ByteBuffer.allocate(4); b.putInt(element.getKey()).flip(); return b; } @Override public ByteBuffer getValue(Map.Entry<Integer, Long> element) { ByteBuffer b = ByteBuffer.allocate(8); b.putLong(element.getValue()).flip(); return b; } @Override public EntryMetaData[] getMetaSchema(Map.Entry<Integer, Long> element) { if (this==INSTANCE) return StaticArrayEntry.EMPTY_SCHEMA; else return metaSchema; } @Override public Object getMetaData(Map.Entry<Integer, Long> element, EntryMetaData meta) { if (this==INSTANCE) throw new UnsupportedOperationException("Unsupported meta data: " + meta); else return metaData.get(meta); } } private static enum ByteEntryGetter implements StaticArrayEntry.GetColVal<Map.Entry<Integer, Long>, byte[]> { INSTANCE, SCHEMA_INSTANCE; @Override public byte[] getColumn(Map.Entry<Integer, Long> element) { ByteBuffer b = ByteBuffer.allocate(4); b.putInt(element.getKey()); return b.array(); } @Override public byte[] getValue(Map.Entry<Integer, Long> element) { ByteBuffer b = ByteBuffer.allocate(8); b.putLong(element.getValue()); return b.array(); } @Override public EntryMetaData[] getMetaSchema(Map.Entry<Integer, Long> element) { if (this==INSTANCE) return StaticArrayEntry.EMPTY_SCHEMA; else return metaSchema; } @Override public Object getMetaData(Map.Entry<Integer, Long> element, EntryMetaData meta) { if (this==INSTANCE) throw new UnsupportedOperationException("Unsupported meta data: " + meta); else return metaData.get(meta); } } private static enum StaticEntryGetter implements StaticArrayEntry.GetColVal<Map.Entry<Integer, Long>, StaticBuffer> { INSTANCE, SCHEMA_INSTANCE; @Override public StaticBuffer getColumn(Map.Entry<Integer, Long> element) { ByteBuffer b = ByteBuffer.allocate(4); b.putInt(element.getKey()); return StaticArrayBuffer.of(b.array()); } @Override public StaticBuffer getValue(Map.Entry<Integer, Long> element) { ByteBuffer b = ByteBuffer.allocate(8); b.putLong(element.getValue()); return StaticArrayBuffer.of(b.array()); } @Override public EntryMetaData[] getMetaSchema(Map.Entry<Integer, Long> element) { if (this==INSTANCE) return StaticArrayEntry.EMPTY_SCHEMA; else return metaSchema; } @Override public Object getMetaData(Map.Entry<Integer, Long> element, EntryMetaData meta) { if (this==INSTANCE) throw new UnsupportedOperationException("Unsupported meta data: " + meta); else return metaData.get(meta); } } }