/*
* Copyright (C) 2012, 2016 higherfrequencytrading.com
* Copyright (C) 2016 Roman Leventov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.chronicle.map;
import net.openhft.hashing.LongHashFunction;
import org.junit.Assert;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;
public class KeySegmentDistributionTest {
@Test
public void keySegmentDistributionTestOneSegment() {
keySegmentDistributionTest(1000, 1);
}
@Test
public void keySegmentDistributionTestPowerOfTwoSegments() {
keySegmentDistributionTest(1000, 4);
}
@Test
public void keySegmentDistributionTestOddSegments() {
keySegmentDistributionTest(1000, 5);
}
public void keySegmentDistributionTest(int size, int segments) {
ChronicleMap<CharSequence, Integer> map = ChronicleMapBuilder
.of(CharSequence.class, Integer.class)
.actualSegments(segments)
// TODO problems with computing proper number of segments/chunks
// when I write this without `* 2` I expect not to have ISE "segment is full"...
.entries(size * 2)
.averageKeySize(10)
.create();
byte[] keyBytes = new byte[10];
ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < size; i++) {
random.nextBytes(keyBytes);
String key = new String(keyBytes, StandardCharsets.US_ASCII);
long hash = LongHashFunction.xx_r39().hashBytes(StandardCharsets.UTF_8.encode(key));
int segmentIndex = (((int) hash) & Integer.MAX_VALUE) % segments;
// Put the segment index as a value to the map
map.put(key, segmentIndex);
}
// The following loop checks that internally hash code and segment index is chosen
// the same way as we explicitly computed in this test. Since ChMap iteration is segment
// by segment, we expect to see the sequence of values 0, 0, ... 0, 1, ..1, 2, ..2, 3, ..3
// or opposite -- 3, 3, ... 3, 2, ..2, 1, ..1, 0, ..0
int currentSegment = -1;
boolean ascendingDirection = false;
for (Integer entrySegment : map.values()) {
if (currentSegment == -1) {
currentSegment = entrySegment;
if (currentSegment == 0) {
ascendingDirection = true;
}
} else {
if (ascendingDirection) {
Assert.assertTrue(entrySegment >= currentSegment);
currentSegment = entrySegment;
} else {
// descending iteration direction
Assert.assertTrue(entrySegment <= currentSegment);
currentSegment = entrySegment;
}
}
}
}
}