// 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.graphdb.idmanagement; import com.google.common.base.Preconditions; import org.janusgraph.diskstorage.ReadBuffer; import org.janusgraph.diskstorage.StaticBuffer; import org.janusgraph.diskstorage.WriteBuffer; import org.janusgraph.diskstorage.util.WriteByteBuffer; import org.janusgraph.graphdb.database.idhandling.VariableLong; import org.apache.commons.lang.time.StopWatch; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Random; import static org.junit.Assert.assertEquals; /** * @author Matthias Broecheler (me@matthiasb.com) */ public class VariableLongTest { private static final Logger log = LoggerFactory.getLogger(VariableLongTest.class); private void readWriteTest(final ReadWriteLong impl, long maxValue, long jump, boolean negative, boolean backward) { Preconditions.checkArgument(maxValue%jump==0); long allocate = maxValue / jump * 8 * (negative?2:1); Preconditions.checkArgument(allocate < (1 << 28)); WriteBuffer wb = new WriteByteBuffer((int) allocate); int num = 0; StopWatch w = new StopWatch(); w.start(); for (long i = (negative?-maxValue:0); i <= maxValue; i += jump) { impl.write(wb, i); num++; } //for (int i=0;i<b.remaining();i++) System.out.print(b.get(i)+"|"); w.stop(); ReadBuffer rb = wb.getStaticBuffer().asReadBuffer(); log.info("Writing " + num + " longs in " + rb.length() + " bytes. in time: " + w.getTime()); final ReadVerify read = new ReadVerify() { @Override public void next(ReadBuffer rb, long expected) { int beforePos = rb.getPosition(); long value = impl.read(rb); assertEquals(expected, value); int length = Math.abs(rb.getPosition()-beforePos); assertEquals("On: " + expected,length,impl.length(expected)); } }; if (backward) { rb.movePositionTo(rb.length()); for (long i = maxValue; i != (negative?-maxValue:0); i -= jump) { read.next(rb,i); } } else { for (long i = (negative?-maxValue:0); i <= maxValue; i += jump) { read.next(rb, i); } } //Test boundaries wb = new WriteByteBuffer(512); impl.write(wb,0); impl.write(wb,Long.MAX_VALUE); if (negative) impl.write(wb,-Long.MAX_VALUE); rb = wb.getStaticBuffer().asReadBuffer(); if (backward) { rb.movePositionTo(rb.length()); if (negative) assertEquals(-Long.MAX_VALUE, impl.read(rb)); assertEquals(Long.MAX_VALUE, impl.read(rb)); assertEquals(0, impl.read(rb)); } else { assertEquals(0, impl.read(rb)); assertEquals(Long.MAX_VALUE, impl.read(rb)); if (negative) assertEquals(-Long.MAX_VALUE, impl.read(rb)); } } private interface ReadVerify { public void next(ReadBuffer rb, long expected); } public void positiveReadWriteTest(ReadWriteLong impl, long maxValue, long jump) { readWriteTest(impl,maxValue,jump,false,false); } public void negativeReadWriteTest(ReadWriteLong impl, long maxValue, long jump) { readWriteTest(impl,maxValue,jump,true,false); } @Test public void testPositiveWriteBig() { positiveReadWriteTest(new PositiveReadWrite(), 10000000000000L, 1000000L); } @Test public void testPositiveWriteSmall() { positiveReadWriteTest(new PositiveReadWrite(),1000000, 1); } @Test public void testNegativeWriteBig() { negativeReadWriteTest(new NegativeReadWrite(), 1000000000000L, 1000000L); } @Test public void testNegativeWriteSmall() { negativeReadWriteTest(new NegativeReadWrite(), 1000000, 1); } @Test public void testPosBackwardWriteBig() { readWriteTest(new PosBackwardReadWrite(), 10000000000000L, 1000000L, false, true); } @Test public void testPosBackwardWriteSmall() { readWriteTest(new PosBackwardReadWrite(), 1000000, 1, false, true); } @Test public void testBackwardWriteBig() { readWriteTest(new BackwardReadWrite(), 10000000000000L, 10000000L, true, true); } @Test public void testBackwardWriteSmall() { readWriteTest(new BackwardReadWrite(), 1000000, 1, true, true); } @Test public void testPrefix1WriteBig() { positiveReadWriteTest(new PrefixReadWrite(3,4), 1000000000000L, 1000000L); } @Test public void testPrefix2WriteTiny() { positiveReadWriteTest(new PrefixReadWrite(2,1),130, 1); } @Test public void testPrefix2WriteSmall() { positiveReadWriteTest(new PrefixReadWrite(2,1),100000, 1); } @Test public void testPrefix3WriteSmall() { positiveReadWriteTest(new PrefixReadWrite(2,0),100000, 1); } public interface ReadWriteLong { public void write(WriteBuffer out, long value); public int length(long value); public long read(ReadBuffer in); } public static class PositiveReadWrite implements ReadWriteLong { @Override public void write(WriteBuffer out, long value) { VariableLong.writePositive(out,value); } @Override public int length(long value) { return VariableLong.positiveLength(value); } @Override public long read(ReadBuffer in) { return VariableLong.readPositive(in); } } public static class NegativeReadWrite implements ReadWriteLong { @Override public void write(WriteBuffer out, long value) { VariableLong.write(out,value); } @Override public int length(long value) { return VariableLong.length(value); } @Override public long read(ReadBuffer in) { return VariableLong.read(in); } } public static class PosBackwardReadWrite implements ReadWriteLong { @Override public void write(WriteBuffer out, long value) { VariableLong.writePositiveBackward(out,value); } @Override public int length(long value) { return VariableLong.positiveBackwardLength(value); } @Override public long read(ReadBuffer in) { return VariableLong.readPositiveBackward(in); } } public static class BackwardReadWrite implements ReadWriteLong { @Override public void write(WriteBuffer out, long value) { VariableLong.writeBackward(out,value); } @Override public int length(long value) { return VariableLong.backwardLength(value); } @Override public long read(ReadBuffer in) { return VariableLong.readBackward(in); } } public static class PrefixReadWrite implements ReadWriteLong { private final int prefixLen; private final int prefix; public PrefixReadWrite(int prefixLen, int prefix) { Preconditions.checkArgument(prefixLen>0); Preconditions.checkArgument(prefix>=0 && prefix<(1<<prefixLen)); this.prefixLen = prefixLen; this.prefix = prefix; } @Override public void write(WriteBuffer out, long value) { VariableLong.writePositiveWithPrefix(out,value,prefix,prefixLen); } @Override public int length(long value) { return VariableLong.positiveWithPrefixLength(value,prefixLen); } @Override public long read(ReadBuffer in) { long[] result = VariableLong.readPositiveWithPrefix(in,prefixLen); assertEquals(prefix,result[1]); return result[0]; } } @Test public void byteOrderPreservingPositiveBackward() { long[] scalingFactors = { Long.MAX_VALUE, 1000, 1000000000l}; for (int t=0;t<10000000;t++) { StaticBuffer[] b = new StaticBuffer[2]; long[] l = new long[2]; for (int i=0;i<2;i++) { l[i] = randomPosLong(scalingFactors[random.nextInt(scalingFactors.length)]); WriteBuffer out = new WriteByteBuffer(11); VariableLong.writePositiveBackward(out,l[i]); b[i]=out.getStaticBuffer(); ReadBuffer res = b[i].asReadBuffer(); res.movePositionTo(res.length()); assertEquals(l[i], VariableLong.readPositiveBackward(res)); } // System.out.println(l[0] + " vs " + l[1]); assertEquals(Math.signum(Long.compare(l[0],l[1])),Math.signum(b[0].compareTo(b[1])), 0.01); } } private static final Random random = new Random(); public static long randomPosLong(long scaling) { long l = Math.round(random.nextGaussian()/3*scaling); if (l<0) l=Math.abs(l+1); assert l>=0; return l; } }