├── .gitignore ├── LICENSE ├── README.md ├── measures ├── jmh-results │ ├── city11hash.txt │ ├── murmur3Hash.txt │ └── vanillaHash.txt ├── pom.xml └── src │ └── main │ └── java │ └── net │ └── openhft │ └── chronicle │ └── algorithms │ └── measures │ ├── AddressWrapper.java │ ├── AddressWrappers.java │ ├── AvalancheScore.java │ ├── CheckMain.java │ ├── MainBytes.java │ ├── MaskHashScore.java │ └── OrtogonalBitsScore.java ├── pom.xml └── src ├── main └── java │ └── net │ └── openhft │ └── chronicle │ └── algo │ ├── MemoryUnit.java │ ├── bitset │ ├── BitSet.java │ ├── BitSetAlgorithm.java │ ├── BitSetFrame.java │ ├── ConcurrentFlatBitSetFrame.java │ ├── FlatBitSetAlgorithm.java │ ├── ReusableBitSet.java │ └── SingleThreadedFlatBitSetFrame.java │ ├── bytes │ ├── Access.java │ ├── AccessCommon.java │ ├── Accessor.java │ ├── ArrayAccessors.java │ ├── ByteBufferAccess.java │ ├── ByteBufferAccessor.java │ ├── BytesAccesses.java │ ├── BytesAccessors.java │ ├── CharSequenceAccess.java │ ├── CharSequenceAccessor.java │ ├── HotSpotStringAccessor.java │ ├── NativeAccess.java │ ├── RandomDataInputAccess.java │ ├── RandomDataOutputAccess.java │ ├── ReadAccess.java │ ├── WriteAccess.java │ └── ZeroAccess.java │ ├── hashing │ ├── CityHash_1_1.java │ ├── LongHashFunction.java │ ├── MurmurHash_3.java │ ├── Primitives.java │ └── XxHash_r39.java │ ├── internal │ └── package-info.java │ └── locks │ ├── AbstractReadWriteLockState.java │ ├── AbstractReadWriteLockingStrategy.java │ ├── AcquisitionStrategies.java │ ├── AcquisitionStrategy.java │ ├── LockState.java │ ├── LockingStrategy.java │ ├── ReadWriteLockState.java │ ├── ReadWriteLockingStrategy.java │ ├── ReadWriteUpdateLockState.java │ ├── ReadWriteUpdateLockingStrategy.java │ ├── ReadWriteUpdateWithWaitsLockState.java │ ├── ReadWriteUpdateWithWaitsLockingStrategy.java │ ├── ReadWriteWithWaitsLockState.java │ ├── ReadWriteWithWaitsLockingStrategy.java │ ├── TryAcquireOperation.java │ ├── TryAcquireOperations.java │ ├── VanillaReadWriteUpdateWithWaitsLockingStrategy.java │ └── VanillaReadWriteWithWaitsLockingStrategy.java └── test ├── java └── net │ └── openhft │ └── chronicle │ ├── algo │ ├── bitset │ │ └── DirectBitSetTest.java │ ├── hashing │ │ ├── City64MoreTest.java │ │ ├── City64_1_1_Test.java │ │ ├── HashSearcherMain.java │ │ ├── HashTest.java │ │ ├── HashTesterMain.java │ │ ├── HashTesterRunner.java │ │ ├── LongHashFunctionTest.java │ │ ├── MurmurHash3MoreTest.java │ │ ├── MurmurHash3Test.java │ │ ├── RandomOptimiser.java │ │ └── XxHash_r39_Test.java │ └── locks │ │ └── LockingStrategyTest.java │ └── map │ └── locks │ └── ChronicleStampedLockTest.java └── resources └── ftse350.csv /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | 8 | # IntelliJ 9 | *.iml 10 | *.ipr 11 | *.iws 12 | .idea 13 | compat_reports 14 | 15 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 16 | hs_err_pid* 17 | 18 | # Eclipse 19 | .classpath 20 | .project 21 | .settings/ 22 | 23 | # maven 24 | target 25 | 26 | # tests 27 | *.bin 28 | *.deleteme -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | == Copyright 2016 higherfrequencytrading.com 2 | 3 | Licensed under the *Apache License, Version 2.0* (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chronicle-Algorithms 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Zero allocation, efficient algorithms for 11 | - hashing 12 | - bit set operations 13 | - access the raw bytes of an data type 14 | - off heap locking 15 | -------------------------------------------------------------------------------- /measures/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | 21 | net.openhft 22 | chronicle-algorithms-benchmarks 23 | 1.0-SNAPSHOT 24 | OpenHFT/Chronicle-Algorithms/Measures 25 | Chronicle-Algorithms-Measures 26 | 27 | 28 | UTF-8 29 | 30 | 31 | 32 | 33 | 34 | 35 | net.openhft 36 | third-party-bom 37 | 3.27ea0 38 | pom 39 | import 40 | 41 | 42 | 43 | net.openhft 44 | chronicle-bom 45 | 2.27ea-SNAPSHOT 46 | pom 47 | import 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | net.openhft 57 | chronicle-algorithms 58 | 59 | 60 | net.openhft 61 | chronicle-bytes 62 | 63 | 64 | org.slf4j 65 | slf4j-simple 66 | 67 | 68 | 69 | org.openjdk.jmh 70 | jmh-core 71 | 72 | 73 | org.openjdk.jmh 74 | jmh-generator-annprocess 75 | 1.10.4 76 | provided 77 | 78 | 79 | 80 | 81 | 82 | 83 | org.apache.maven.plugins 84 | maven-shade-plugin 85 | 2.2 86 | 87 | 88 | package 89 | 90 | shade 91 | 92 | 93 | ${project.name}.jar 94 | 95 | 97 | net.openhft.chronicle.algorithms.measures.MainBytes 98 | 99 | 100 | 101 | 102 | 106 | *:* 107 | 108 | META-INF/*.SF 109 | META-INF/*.DSA 110 | META-INF/*.RSA 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | org.apache.maven.plugins 124 | maven-install-plugin 125 | 3.0.0-M1 126 | 127 | 128 | maven-jar-plugin 129 | 2.6 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /measures/src/main/java/net/openhft/chronicle/algorithms/measures/AddressWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algorithms.measures; 18 | 19 | /** 20 | * Created by peter on 21/08/15. 21 | */ 22 | public interface AddressWrapper { 23 | void setAddress(long address, long length); 24 | 25 | long hash(); 26 | } 27 | -------------------------------------------------------------------------------- /measures/src/main/java/net/openhft/chronicle/algorithms/measures/AddressWrappers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algorithms.measures; 18 | 19 | import net.openhft.chronicle.algo.bytes.NativeAccess; 20 | import net.openhft.chronicle.algo.hashing.LongHashFunction; 21 | import net.openhft.chronicle.bytes.Bytes; 22 | import net.openhft.chronicle.bytes.BytesStore; 23 | import net.openhft.chronicle.bytes.PointerBytesStore; 24 | import net.openhft.chronicle.bytes.algo.OptimisedBytesStoreHash; 25 | 26 | import java.security.SecureRandom; 27 | import java.util.Random; 28 | 29 | /** 30 | * Created by peter on 21/08/15. 31 | */ 32 | public enum AddressWrappers implements AddressWrapper { 33 | RANDOM { 34 | Random rand = new Random(); 35 | 36 | @Override 37 | public void setAddress(long address, long length) { 38 | } 39 | @Override 40 | public long hash() { 41 | return rand.nextLong(); 42 | } 43 | }, 44 | SECURE_RANDOM { 45 | SecureRandom rand = new SecureRandom(); 46 | 47 | @Override 48 | public void setAddress(long address, long length) { 49 | } 50 | @Override 51 | public long hash() { 52 | return rand.nextLong(); 53 | } 54 | }, 55 | 56 | VANILLA { 57 | int length; 58 | Bytes bytes; 59 | 60 | @Override 61 | public void setAddress(long address, long length) { 62 | this.length = (int) length; 63 | PointerBytesStore pbs = BytesStore.nativePointer(); 64 | pbs.set(address, length); 65 | bytes = pbs.bytesForRead().unchecked(true); 66 | } 67 | @Override 68 | public long hash() { 69 | return OptimisedBytesStoreHash.applyAsLong32bytesMultiple(bytes, length); 70 | } 71 | }, 72 | CITY_1_1 { 73 | long address,length; 74 | 75 | @Override 76 | public void setAddress(long address, long length) { 77 | this.address = address; 78 | this.length = length; 79 | } 80 | @Override 81 | public long hash() { 82 | return LongHashFunction.city_1_1().hash((Object) null, NativeAccess.instance(), address, length); 83 | } 84 | }, 85 | MURMUR_3 { 86 | long address,length; 87 | 88 | @Override 89 | public void setAddress(long address, long length) { 90 | this.address = address; 91 | this.length = length; 92 | } 93 | @Override 94 | public long hash() { 95 | return LongHashFunction.murmur_3().hash((Object) null, NativeAccess.instance(), address, length); 96 | } 97 | }, 98 | STRING32 { 99 | Bytes bytes; 100 | 101 | @Override 102 | public void setAddress(long address, long length) { 103 | PointerBytesStore pbs = BytesStore.nativePointer(); 104 | pbs.set(address, length); 105 | bytes = pbs.bytesForRead().unchecked(true); 106 | } 107 | @Override 108 | public long hash() { 109 | int hc = 0; 110 | for (int i = 0; i < bytes.length(); i++) 111 | hc = hc * 31 + bytes.charAt(i); 112 | 113 | return hash(hc); 114 | } 115 | // from the hash() function in HashMap 116 | int hash(int h) { 117 | // This function ensures that hashCodes that differ only by 118 | // constant multiples at each bit position have a bounded 119 | // number of collisions (approximately 8 at default load factor). 120 | h ^= (h >>> 20) ^ (h >>> 12); 121 | return h ^ (h >>> 7) ^ (h >>> 4); 122 | } 123 | }, 124 | STRING64 { 125 | Bytes bytes; 126 | 127 | @Override 128 | public void setAddress(long address, long length) { 129 | PointerBytesStore pbs = BytesStore.nativePointer(); 130 | pbs.set(address, length); 131 | bytes = pbs.bytesForRead().unchecked(true); 132 | } 133 | @Override 134 | public long hash() { 135 | long hc = 0; 136 | for (int i = 0; i < bytes.length(); i++) 137 | hc = hc * 31 + bytes.charAt(i); 138 | return hash(hc); 139 | } 140 | // based on the hash() function in HashMap 141 | long hash(long h) { 142 | h ^= (h >>> 41) ^ (h >>> 23); 143 | return h ^ (h >>> 14) ^ (h >>> 7); 144 | } 145 | }, 146 | STRING32_WITHOUT_AGITATE { 147 | Bytes bytes; 148 | 149 | @Override 150 | public void setAddress(long address, long length) { 151 | PointerBytesStore pbs = BytesStore.nativePointer(); 152 | pbs.set(address, length); 153 | bytes = pbs.bytesForRead().unchecked(true); 154 | } 155 | @Override 156 | public long hash() { 157 | int hc = 0; 158 | for (int i = 0; i < bytes.length(); i++) 159 | hc = hc * 31 + bytes.charAt(i); 160 | 161 | return hc; 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /measures/src/main/java/net/openhft/chronicle/algorithms/measures/AvalancheScore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algorithms.measures; 18 | 19 | import net.openhft.chronicle.bytes.NativeBytes; 20 | 21 | import java.security.SecureRandom; 22 | import java.util.Arrays; 23 | 24 | /** 25 | * Created by peter on 22/08/15. 26 | */ 27 | public class AvalancheScore { 28 | /** 29 | * Based on the SMHasher Avalanche test 30 | *

31 | * search for biases bits. 32 | * when flipping a single bit of the input, the output should have a 49% - 51% chance of flipping. Some randomness is expected. 33 | * 34 | * @return the worst flip bias. 35 | */ 36 | public static double score(AddressWrapper wrapper) { 37 | int runs = 1000; 38 | int bits = 8192; 39 | Double[] scores = new Double[runs]; 40 | for (int t = 0; t < runs; t++) { 41 | int[] bitFlipCount = new int[64]; 42 | long[] hashs = new long[bits]; 43 | byte[] init = new byte[hashs.length / 8]; 44 | NativeBytes b = NativeBytes.nativeBytes(init.length); 45 | SecureRandom rand = new SecureRandom(); 46 | rand.nextBytes(init); 47 | // low bit count test 48 | if (t % 2 == 0) { 49 | byte[] init2 = new byte[hashs.length / 8]; 50 | rand.nextBytes(init2); 51 | for (int i = 0; i < init.length; i++) 52 | init[i] &= init2[i]; 53 | } 54 | wrapper.setAddress(b.address(0), b.realCapacity()); 55 | 56 | b.clear(); 57 | b.write(init); 58 | for (int i = 0; i < hashs.length; i++) { 59 | int index = i >> 6 << 3; 60 | long prev = b.readLong(index); 61 | b.writeLong(index, prev ^ (1L << i)); 62 | b.readLimit(hashs.length / 8); 63 | hashs[i] = wrapper.hash(); 64 | b.writeLong(index, prev); 65 | } 66 | for (int i = 0; i < hashs.length - 1; i++) { 67 | long diff = hashs[i + 1] ^ hashs[i]; 68 | for (int k = 0; k < 64; k++) { 69 | bitFlipCount[k] += (int) ((diff >>> k) & 1); 70 | } 71 | } 72 | long tests = bits - 1; 73 | double sum = 0; 74 | for (int i = 0; i < bitFlipCount.length; i++) { 75 | int count = bitFlipCount[i]; 76 | double bitScore = 10000L * count / tests / 100.0; 77 | double err = Math.abs(bitScore - 50); 78 | sum += err; 79 | } 80 | scores[t] = sum / bitFlipCount.length; 81 | } 82 | Arrays.sort(scores); 83 | 84 | double score = scores[runs * 99 / 100]; 85 | System.out.printf("Avalanche: The 99%%tile of the drift from 50%% was %.2f%%%n", score); 86 | return score; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /measures/src/main/java/net/openhft/chronicle/algorithms/measures/CheckMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algorithms.measures; 18 | 19 | /** 20 | * Created by peter on 21/08/15. 21 | */ 22 | /* 23 | VANILLA 24 | Orthogonal bits: 99%tile score: 6066 25 | Speed: The 99%tile for latency was 0.223 us 26 | Avalanche: The 99%tile of the drift from 50% was 0.55% 27 | Mask of Hash: 99%tile collisions: 1815 28 | 29 | CITY_1_1 30 | Orthogonal bits: 99%tile score: 7395 31 | Speed: The 99%tile for latency was 0.267 us 32 | Avalanche: The 99%tile of the drift from 50% was 0.55% 33 | Mask of Hash: 99%tile collisions: 1817 34 | 35 | MURMUR_3 36 | Orthogonal bits: 99%tile score: 7524 37 | Speed: The 99%tile for latency was 0.378 us 38 | Avalanche: The 99%tile of the drift from 50% was 0.54% 39 | Mask of Hash: 99%tile collisions: 1815 40 | 41 | STRING32 42 | Orthogonal bits: 99%tile score: 295906433 43 | Speed: The 99%tile for latency was 1.580 us 44 | Avalanche: The 99%tile of the drift from 50% was 1.02% 45 | Mask of Hash: 99%tile collisions: 1814 46 | 47 | STRING64 48 | Orthogonal bits: 99%tile score: 1939167 49 | Speed: The 99%tile for latency was 1.520 us 50 | Avalanche: The 99%tile of the drift from 50% was 0.61% 51 | Mask of Hash: 99%tile collisions: 1816 52 | */ 53 | public class CheckMain { 54 | public static void main(String[] args) { 55 | for (AddressWrappers aw : AddressWrappers.values()) { 56 | System.out.println(aw); 57 | OrtogonalBitsScore.score(aw); 58 | AvalancheScore.score(aw); 59 | MaskHashScore.score(aw); 60 | System.out.println(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /measures/src/main/java/net/openhft/chronicle/algorithms/measures/MainBytes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algorithms.measures; 18 | 19 | import net.openhft.affinity.Affinity; 20 | import net.openhft.chronicle.algo.hashing.LongHashFunction; 21 | import net.openhft.chronicle.bytes.Bytes; 22 | import net.openhft.chronicle.bytes.algo.OptimisedBytesStoreHash; 23 | import net.openhft.chronicle.core.Jvm; 24 | import org.openjdk.jmh.annotations.*; 25 | import org.openjdk.jmh.runner.Runner; 26 | import org.openjdk.jmh.runner.RunnerException; 27 | import org.openjdk.jmh.runner.options.Options; 28 | import org.openjdk.jmh.runner.options.OptionsBuilder; 29 | import org.openjdk.jmh.runner.options.TimeValue; 30 | 31 | import java.lang.reflect.InvocationTargetException; 32 | import java.lang.reflect.Method; 33 | import java.util.concurrent.TimeUnit; 34 | 35 | @State(Scope.Thread) 36 | public class MainBytes { 37 | static final LongHashFunction city_1_1 = LongHashFunction.city_1_1(); 38 | static final LongHashFunction murmur_3 = LongHashFunction.murmur_3(); 39 | static final LongHashFunction xx_r39 = LongHashFunction.xx_r39(); 40 | Bytes bytes; 41 | long num = 0; 42 | @Param({"16", "64", "256"}) 43 | int size; 44 | 45 | public static void main(String... args) 46 | throws RunnerException, InvocationTargetException, IllegalAccessException { 47 | Affinity.setAffinity(2); 48 | if (Jvm.isDebug()) { 49 | MainBytes main = new MainBytes(); 50 | main.size = 16; 51 | main.fillBytes(); 52 | for (Method m : MainBytes.class.getMethods()) { 53 | if (m.getAnnotation(Benchmark.class) != null) { 54 | m.invoke(main); 55 | } 56 | } 57 | } else { 58 | int time = Jvm.getBoolean("longTest") ? 30 : 2; 59 | System.out.println("measurementTime: " + time + " secs"); 60 | Options opt = new OptionsBuilder() 61 | .include(MainBytes.class.getSimpleName()) 62 | .mode(Mode.SampleTime) 63 | .measurementTime(TimeValue.seconds(time)) 64 | .timeUnit(TimeUnit.NANOSECONDS) 65 | .forks(1) 66 | .build(); 67 | 68 | new Runner(opt).run(); 69 | } 70 | } 71 | @Setup(Level.Trial) 72 | public void fillBytes() { 73 | bytes = Bytes.allocateDirect(size).unchecked(true); 74 | for (int i = 0; i < bytes.capacity(); i += 8) { 75 | bytes.writeLong(i, num += 0x1111111111111111L); 76 | } 77 | bytes.writePosition(bytes.capacity()); 78 | } 79 | @Benchmark 80 | public long vanillaHash() { 81 | return OptimisedBytesStoreHash.INSTANCE.applyAsLong(bytes); 82 | } 83 | @Benchmark 84 | public long city11Hash() { 85 | return city_1_1.hashMemory(bytes.address(bytes.readPosition()), bytes.readRemaining()); 86 | } 87 | @Benchmark 88 | public long murmur3Hash() { 89 | return murmur_3.hashMemory(bytes.address(bytes.readPosition()), bytes.readRemaining()); 90 | } 91 | @Benchmark 92 | public long xx39Hash() { 93 | return xx_r39.hashMemory(bytes.address(bytes.readPosition()), bytes.readRemaining()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /measures/src/main/java/net/openhft/chronicle/algorithms/measures/MaskHashScore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algorithms.measures; 18 | 19 | import net.openhft.chronicle.bytes.NativeBytes; 20 | 21 | import java.security.SecureRandom; 22 | import java.util.Arrays; 23 | import java.util.HashSet; 24 | import java.util.Set; 25 | 26 | /** 27 | * Created by peter on 22/08/15. 28 | */ 29 | public class MaskHashScore { 30 | /** 31 | * This test looks at how many collision you get in the lower bits. 32 | * It generates 8K hashes, for 8Kbit input and look at at the lower 14 bits (for 16K values) 33 | * The ideal is 8K unique hashes after mask. 34 | */ 35 | public static double score(AddressWrapper wrapper) { 36 | int runs = 2000; 37 | 38 | int bits = 8192; 39 | int mask = bits * 2 - 1; 40 | int[] collisions = new int[runs]; 41 | for (int t = 0; t < runs; t++) { 42 | Set maskedhashs = new HashSet<>(); 43 | byte[] init = new byte[bits / 8]; 44 | NativeBytes b = NativeBytes.nativeBytes(init.length); 45 | SecureRandom rand = new SecureRandom(); 46 | rand.nextBytes(init); 47 | // low bit count test 48 | if (t % 2 == 0) { 49 | byte[] init2 = new byte[bits / 8]; 50 | rand.nextBytes(init2); 51 | for (int i = 0; i < init.length; i++) 52 | init[i] &= init2[i]; 53 | } 54 | wrapper.setAddress(b.address(0), b.realCapacity()); 55 | 56 | b.clear(); 57 | b.write(init); 58 | for (int i = 0; i < bits; i++) { 59 | int index = i >> 6 << 3; 60 | long prev = b.readLong(index); 61 | b.writeLong(index, prev ^ (1L << i)); 62 | b.readLimit(bits / 8); 63 | maskedhashs.add((int) (wrapper.hash() & mask)); 64 | b.writeLong(index, prev); 65 | } 66 | collisions[t] = (bits - maskedhashs.size()); 67 | } 68 | Arrays.sort(collisions); 69 | int score = collisions[runs * 99 / 100]; 70 | System.out.println("Mask of Hash: 99%tile collisions: " + score); 71 | return score; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /measures/src/main/java/net/openhft/chronicle/algorithms/measures/OrtogonalBitsScore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algorithms.measures; 18 | 19 | import net.openhft.chronicle.bytes.NativeBytes; 20 | 21 | import java.security.SecureRandom; 22 | import java.util.Arrays; 23 | 24 | /** 25 | * Created by peter on 21/08/15. 26 | */ 27 | public class OrtogonalBitsScore { 28 | public static long score(AddressWrapper wrapper) { 29 | int runs = 1000; 30 | long[] scores = new long[runs]; 31 | long[] times = new long[runs]; 32 | for (int t = 0; t < runs; t++) { 33 | long[] hashs = new long[8192]; 34 | byte[] init = new byte[hashs.length / 8]; 35 | NativeBytes b = NativeBytes.nativeBytes(init.length); 36 | SecureRandom rand = new SecureRandom(); 37 | rand.nextBytes(init); 38 | // low bit count test 39 | if (t % 2 == 0) { 40 | byte[] init2 = new byte[hashs.length / 8]; 41 | rand.nextBytes(init2); 42 | for (int i = 0; i < init.length; i++) 43 | init[i] &= init2[i]; 44 | } 45 | wrapper.setAddress(b.address(0), b.realCapacity()); 46 | 47 | b.clear(); 48 | b.write(init); 49 | for (int i = 0; i < hashs.length; i++) { 50 | int index = i >> 6 << 3; 51 | long prev = b.readLong(index); 52 | b.writeLong(index, prev ^ (1L << i)); 53 | b.readLimit(hashs.length / 8); 54 | long start = System.nanoTime(); 55 | hashs[i] = wrapper.hash(); 56 | times[t] = System.nanoTime() - start; 57 | b.writeLong(index, prev); 58 | } 59 | long score = 0; 60 | for (int i = 0; i < hashs.length - 1; i++) 61 | for (int j = i + 1; j < hashs.length; j++) { 62 | long diff = hashs[j] ^ hashs[i]; 63 | int diffBC = Long.bitCount(diff); 64 | if (diffBC < 18) { 65 | long d = 1L << (17 - diffBC); 66 | score += d; 67 | } 68 | } 69 | scores[t] = score; 70 | } 71 | Arrays.sort(scores); 72 | Arrays.sort(times); 73 | long score = scores[runs * 99 / 100]; 74 | long time = times[runs * 99 / 100]; 75 | System.out.println("Orthogonal bits: 99%tile score: " + score); 76 | System.out.printf("Speed: The 99%%tile for latency was %.3f us%n", time / 1e3); 77 | return score; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 4.0.0 22 | 23 | 24 | net.openhft 25 | java-parent-pom 26 | 1.27ea1 27 | 28 | 29 | 30 | chronicle-algorithms 31 | 2.27ea1-SNAPSHOT 32 | OpenHFT/Chronicle-Algorithms 33 | Chronicle-Algorithms 34 | 35 | 36 | 37 | 38 | 39 | net.openhft 40 | third-party-bom 41 | 3.27ea0 42 | pom 43 | import 44 | 45 | 46 | 47 | net.openhft 48 | chronicle-bom 49 | 2.27ea-SNAPSHOT 50 | pom 51 | import 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | net.openhft 62 | chronicle-core 63 | 64 | 65 | 66 | net.openhft 67 | chronicle-bytes 68 | 69 | 70 | 71 | org.slf4j 72 | slf4j-api 73 | 74 | 75 | 76 | org.jetbrains 77 | annotations 78 | 79 | 80 | 81 | 82 | 83 | junit 84 | junit 85 | test 86 | 87 | 88 | 89 | org.slf4j 90 | slf4j-simple 91 | test 92 | 93 | 94 | 95 | org.mockito 96 | mockito-core 97 | test 98 | 99 | 100 | 101 | com.google.guava 102 | guava-testlib 103 | test 104 | 105 | 106 | 107 | 108 | 109 | 110 | third-party-release 111 | ThirdParty Repository 112 | 113 | https://nexus.chronicle.software/content/repositories/thirdparty/ 114 | 115 | 116 | true 117 | 118 | 119 | 120 | chronicle-enterprise-snapshots 121 | Snapshot Repository 122 | 123 | https://nexus.chronicle.software/content/repositories/snapshots 124 | 125 | 126 | true 127 | 128 | 129 | 130 | chronicle-enterprise-release 131 | 132 | https://nexus.chronicle.software/content/repositories/releases 133 | 134 | 135 | true 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-scm-publish-plugin 146 | 147 | ${project.build.directory}/scmpublish/javadoc 148 | 149 | Publishing javadoc for ${project.artifactId}:${project.version} 150 | 151 | ${project.reporting.outputDirectory} 152 | true 153 | scm:git:git@github.com:OpenHFT/Chronicle-Algorithms 154 | gh-pages 155 | 156 | 157 | 158 | 159 | 160 | 161 | scm:git:git@github.com:OpenHFT/Chronicle-Algorithms.git 162 | scm:git:git@github.com:OpenHFT/Chronicle-Algorithms.git 163 | scm:git:git@github.com:OpenHFT/Chronicle-Algorithms.git 164 | 165 | master 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bitset/BitSetAlgorithm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bitset; 18 | 19 | public interface BitSetAlgorithm { 20 | 21 | long sizeInBytes(long logicalSize); 22 | 23 | long maxLogicalSizeFittingSameSizeInBytes(long logicalSize); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bitset/FlatBitSetAlgorithm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bitset; 18 | 19 | import static net.openhft.chronicle.algo.MemoryUnit.BITS; 20 | 21 | enum FlatBitSetAlgorithm implements BitSetAlgorithm { 22 | INSTANCE; 23 | 24 | @Override 25 | public long sizeInBytes(long logicalSize) { 26 | return BITS.toBytes(logicalSize); 27 | } 28 | 29 | @Override 30 | public long maxLogicalSizeFittingSameSizeInBytes(long logicalSize) { 31 | return logicalSize; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bitset/ReusableBitSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bitset; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | public class ReusableBitSet implements BitSet { 22 | protected BitSetFrame frame; 23 | protected Access access; 24 | protected Object handle; 25 | protected long offset; 26 | 27 | public ReusableBitSet( 28 | BitSetFrame frame, Access access, T handle, long offset) { 29 | reuse(frame, access, handle, offset); 30 | } 31 | 32 | public final ReusableBitSet reuse( 33 | BitSetFrame frame, Access access, T handle, long offset) { 34 | this.frame = frame; 35 | @SuppressWarnings("unchecked") 36 | Access access0 = (Access) access; 37 | this.access = access0; 38 | this.handle = handle; 39 | this.offset = offset; 40 | return this; 41 | } 42 | 43 | public void setOffset(long offset) { 44 | this.offset = offset; 45 | } 46 | 47 | @Override 48 | public void flip(long bitIndex) { 49 | frame.flip(access, handle, offset, bitIndex); 50 | } 51 | 52 | @Override 53 | public void flipRange(long fromIndex, long toIndex) { 54 | frame.flipRange(access, handle, offset, fromIndex, toIndex); 55 | } 56 | 57 | @Override 58 | public void set(long bitIndex) { 59 | frame.set(access, handle, offset, bitIndex); 60 | } 61 | 62 | @Override 63 | public boolean setIfClear(long bitIndex) { 64 | return frame.setIfClear(access, handle, offset, bitIndex); 65 | } 66 | 67 | @Override 68 | public boolean clearIfSet(long bitIndex) { 69 | return frame.clearIfSet(access, handle, offset, bitIndex); 70 | } 71 | 72 | @Override 73 | public void setRange(long fromIndex, long toIndex) { 74 | frame.setRange(access, handle, offset, fromIndex, toIndex); 75 | } 76 | 77 | @Override 78 | public boolean isRangeSet(long fromIndex, long toIndex) { 79 | return frame.isRangeSet(access, handle, offset, fromIndex, toIndex); 80 | } 81 | 82 | @Override 83 | public void setAll() { 84 | frame.setAll(access, handle, offset); 85 | } 86 | 87 | @Override 88 | public void clear(long bitIndex) { 89 | frame.clear(access, handle, offset, bitIndex); 90 | } 91 | 92 | @Override 93 | public void clearRange(long fromIndex, long toIndex) { 94 | frame.clearRange(access, handle, offset, fromIndex, toIndex); 95 | } 96 | 97 | @Override 98 | public boolean isRangeClear(long fromIndex, long toIndex) { 99 | return frame.isRangeClear(access, handle, offset, fromIndex, toIndex); 100 | } 101 | 102 | @Override 103 | public void clearAll() { 104 | frame.clearAll(access, handle, offset); 105 | } 106 | 107 | @Override 108 | public boolean get(long bitIndex) { 109 | return frame.get(access, handle, offset, bitIndex); 110 | } 111 | 112 | @Override 113 | public long nextSetBit(long fromIndex) { 114 | return frame.nextSetBit(access, handle, offset, fromIndex); 115 | } 116 | 117 | @Override 118 | public long nextClearBit(long fromIndex) { 119 | return frame.nextClearBit(access, handle, offset, fromIndex); 120 | } 121 | 122 | @Override 123 | public long previousSetBit(long fromIndex) { 124 | return frame.previousSetBit(access, handle, offset, fromIndex); 125 | } 126 | 127 | @Override 128 | public long previousClearBit(long fromIndex) { 129 | return frame.previousClearBit(access, handle, offset, fromIndex); 130 | } 131 | 132 | @Override 133 | public long logicalSize() { 134 | return frame.logicalSize(); 135 | } 136 | 137 | @Override 138 | public long cardinality() { 139 | return frame.cardinality(access, handle, offset); 140 | } 141 | 142 | @Override 143 | public long setNextClearBit(long fromIndex) { 144 | return frame.setNextClearBit(access, handle, offset, fromIndex); 145 | } 146 | 147 | @Override 148 | public long clearNextSetBit(long fromIndex) { 149 | return frame.clearNextSetBit(access, handle, offset, fromIndex); 150 | } 151 | 152 | @Override 153 | public long setPreviousClearBit(long fromIndex) { 154 | return frame.setPreviousClearBit(access, handle, offset, fromIndex); 155 | } 156 | 157 | @Override 158 | public long clearPreviousSetBit(long fromIndex) { 159 | return frame.clearPreviousSetBit(access, handle, offset, fromIndex); 160 | } 161 | 162 | @Override 163 | public long setNextNContinuousClearBits(long fromIndex, int numberOfBits) { 164 | return frame.setNextNContinuousClearBits(access, handle, offset, fromIndex, numberOfBits); 165 | } 166 | 167 | @Override 168 | public long clearNextNContinuousSetBits(long fromIndex, int numberOfBits) { 169 | return frame.clearNextNContinuousSetBits(access, handle, offset, fromIndex, numberOfBits); 170 | } 171 | 172 | @Override 173 | public long setPreviousNContinuousClearBits(long fromIndex, int numberOfBits) { 174 | return frame.setPreviousNContinuousClearBits(access, handle, offset, 175 | fromIndex, numberOfBits); 176 | } 177 | 178 | @Override 179 | public long clearPreviousNContinuousSetBits(long fromIndex, int numberOfBits) { 180 | return frame.clearPreviousNContinuousSetBits(access, handle, offset, 181 | fromIndex, numberOfBits); 182 | } 183 | 184 | @Override 185 | public Bits setBits() { 186 | return new Bits(frame.setBits()); 187 | } 188 | 189 | protected class Bits implements BitSet.Bits { 190 | protected BitSetFrame.Bits frameBits; 191 | 192 | public Bits(BitSetFrame.Bits frameBits) { 193 | this.frameBits = frameBits; 194 | } 195 | 196 | @Override 197 | public BitSet.Bits reset() { 198 | frameBits.reset(access, handle, offset); 199 | return this; 200 | } 201 | 202 | @Override 203 | public long next() { 204 | return frameBits.next(access, handle, offset); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/Access.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import net.openhft.chronicle.bytes.BytesStore; 20 | import net.openhft.chronicle.bytes.RandomDataInput; 21 | 22 | import java.nio.ByteBuffer; 23 | 24 | public interface Access extends ReadAccess, WriteAccess { 25 | 26 | static Access nativeAccess() { 27 | return NativeAccess.instance(); 28 | } 29 | 30 | static Access checkedByteBufferAccess() { 31 | return ByteBufferAccess.INSTANCE; 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | static , U> Access checkedBytesStoreAccess() { 36 | return (Access) BytesAccesses.Full.INSTANCE; 37 | } 38 | 39 | static ReadAccess checkedRandomDataInputAccess() { 40 | return BytesAccesses.RandomDataInputReadAccessEnum.INSTANCE; 41 | } 42 | 43 | static void copy(final ReadAccess sourceAccess, 44 | final S source, 45 | final long sourceOffset, 46 | final WriteAccess targetAccess, 47 | final T target, 48 | final long targetOffset, 49 | final long len) { 50 | if (targetAccess == sourceAccess && target == source && targetOffset == sourceOffset) 51 | return; 52 | long i = 0; 53 | while (len - i >= 8L) { 54 | targetAccess.writeLong(target, targetOffset + i, sourceAccess.readLong(source, sourceOffset + i)); 55 | i += 8L; 56 | } 57 | if (len - i >= 4L) { 58 | targetAccess.writeInt(target, targetOffset + i, sourceAccess.readInt(source, sourceOffset + i)); 59 | i += 4L; 60 | } 61 | if (len - i >= 2L) { 62 | targetAccess.writeShort(target, targetOffset + i, sourceAccess.readShort(source, sourceOffset + i)); 63 | i += 2L; 64 | } 65 | if (i < len) { 66 | targetAccess.writeByte(target, targetOffset + i, sourceAccess.readByte(source, sourceOffset + i)); 67 | } 68 | } 69 | 70 | static boolean equivalent(final ReadAccess access1, 71 | final T handle1, 72 | final long offset1, 73 | final ReadAccess access2, 74 | final U handle2, 75 | final long offset2, 76 | final long len) { 77 | long i = 0; 78 | while (len - i >= 8L) { 79 | if (access1.readLong(handle1, offset1 + i) != access2.readLong(handle2, offset2 + i)) 80 | return false; 81 | i += 8L; 82 | } 83 | if (len - i >= 4L) { 84 | if (access1.readInt(handle1, offset1 + i) != access2.readInt(handle2, offset2 + i)) 85 | return false; 86 | i += 4L; 87 | } 88 | if (len - i >= 2L) { 89 | if (access1.readShort(handle1, offset1 + i) != access2.readShort(handle2, offset2 + i)) 90 | return false; 91 | i += 2L; 92 | } 93 | if (i < len) { 94 | return access1.readByte(handle1, offset1 + i) == access2.readByte(handle2, offset2 + i); 95 | } 96 | return true; 97 | } 98 | 99 | /** 100 | * Default implementation: throws {@code UnsupportedOperationException}. 101 | */ 102 | boolean compareAndSwapInt(T handle, long offset, int expected, int value); 103 | 104 | /** 105 | * Default implementation: throws {@code UnsupportedOperationException}. 106 | */ 107 | boolean compareAndSwapLong(T handle, long offset, long expected, long value); 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/AccessCommon.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import java.nio.ByteOrder; 20 | 21 | @FunctionalInterface 22 | interface AccessCommon { 23 | ByteOrder byteOrder(T handle); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/Accessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import net.openhft.chronicle.bytes.BytesStore; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.nio.ByteOrder; 23 | 24 | public interface Accessor> { 25 | 26 | @SuppressWarnings("unchecked") 27 | static , U> Accessor.Full checkedBytesStoreAccessor() { 28 | return (Accessor.Full) BytesAccessors.Generic.INSTANCE; 29 | } 30 | 31 | static Accessor.Full uncheckedByteBufferAccessor( 32 | ByteBuffer buffer) { 33 | return ByteBufferAccessor.unchecked(buffer); 34 | } 35 | 36 | static Accessor.Full booleanArrayAccessor() { 37 | return ArrayAccessors.Boolean.INSTANCE; 38 | } 39 | 40 | static Accessor.Full byteArrayAccessor() { 41 | return ArrayAccessors.Byte.INSTANCE; 42 | } 43 | 44 | static Accessor.Full charArrayAccessor() { 45 | return ArrayAccessors.Char.INSTANCE; 46 | } 47 | 48 | static Accessor.Full shortArrayAccessor() { 49 | return ArrayAccessors.Short.INSTANCE; 50 | } 51 | 52 | static Accessor.Full intArrayAccessor() { 53 | return ArrayAccessors.Int.INSTANCE; 54 | } 55 | 56 | static Accessor.Full longArrayAccessor() { 57 | return ArrayAccessors.Long.INSTANCE; 58 | } 59 | 60 | @SuppressWarnings("unchecked") 61 | static Accessor.Read stringAccessor() { 62 | return (Read) CharSequenceAccessor.stringAccessor; 63 | } 64 | 65 | static Accessor.Read checkedNativeCharSequenceAccessor() { 66 | return CharSequenceAccessor.nativeCharSequenceAccessor(); 67 | } 68 | 69 | static Accessor.Read checkedCharSequenceAccess(ByteOrder order) { 70 | return order == ByteOrder.LITTLE_ENDIAN ? CharSequenceAccessor.LITTLE_ENDIAN : 71 | CharSequenceAccessor.BIG_ENDIAN; 72 | } 73 | 74 | /** 75 | * Returns {@code Access} for the given source. 76 | * 77 | * @return {@code Access} for the given source 78 | */ 79 | A access(); 80 | 81 | /** 82 | * Returns handle for {@code Access} to the given source. 83 | * 84 | * @param source the source 85 | * @return handle for {@code Access} to the given source 86 | */ 87 | T handle(S source); 88 | 89 | /** 90 | * Convert index in the source domain to {@code Access} offset. 91 | * 92 | * @param source the source 93 | * @param index index in the source type domain 94 | * @return offset for {@code Access}, corresponding to the given index 95 | */ 96 | long offset(S source, long index); 97 | 98 | /** 99 | * Convert size (length) in the source domain to size in bytes. 100 | *

101 | * The default implementation returns the given {@code size} back, i. e. assuming 102 | * byte-indexed source. 103 | * 104 | * @param size size (length) in the source type domain 105 | * @return number of bytes, corresponding to the given size in the source type domain 106 | */ 107 | default long size(long size) { 108 | return size; 109 | } 110 | 111 | interface Read extends Accessor> { 112 | } 113 | 114 | interface Full extends Accessor> { 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/ArrayAccessors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import static net.openhft.chronicle.core.UnsafeMemory.MEMORY; 20 | 21 | final class ArrayAccessors { 22 | 23 | static final long BYTE_BASE; 24 | private static final long BOOLEAN_BASE; 25 | private static final long CHAR_BASE; 26 | private static final long SHORT_BASE; 27 | private static final long INT_BASE; 28 | private static final long LONG_BASE; 29 | 30 | static { 31 | try { 32 | BOOLEAN_BASE = MEMORY.arrayBaseOffset(boolean[].class); 33 | BYTE_BASE = MEMORY.arrayBaseOffset(byte[].class); 34 | CHAR_BASE = MEMORY.arrayBaseOffset(char[].class); 35 | SHORT_BASE = MEMORY.arrayBaseOffset(short[].class); 36 | INT_BASE = MEMORY.arrayBaseOffset(int[].class); 37 | LONG_BASE = MEMORY.arrayBaseOffset(long[].class); 38 | 39 | } catch (Exception e) { 40 | throw new AssertionError(e); 41 | } 42 | } 43 | 44 | private ArrayAccessors() { 45 | } 46 | 47 | enum Boolean implements Accessor.Full { 48 | INSTANCE; 49 | 50 | @Override 51 | public Access access() { 52 | return NativeAccess.instance(); 53 | } 54 | 55 | @Override 56 | public boolean[] handle(boolean[] source) { 57 | return source; 58 | } 59 | 60 | @Override 61 | public long offset(boolean[] source, long index) { 62 | return BOOLEAN_BASE + index; 63 | } 64 | } 65 | 66 | enum Byte implements Accessor.Full { 67 | INSTANCE; 68 | 69 | @Override 70 | public Access access() { 71 | return NativeAccess.instance(); 72 | } 73 | 74 | @Override 75 | public byte[] handle(byte[] source) { 76 | return source; 77 | } 78 | 79 | @Override 80 | public long offset(byte[] source, long index) { 81 | return BYTE_BASE + index; 82 | } 83 | } 84 | 85 | enum Char implements Accessor.Full { 86 | INSTANCE; 87 | 88 | @Override 89 | public Access access() { 90 | return NativeAccess.instance(); 91 | } 92 | 93 | @Override 94 | public char[] handle(char[] source) { 95 | return source; 96 | } 97 | 98 | @Override 99 | public long offset(char[] source, long index) { 100 | return CHAR_BASE + (index * 2L); 101 | } 102 | 103 | @Override 104 | public long size(long size) { 105 | return size * 2L; 106 | } 107 | } 108 | 109 | enum Short implements Accessor.Full { 110 | INSTANCE; 111 | 112 | @Override 113 | public Access access() { 114 | return NativeAccess.instance(); 115 | } 116 | 117 | @Override 118 | public short[] handle(short[] source) { 119 | return source; 120 | } 121 | 122 | @Override 123 | public long offset(short[] source, long index) { 124 | return SHORT_BASE + (index * 2L); 125 | } 126 | 127 | @Override 128 | public long size(long size) { 129 | return size * 2L; 130 | } 131 | } 132 | 133 | enum Int implements Accessor.Full { 134 | INSTANCE; 135 | 136 | @Override 137 | public Access access() { 138 | return NativeAccess.instance(); 139 | } 140 | 141 | @Override 142 | public int[] handle(int[] source) { 143 | return source; 144 | } 145 | 146 | @Override 147 | public long offset(int[] source, long index) { 148 | return INT_BASE + (index * 4L); 149 | } 150 | 151 | @Override 152 | public long size(long size) { 153 | return size * 4L; 154 | } 155 | } 156 | 157 | enum Long implements Accessor.Full { 158 | INSTANCE; 159 | 160 | @Override 161 | public Access access() { 162 | return NativeAccess.instance(); 163 | } 164 | 165 | @Override 166 | public long[] handle(long[] source) { 167 | return source; 168 | } 169 | 170 | @Override 171 | public long offset(long[] source, long index) { 172 | return LONG_BASE + (index * 8L); 173 | } 174 | 175 | @Override 176 | public long size(long size) { 177 | return size * 8L; 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/ByteBufferAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.nio.ByteOrder; 21 | 22 | final class ByteBufferAccess implements Access { 23 | public static final ByteBufferAccess INSTANCE = new ByteBufferAccess(); 24 | 25 | private ByteBufferAccess() { 26 | } 27 | 28 | @Override 29 | public byte readByte(ByteBuffer buffer, long offset) { 30 | return buffer.get((int) offset); 31 | } 32 | 33 | @Override 34 | public short readShort(ByteBuffer buffer, long offset) { 35 | return buffer.getShort((int) offset); 36 | } 37 | 38 | @Override 39 | public char readChar(ByteBuffer buffer, long offset) { 40 | return buffer.getChar((int) offset); 41 | } 42 | 43 | @Override 44 | public int readInt(ByteBuffer buffer, long offset) { 45 | return buffer.getInt((int) offset); 46 | } 47 | 48 | @Override 49 | public long readLong(ByteBuffer buffer, long offset) { 50 | return buffer.getLong((int) offset); 51 | } 52 | 53 | @Override 54 | public float readFloat(ByteBuffer buffer, long offset) { 55 | return buffer.getFloat((int) offset); 56 | } 57 | 58 | @Override 59 | public double readDouble(ByteBuffer buffer, long offset) { 60 | return buffer.getDouble((int) offset); 61 | } 62 | 63 | @Override 64 | public void writeByte(ByteBuffer buffer, long offset, byte i8) { 65 | buffer.put((int) offset, i8); 66 | } 67 | 68 | @Override 69 | public void writeShort(ByteBuffer buffer, long offset, short i) { 70 | buffer.putShort((int) offset, i); 71 | } 72 | 73 | @Override 74 | public void writeChar(ByteBuffer buffer, long offset, char c) { 75 | buffer.putChar((int) offset, c); 76 | } 77 | 78 | @Override 79 | public void writeInt(ByteBuffer buffer, long offset, int i) { 80 | buffer.putInt((int) offset, i); 81 | } 82 | 83 | @Override 84 | public void writeLong(ByteBuffer buffer, long offset, long i) { 85 | buffer.putLong((int) offset, i); 86 | } 87 | 88 | @Override 89 | public void writeFloat(ByteBuffer buffer, long offset, float d) { 90 | buffer.putFloat((int) offset, d); 91 | } 92 | 93 | @Override 94 | public void writeDouble(ByteBuffer buffer, long offset, double d) { 95 | buffer.putDouble((int) offset, d); 96 | } 97 | 98 | @Override 99 | public ByteOrder byteOrder(ByteBuffer buffer) { 100 | return buffer.order(); 101 | } 102 | 103 | @Override 104 | public boolean compareAndSwapInt(ByteBuffer handle, long offset, int expected, int value) { 105 | throw new UnsupportedOperationException("todo"); 106 | } 107 | 108 | @Override 109 | public boolean compareAndSwapLong(ByteBuffer handle, long offset, long expected, long value) { 110 | throw new UnsupportedOperationException("todo"); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/ByteBufferAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | import static net.openhft.chronicle.core.UnsafeMemory.MEMORY; 22 | 23 | interface ByteBufferAccessor extends Accessor.Full { 24 | 25 | static ByteBufferAccessor unchecked(ByteBuffer buffer) { 26 | return buffer.isDirect() ? Direct.INSTANCE : Heap.INSTANCE; 27 | } 28 | 29 | static ByteBufferAccessor checked() { 30 | return Generic.INSTANCE; 31 | } 32 | 33 | enum Direct implements ByteBufferAccessor { 34 | INSTANCE; 35 | 36 | @Override 37 | public Access access() { 38 | return NativeAccess.instance(); 39 | } 40 | 41 | @Override 42 | public Void handle(ByteBuffer buffer) { 43 | return null; 44 | } 45 | 46 | @Override 47 | public long offset(ByteBuffer buffer, long bufferIndex) { 48 | return MEMORY.address(buffer) + bufferIndex; 49 | } 50 | } 51 | 52 | enum Heap implements ByteBufferAccessor { 53 | INSTANCE; 54 | 55 | @Override 56 | public Access access() { 57 | return NativeAccess.instance(); 58 | } 59 | 60 | @Override 61 | public byte[] handle(ByteBuffer buffer) { 62 | return buffer.array(); 63 | } 64 | 65 | @Override 66 | public long offset(ByteBuffer buffer, long bufferIndex) { 67 | return ArrayAccessors.BYTE_BASE + buffer.arrayOffset() + bufferIndex; 68 | } 69 | } 70 | 71 | enum Generic implements ByteBufferAccessor { 72 | INSTANCE; 73 | 74 | @Override 75 | public Access access() { 76 | return ByteBufferAccess.INSTANCE; 77 | } 78 | 79 | @Override 80 | public ByteBuffer handle(ByteBuffer buffer) { 81 | return buffer; 82 | } 83 | 84 | @Override 85 | public long offset(ByteBuffer buffer, long bufferIndex) { 86 | return bufferIndex; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/BytesAccesses.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import net.openhft.chronicle.bytes.BytesStore; 20 | import net.openhft.chronicle.bytes.RandomDataInput; 21 | 22 | import java.nio.ByteOrder; 23 | 24 | final class BytesAccesses { 25 | 26 | private BytesAccesses() { 27 | } 28 | 29 | enum RandomDataInputReadAccessEnum implements RandomDataInputAccess { 30 | INSTANCE 31 | } 32 | 33 | static class Full, U> implements RandomDataInputAccess, 34 | RandomDataOutputAccess, Access { 35 | 36 | static final Full INSTANCE = new Full<>(); 37 | 38 | @Override 39 | public boolean compareAndSwapInt(B handle, long offset, int expected, int value) { 40 | return handle.compareAndSwapInt(offset, expected, value); 41 | } 42 | 43 | @Override 44 | public boolean compareAndSwapLong(B handle, long offset, long expected, long value) { 45 | return handle.compareAndSwapLong(offset, expected, value); 46 | } 47 | 48 | @Override 49 | public ByteOrder byteOrder(B handle) { 50 | return handle.byteOrder(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/BytesAccessors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import net.openhft.chronicle.bytes.BytesStore; 20 | 21 | final class BytesAccessors { 22 | 23 | private BytesAccessors() { 24 | } 25 | 26 | static class Generic> implements Accessor.Full { 27 | 28 | static final Generic INSTANCE = new Generic<>(); 29 | 30 | @SuppressWarnings("unchecked") 31 | @Override 32 | public Access access() { 33 | return (Access) BytesAccesses.Full.INSTANCE; 34 | } 35 | 36 | @Override 37 | public S handle(S source) { 38 | return source; 39 | } 40 | 41 | @Override 42 | public long offset(S source, long index) { 43 | return index; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/CharSequenceAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import java.nio.ByteOrder; 20 | 21 | import static java.nio.ByteOrder.BIG_ENDIAN; 22 | import static java.nio.ByteOrder.LITTLE_ENDIAN; 23 | 24 | abstract class CharSequenceAccess implements ReadAccess { 25 | 26 | private CharSequenceAccess() { 27 | } 28 | 29 | private static CharSequenceAccess charSequenceAccess(ByteOrder order) { 30 | return order == LITTLE_ENDIAN ? 31 | LittleEndianCharSequenceAccess.INSTANCE : 32 | BigEndianCharSequenceAccess.INSTANCE; 33 | } 34 | 35 | private static int ix(long offset) { 36 | return (int) (offset >> 1); 37 | } 38 | 39 | private static long getLong(CharSequence input, long offset, 40 | int char0Off, int char1Off, int char2Off, int char3Off) { 41 | int base = ix(offset); 42 | long char0 = input.charAt(base + char0Off); 43 | long char1 = input.charAt(base + char1Off); 44 | long char2 = input.charAt(base + char2Off); 45 | long char3 = input.charAt(base + char3Off); 46 | return char0 | (char1 << 16) | (char2 << 32) | (char3 << 48); 47 | } 48 | 49 | private static long getUnsignedInt(CharSequence input, long offset, 50 | int char0Off, int char1Off) { 51 | int base = ix(offset); 52 | long char0 = input.charAt(base + char0Off); 53 | long char1 = input.charAt(base + char1Off); 54 | return char0 | (char1 << 16); 55 | } 56 | 57 | private static int getUnsignedByte(CharSequence input, long offset, int shift) { 58 | return (input.charAt(ix(offset)) >> shift) & 0xFF; 59 | } 60 | 61 | @Override 62 | public int readInt(CharSequence input, long offset) { 63 | return (int) readUnsignedInt(input, offset); 64 | } 65 | 66 | @Override 67 | public int readUnsignedShort(CharSequence input, long offset) { 68 | return input.charAt(ix(offset)); 69 | } 70 | 71 | @Override 72 | public short readShort(CharSequence input, long offset) { 73 | return (short) input.charAt(ix(offset)); 74 | } 75 | 76 | @Override 77 | public byte readByte(CharSequence input, long offset) { 78 | return (byte) readUnsignedByte(input, offset); 79 | } 80 | 81 | static class LittleEndianCharSequenceAccess extends CharSequenceAccess { 82 | static final CharSequenceAccess INSTANCE = new LittleEndianCharSequenceAccess(); 83 | 84 | private LittleEndianCharSequenceAccess() { 85 | } 86 | 87 | @Override 88 | public long readLong(CharSequence input, long offset) { 89 | return getLong(input, offset, 0, 1, 2, 3); 90 | } 91 | 92 | @Override 93 | public long readUnsignedInt(CharSequence input, long offset) { 94 | return getUnsignedInt(input, offset, 0, 1); 95 | } 96 | 97 | @Override 98 | public int readUnsignedByte(CharSequence input, long offset) { 99 | return getUnsignedByte(input, offset, ((int) offset & 1) << 3); 100 | } 101 | 102 | @Override 103 | public ByteOrder byteOrder(CharSequence input) { 104 | return LITTLE_ENDIAN; 105 | } 106 | } 107 | 108 | static class BigEndianCharSequenceAccess extends CharSequenceAccess { 109 | static final CharSequenceAccess INSTANCE = new BigEndianCharSequenceAccess(); 110 | 111 | private BigEndianCharSequenceAccess() { 112 | } 113 | 114 | @Override 115 | public long readLong(CharSequence input, long offset) { 116 | return getLong(input, offset, 3, 2, 1, 0); 117 | } 118 | 119 | @Override 120 | public long readUnsignedInt(CharSequence input, long offset) { 121 | return getUnsignedInt(input, offset, 1, 0); 122 | } 123 | 124 | @Override 125 | public int readUnsignedByte(CharSequence input, long offset) { 126 | return getUnsignedByte(input, offset, (((int) offset & 1) ^ 1) << 3); 127 | } 128 | 129 | @Override 130 | public ByteOrder byteOrder(CharSequence input) { 131 | return BIG_ENDIAN; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/CharSequenceAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import net.openhft.chronicle.core.Jvm; 20 | 21 | import java.nio.ByteOrder; 22 | 23 | abstract class CharSequenceAccessor 24 | implements Accessor.Read { 25 | 26 | static final Accessor.Read stringAccessor; 27 | static final CharSequenceAccessor LITTLE_ENDIAN = new CharSequenceAccessor() { 28 | @Override 29 | public ReadAccess access() { 30 | return CharSequenceAccess.LittleEndianCharSequenceAccess.INSTANCE; 31 | } 32 | }; 33 | static final CharSequenceAccessor BIG_ENDIAN = new CharSequenceAccessor() { 34 | @Override 35 | public ReadAccess access() { 36 | return CharSequenceAccess.BigEndianCharSequenceAccess.INSTANCE; 37 | } 38 | }; 39 | 40 | static { 41 | if (Jvm.isJava9Plus()) 42 | stringAccessor = HotSpotStringAccessor.JAVA9PLUS; 43 | else 44 | stringAccessor = HotSpotStringAccessor.JAVA8; 45 | } 46 | 47 | private CharSequenceAccessor() { 48 | } 49 | 50 | static CharSequenceAccessor nativeCharSequenceAccessor() { 51 | return ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? LITTLE_ENDIAN : BIG_ENDIAN; 52 | } 53 | 54 | @Override 55 | public CharSequence handle(CharSequence source) { 56 | return source; 57 | } 58 | 59 | @Override 60 | public long offset(CharSequence source, long index) { 61 | return index * 2L; 62 | } 63 | 64 | @Override 65 | public long size(long size) { 66 | return size * 2L; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/HotSpotStringAccessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import java.lang.reflect.Field; 20 | 21 | import static net.openhft.chronicle.core.UnsafeMemory.MEMORY; 22 | 23 | final class HotSpotStringAccessor implements Accessor.Read { 24 | public static final HotSpotStringAccessor JAVA8 = new HotSpotStringAccessor<>(); 25 | public static final HotSpotStringAccessor JAVA9PLUS = new HotSpotStringAccessor<>(); 26 | 27 | private static final long valueOffset; 28 | 29 | static { 30 | try { 31 | Field valueField = String.class.getDeclaredField("value"); 32 | valueOffset = MEMORY.objectFieldOffset(valueField); 33 | } catch (NoSuchFieldException e) { 34 | throw new AssertionError(e); 35 | } 36 | } 37 | 38 | private HotSpotStringAccessor() { 39 | } 40 | 41 | @Override 42 | public ReadAccess access() { 43 | return NativeAccess.instance(); 44 | } 45 | 46 | @SuppressWarnings("unchecked") 47 | @Override 48 | public T handle(String source) { 49 | return (T) MEMORY.getObject(source, valueOffset); 50 | } 51 | 52 | @Override 53 | public long offset(String source, long index) { 54 | return ArrayAccessors.Char.INSTANCE.offset(null, index); 55 | } 56 | 57 | @Override 58 | public long size(long size) { 59 | return size * 2L; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/NativeAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import java.nio.ByteOrder; 20 | 21 | import static net.openhft.chronicle.core.UnsafeMemory.MEMORY; 22 | 23 | public final class NativeAccess implements Access { 24 | 25 | private static final NativeAccess INSTANCE = new NativeAccess<>(); 26 | 27 | private NativeAccess() { 28 | } 29 | 30 | @SuppressWarnings("unchecked") 31 | public static NativeAccess instance() { 32 | //noinspection unchecked 33 | return (NativeAccess) INSTANCE; 34 | } 35 | 36 | @Override 37 | public byte readByte(T handle, long offset) { 38 | return MEMORY.readByte(handle, offset); 39 | } 40 | 41 | @Override 42 | public short readShort(T handle, long offset) { 43 | return MEMORY.readShort(handle, offset); 44 | } 45 | 46 | @Override 47 | public char readChar(T handle, long offset) { 48 | return (char) MEMORY.readShort(handle, offset); 49 | } 50 | 51 | @Override 52 | public int readInt(T handle, long offset) { 53 | return MEMORY.readInt(handle, offset); 54 | } 55 | 56 | @Override 57 | public long readLong(T handle, long offset) { 58 | return MEMORY.readLong(handle, offset); 59 | } 60 | 61 | @Override 62 | public float readFloat(T handle, long offset) { 63 | return MEMORY.readFloat(handle, offset); 64 | } 65 | 66 | @Override 67 | public double readDouble(T handle, long offset) { 68 | return MEMORY.readDouble(handle, offset); 69 | } 70 | 71 | @Override 72 | public int readVolatileInt(T handle, long offset) { 73 | return MEMORY.readVolatileInt(handle, offset); 74 | } 75 | 76 | @Override 77 | public long readVolatileLong(T handle, long offset) { 78 | return MEMORY.readVolatileLong(handle, offset); 79 | } 80 | 81 | @Override 82 | public void writeByte(T handle, long offset, byte i8) { 83 | MEMORY.writeByte(handle, offset, i8); 84 | } 85 | 86 | @Override 87 | public void writeShort(T handle, long offset, short i) { 88 | MEMORY.writeShort(handle, offset, i); 89 | } 90 | 91 | @Override 92 | public void writeChar(T handle, long offset, char c) { 93 | MEMORY.writeShort(handle, offset, (short) c); 94 | } 95 | 96 | @Override 97 | public void writeInt(T handle, long offset, int i) { 98 | MEMORY.writeInt(handle, offset, i); 99 | } 100 | 101 | @Override 102 | public void writeOrderedInt(T handle, long offset, int i) { 103 | MEMORY.writeOrderedInt(handle, offset, i); 104 | } 105 | 106 | @Override 107 | public void writeLong(T handle, long offset, long i) { 108 | MEMORY.writeLong(handle, offset, i); 109 | } 110 | 111 | @Override 112 | public void writeOrderedLong(T handle, long offset, long i) { 113 | MEMORY.writeOrderedLong(handle, offset, i); 114 | } 115 | 116 | @Override 117 | public void writeFloat(T handle, long offset, float d) { 118 | MEMORY.writeFloat(handle, offset, d); 119 | } 120 | 121 | @Override 122 | public void writeDouble(T handle, long offset, double d) { 123 | MEMORY.writeDouble(handle, offset, d); 124 | } 125 | 126 | @Override 127 | public boolean compareAndSwapInt(T handle, long offset, int expected, int value) { 128 | return MEMORY.compareAndSwapInt(handle, offset, expected, value); 129 | } 130 | 131 | @Override 132 | public boolean compareAndSwapLong(T handle, long offset, long expected, long value) { 133 | return MEMORY.compareAndSwapLong(handle, offset, expected, value); 134 | } 135 | 136 | @Override 137 | public ByteOrder byteOrder(T handle) { 138 | return ByteOrder.nativeOrder(); 139 | } 140 | 141 | @Override 142 | public void writeBytes(T handle, long offset, long len, byte b) { 143 | MEMORY.setMemory(handle, offset, len, b); 144 | } 145 | 146 | @Override 147 | public void zeroOut(T handle, long offset, long len) { 148 | MEMORY.setMemory(handle, offset, len, (byte) 0); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/RandomDataInputAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import net.openhft.chronicle.bytes.RandomDataInput; 20 | 21 | import java.nio.ByteOrder; 22 | 23 | interface RandomDataInputAccess extends ReadAccess { 24 | @Override 25 | default boolean readBoolean(S handle, long offset) { 26 | return handle.readBoolean(offset); 27 | } 28 | 29 | @Override 30 | default byte readByte(S handle, long offset) { 31 | return handle.readByte(offset); 32 | } 33 | 34 | @Override 35 | default int readUnsignedByte(S handle, long offset) { 36 | return handle.readUnsignedByte(offset); 37 | } 38 | 39 | @Override 40 | default short readShort(S handle, long offset) { 41 | return handle.readShort(offset); 42 | } 43 | 44 | @Override 45 | default int readUnsignedShort(S handle, long offset) { 46 | return handle.readUnsignedShort(offset); 47 | } 48 | 49 | @Override 50 | default int readInt(S handle, long offset) { 51 | return handle.readInt(offset); 52 | } 53 | 54 | @Override 55 | default long readUnsignedInt(S handle, long offset) { 56 | return handle.readUnsignedInt(offset); 57 | } 58 | 59 | @Override 60 | default long readLong(S handle, long offset) { 61 | return handle.readLong(offset); 62 | } 63 | 64 | @Override 65 | default float readFloat(S handle, long offset) { 66 | return handle.readFloat(offset); 67 | } 68 | 69 | @Override 70 | default double readDouble(S handle, long offset) { 71 | return handle.readDouble(offset); 72 | } 73 | 74 | @Override 75 | default String printable(S handle, long offset) { 76 | return handle.printable(offset); 77 | } 78 | 79 | @Override 80 | default int readVolatileInt(S handle, long offset) { 81 | return handle.readVolatileInt(offset); 82 | } 83 | 84 | @Override 85 | default long readVolatileLong(S handle, long offset) { 86 | return handle.readVolatileLong(offset); 87 | } 88 | 89 | @Override 90 | default ByteOrder byteOrder(S handle) { 91 | return handle.byteOrder(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/RandomDataOutputAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import net.openhft.chronicle.bytes.RandomDataOutput; 20 | 21 | import java.nio.ByteOrder; 22 | 23 | interface RandomDataOutputAccess> 24 | extends WriteAccess { 25 | @Override 26 | default void writeByte(R handle, long offset, int i) { 27 | handle.writeByte(offset, i); 28 | } 29 | 30 | @Override 31 | default void writeUnsignedByte(R handle, long offset, int i) { 32 | handle.writeUnsignedByte(offset, i); 33 | } 34 | 35 | @Override 36 | default void writeBoolean(R handle, long offset, boolean flag) { 37 | handle.writeBoolean(offset, flag); 38 | } 39 | 40 | @Override 41 | default void writeUnsignedShort(R handle, long offset, int i) { 42 | handle.writeUnsignedShort(offset, i); 43 | } 44 | 45 | @Override 46 | default void writeUnsignedInt(R handle, long offset, long i) { 47 | handle.writeUnsignedInt(offset, i); 48 | } 49 | 50 | @Override 51 | default void writeByte(R handle, long offset, byte i8) { 52 | handle.writeByte(offset, i8); 53 | } 54 | 55 | @Override 56 | default void writeShort(R handle, long offset, short i) { 57 | handle.writeShort(offset, i); 58 | } 59 | 60 | @Override 61 | default void writeInt(R handle, long offset, int i) { 62 | handle.writeInt(offset, i); 63 | } 64 | 65 | @Override 66 | default void writeOrderedInt(R handle, long offset, int i) { 67 | handle.writeOrderedInt(offset, i); 68 | } 69 | 70 | @Override 71 | default void writeLong(R handle, long offset, long i) { 72 | handle.writeLong(offset, i); 73 | } 74 | 75 | @Override 76 | default void writeOrderedLong(R handle, long offset, long i) { 77 | handle.writeOrderedLong(offset, i); 78 | } 79 | 80 | @Override 81 | default void writeFloat(R handle, long offset, float d) { 82 | handle.writeFloat(offset, d); 83 | } 84 | 85 | @Override 86 | default void writeDouble(R handle, long offset, double d) { 87 | handle.writeDouble(offset, d); 88 | } 89 | 90 | @Override 91 | default ByteOrder byteOrder(R handle) { 92 | return handle.byteOrder(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/ReadAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | public interface ReadAccess extends AccessCommon { 20 | 21 | static ReadAccess zeros() { 22 | return ZeroAccess.INSTANCE; 23 | } 24 | 25 | default boolean readBoolean(T handle, long offset) { 26 | return readByte(handle, offset) != 0; 27 | } 28 | 29 | byte readByte(T handle, long offset); 30 | 31 | default int readUnsignedByte(T handle, long offset) { 32 | return readByte(handle, offset) & 0xFF; 33 | } 34 | 35 | short readShort(T handle, long offset); 36 | 37 | default int readUnsignedShort(T handle, long offset) { 38 | return readShort(handle, offset) & 0xFFFF; 39 | } 40 | 41 | default char readChar(T handle, long offset) { 42 | return (char) readShort(handle, offset); 43 | } 44 | 45 | int readInt(T handle, long offset); 46 | 47 | default long readUnsignedInt(T handle, long offset) { 48 | return readInt(handle, offset) & 0xFFFFFFFFL; 49 | } 50 | 51 | long readLong(T handle, long offset); 52 | 53 | /** 54 | * Default implementation: {@code Float.intBitsToFloat(readInt(handle, offset))}. 55 | */ 56 | default float readFloat(T handle, long offset) { 57 | return Float.intBitsToFloat(readInt(handle, offset)); 58 | } 59 | 60 | /** 61 | * Default implementation: {@code Double.longBitsToDouble(readLong(handle, offset))}. 62 | */ 63 | default double readDouble(T handle, long offset) { 64 | return Double.longBitsToDouble(readLong(handle, offset)); 65 | } 66 | 67 | default String printable(T handle, long offset) { 68 | int b = readUnsignedByte(handle, offset); 69 | if (b == 0) 70 | return "\u0660"; 71 | else if (b < 21) 72 | return String.valueOf((char) (b + 0x2487)); 73 | else 74 | return String.valueOf((char) b); 75 | } 76 | 77 | /** 78 | * Default implementation: throws {@code UnsupportedOperationException}. 79 | */ 80 | default int readVolatileInt(T handle, long offset) { 81 | throw new UnsupportedOperationException(); 82 | } 83 | 84 | /** 85 | * Default implementation: throws {@code UnsupportedOperationException}. 86 | */ 87 | default long readVolatileLong(T handle, long offset) { 88 | throw new UnsupportedOperationException(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/WriteAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import net.openhft.chronicle.core.Maths; 20 | 21 | interface WriteAccess extends AccessCommon { 22 | default void writeByte(T handle, long offset, int i) { 23 | writeByte(handle, offset, Maths.toInt8(i)); 24 | } 25 | 26 | default void writeUnsignedByte(T handle, long offset, int i) { 27 | writeByte(handle, offset, (byte) Maths.toUInt8(i)); 28 | } 29 | 30 | default void writeBoolean(T handle, long offset, boolean flag) { 31 | writeByte(handle, offset, flag ? 'Y' : 0); 32 | } 33 | 34 | default void writeUnsignedShort(T handle, long offset, int i) { 35 | writeShort(handle, offset, (short) Maths.toUInt16(i)); 36 | } 37 | 38 | default void writeChar(T handle, long offset, char c) { 39 | writeShort(handle, offset, (short) c); 40 | } 41 | 42 | default void writeUnsignedInt(T handle, long offset, long i) { 43 | writeInt(handle, offset, (int) Maths.toUInt32(i)); 44 | } 45 | 46 | void writeByte(T handle, long offset, byte i8); 47 | 48 | void writeShort(T handle, long offset, short i); 49 | 50 | void writeInt(T handle, long offset, int i); 51 | 52 | /** 53 | * Default implementation: throws {@code UnsupportedOperationException}. 54 | */ 55 | default void writeOrderedInt(T handle, long offset, int i) { 56 | throw new UnsupportedOperationException(); 57 | } 58 | 59 | void writeLong(T handle, long offset, long i); 60 | 61 | /** 62 | * Default implementation: throws {@code UnsupportedOperationException}. 63 | */ 64 | default void writeOrderedLong(T handle, long offset, long i) { 65 | throw new UnsupportedOperationException(); 66 | } 67 | 68 | void writeFloat(T handle, long offset, float d); 69 | 70 | void writeDouble(T handle, long offset, double d); 71 | 72 | default void writeBytes(T handle, long offset, long len, byte b) { 73 | char c; 74 | int i; 75 | long l; 76 | switch (b) { 77 | case 0: 78 | zeroOut(handle, offset, len); 79 | return; 80 | case -1: 81 | c = Character.MAX_VALUE; 82 | i = -1; 83 | l = -1; 84 | break; 85 | default: 86 | int ub = b & 0xFF; 87 | int ic = ub | (ub << 8); 88 | c = (char) ic; 89 | i = ic | (ic << 16); 90 | long ui = i & 0xFFFFFFFFL; 91 | l = ui | (ui << 32); 92 | } 93 | long index = 0; 94 | while (len - index >= 8L) { 95 | writeLong(handle, offset + index, l); 96 | index += 8L; 97 | } 98 | if (len - index >= 4L) { 99 | writeInt(handle, offset + index, i); 100 | index += 4L; 101 | } 102 | if (len - index >= 2L) { 103 | writeChar(handle, offset + index, c); 104 | index += 2L; 105 | } 106 | if (index < len) 107 | writeByte(handle, offset + index, b); 108 | } 109 | 110 | default void zeroOut(T handle, long offset, long len) { 111 | long index = 0; 112 | while (len - index >= 8L) { 113 | writeLong(handle, offset + index, 0L); 114 | index += 8L; 115 | } 116 | if (len - index >= 4L) { 117 | writeInt(handle, offset + index, 0); 118 | index += 4L; 119 | } 120 | if (len - index >= 2L) { 121 | writeChar(handle, offset + index, (char) 0); 122 | index += 2L; 123 | } 124 | if (index < len) 125 | writeByte(handle, offset + index, (byte) 0); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/bytes/ZeroAccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.bytes; 18 | 19 | import java.nio.ByteOrder; 20 | 21 | enum ZeroAccess implements ReadAccess { 22 | INSTANCE; 23 | 24 | @Override 25 | public boolean readBoolean(Void handle, long offset) { 26 | return false; 27 | } 28 | 29 | @Override 30 | public byte readByte(Void handle, long offset) { 31 | return 0; 32 | } 33 | 34 | @Override 35 | public int readUnsignedByte(Void handle, long offset) { 36 | return 0; 37 | } 38 | 39 | @Override 40 | public short readShort(Void handle, long offset) { 41 | return 0; 42 | } 43 | 44 | @Override 45 | public int readUnsignedShort(Void handle, long offset) { 46 | return 0; 47 | } 48 | 49 | @Override 50 | public char readChar(Void handle, long offset) { 51 | return 0; 52 | } 53 | 54 | @Override 55 | public int readInt(Void handle, long offset) { 56 | return 0; 57 | } 58 | 59 | @Override 60 | public long readUnsignedInt(Void handle, long offset) { 61 | return 0; 62 | } 63 | 64 | @Override 65 | public long readLong(Void handle, long offset) { 66 | return 0; 67 | } 68 | 69 | @Override 70 | public float readFloat(Void handle, long offset) { 71 | return 0; 72 | } 73 | 74 | @Override 75 | public double readDouble(Void handle, long offset) { 76 | return 0; 77 | } 78 | 79 | @Override 80 | public String printable(Void handle, long offset) { 81 | return "\u0660"; 82 | } 83 | 84 | @Override 85 | public int readVolatileInt(Void handle, long offset) { 86 | return 0; 87 | } 88 | 89 | @Override 90 | public long readVolatileLong(Void handle, long offset) { 91 | return 0; 92 | } 93 | 94 | @Override 95 | public ByteOrder byteOrder(Void handle) { 96 | return ByteOrder.nativeOrder(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/hashing/MurmurHash_3.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import net.openhft.chronicle.algo.bytes.ReadAccess; 20 | 21 | import static java.lang.Long.reverseBytes; 22 | import static java.nio.ByteOrder.LITTLE_ENDIAN; 23 | import static net.openhft.chronicle.algo.hashing.LongHashFunction.NATIVE_LITTLE_ENDIAN; 24 | 25 | /** 26 | * Derived from https://github.com/google/guava/blob/fa95e381e665d8ee9639543b99ed38020c8de5ef 27 | * /guava/src/com/google/common/hash/Murmur3_128HashFunction.java 28 | */ 29 | class MurmurHash_3 { 30 | private static final MurmurHash_3 INSTANCE = new MurmurHash_3(); 31 | 32 | private static final MurmurHash_3 NATIVE_MURMUR = NATIVE_LITTLE_ENDIAN ? 33 | MurmurHash_3.INSTANCE : BigEndian.INSTANCE; 34 | 35 | private static final long C1 = 0x87c37b91114253d5L; 36 | private static final long C2 = 0x4cf5ad432745937fL; 37 | 38 | private MurmurHash_3() { 39 | } 40 | 41 | private static long finalize(long length, long h1, long h2) { 42 | h1 ^= length; 43 | h2 ^= length; 44 | 45 | h1 += h2; 46 | h2 += h1; 47 | 48 | h1 = fmix64(h1); 49 | h2 = fmix64(h2); 50 | 51 | h1 += h2; 52 | return h1; 53 | } 54 | 55 | private static long fmix64(long k) { 56 | k ^= k >>> 33; 57 | k *= 0xff51afd7ed558ccdL; 58 | k ^= k >>> 33; 59 | k *= 0xc4ceb9fe1a85ec53L; 60 | k ^= k >>> 33; 61 | return k; 62 | } 63 | 64 | private static long mixK1(long k1) { 65 | k1 *= C1; 66 | k1 = Long.rotateLeft(k1, 31); 67 | k1 *= C2; 68 | return k1; 69 | } 70 | 71 | private static long mixK2(long k2) { 72 | k2 *= C2; 73 | k2 = Long.rotateLeft(k2, 33); 74 | k2 *= C1; 75 | return k2; 76 | } 77 | 78 | public static LongHashFunction asLongHashFunctionWithoutSeed() { 79 | return AsLongHashFunction.INSTANCE; 80 | } 81 | 82 | public static LongHashFunction asLongHashFunctionWithSeed(long seed) { 83 | return new AsLongHashFunctionSeeded(seed); 84 | } 85 | 86 | long fetch64(ReadAccess access, T in, long off) { 87 | return access.readLong(in, off); 88 | } 89 | 90 | int fetch32(ReadAccess access, T in, long off) { 91 | return access.readInt(in, off); 92 | } 93 | 94 | long toLittleEndian(long v) { 95 | return v; 96 | } 97 | 98 | int toLittleEndian(int v) { 99 | return v; 100 | } 101 | 102 | int toLittleEndianShort(int unsignedShort) { 103 | return unsignedShort; 104 | } 105 | 106 | @SuppressWarnings("fallthrough") 107 | public long hash(long seed, T input, ReadAccess access, long offset, long length) { 108 | long h1 = seed; 109 | long h2 = seed; 110 | long remaining = length; 111 | while (remaining >= 16L) { 112 | long k1 = fetch64(access, input, offset); 113 | long k2 = fetch64(access, input, offset + 8L); 114 | offset += 16L; 115 | remaining -= 16L; 116 | h1 ^= mixK1(k1); 117 | 118 | h1 = Long.rotateLeft(h1, 27); 119 | h1 += h2; 120 | h1 = h1 * 5L + 0x52dce729L; 121 | 122 | h2 ^= mixK2(k2); 123 | 124 | h2 = Long.rotateLeft(h2, 31); 125 | h2 += h1; 126 | h2 = h2 * 5L + 0x38495ab5L; 127 | } 128 | if (remaining > 0L) { 129 | long k1 = 0L; 130 | long k2 = 0L; 131 | switch ((int) remaining) { 132 | case 15: 133 | k2 ^= ((long) access.readUnsignedByte(input, offset + 14L)) << 48;//fall through 134 | case 14: 135 | k2 ^= ((long) access.readUnsignedByte(input, offset + 13L)) << 40;//fall through 136 | case 13: 137 | k2 ^= ((long) access.readUnsignedByte(input, offset + 12L)) << 32;//fall through 138 | case 12: 139 | k2 ^= ((long) access.readUnsignedByte(input, offset + 11L)) << 24;//fall through 140 | case 11: 141 | k2 ^= ((long) access.readUnsignedByte(input, offset + 10L)) << 16;//fall through 142 | case 10: 143 | k2 ^= ((long) access.readUnsignedByte(input, offset + 9L)) << 8; // fall through 144 | case 9: 145 | k2 ^= access.readUnsignedByte(input, offset + 8L); // fall through 146 | case 8: 147 | k1 ^= fetch64(access, input, offset); 148 | break; 149 | case 7: 150 | k1 ^= ((long) access.readUnsignedByte(input, offset + 6L)) << 48;// fall through 151 | case 6: 152 | k1 ^= ((long) access.readUnsignedByte(input, offset + 5L)) << 40;// fall through 153 | case 5: 154 | k1 ^= ((long) access.readUnsignedByte(input, offset + 4L)) << 32;// fall through 155 | case 4: 156 | k1 ^= Primitives.unsignedInt(fetch32(access, input, offset)); 157 | break; 158 | case 3: 159 | k1 ^= ((long) access.readUnsignedByte(input, offset + 2L)) << 16;// fall through 160 | case 2: 161 | k1 ^= ((long) access.readUnsignedByte(input, offset + 1L)) << 8; // fall through 162 | case 1: 163 | k1 ^= access.readUnsignedByte(input, offset); 164 | case 0: 165 | break; 166 | default: 167 | throw new AssertionError("Should never get here."); 168 | } 169 | h1 ^= mixK1(k1); 170 | h2 ^= mixK2(k2); 171 | } 172 | // This version appears to be working slower 173 | 174 | // if (remaining > 0L) { 175 | // long k1 = 0L; 176 | // long k2 = 0L; 177 | // megaSwitch: 178 | // { 179 | // fetch0_7: 180 | // { 181 | // fetch8_11: 182 | // { 183 | // fetch0_3: 184 | // { 185 | // switch ((int) remaining) { 186 | // case 15: 187 | // k2 ^= ((long) access.readUnsignedByte(input, offset + 14L)) << 48; 188 | // case 14: 189 | // k2 ^= ((long) toLittleEndianShort( 190 | // access.getUnsignedShort(input, offset + 12L))) << 32; 191 | // break fetch8_11; 192 | // case 13: 193 | // k2 ^= ((long) access.readUnsignedByte(input, offset + 12L)) << 32; 194 | // case 12: 195 | // break fetch8_11; 196 | // case 11: 197 | // k2 ^= ((long) access.readUnsignedByte(input, offset + 10L)) << 16; 198 | // case 10: 199 | // k2 ^= (long) toLittleEndianShort( 200 | // access.getUnsignedShort(input, offset + 8L)); 201 | // break fetch0_7; 202 | // case 9: 203 | // k2 ^= ((long) access.readUnsignedByte(input, offset + 8L)); 204 | // case 8: 205 | // break fetch0_7; 206 | // case 7: 207 | // k1 ^= ((long) access.readUnsignedByte(input, offset + 6L)) << 48; 208 | // case 6: 209 | // k1 ^= ((long) toLittleEndianShort( 210 | // access.getUnsignedShort(input, offset + 4L))) << 32; 211 | // break fetch0_3; 212 | // case 5: 213 | // k1 ^= ((long) access.readUnsignedByte(input, offset + 4L)) << 32; 214 | // case 4: 215 | // break fetch0_3; 216 | // case 3: 217 | // k1 ^= ((long) access.readUnsignedByte(input, offset + 2L)) << 16; 218 | // case 2: 219 | // k1 ^= (long) toLittleEndianShort( 220 | // access.getUnsignedShort(input, offset)); 221 | // break megaSwitch; 222 | // case 1: 223 | // k1 ^= ((long) access.readUnsignedByte(input, offset)); 224 | // break megaSwitch; 225 | // default: 226 | // throw new AssertionError(); 227 | // } 228 | // } // fetch0_3 229 | // k1 ^= unsignedInt(fetch32(access, input, offset)); 230 | // break megaSwitch; 231 | // } // fetch8_11 232 | // k2 ^= unsignedInt(fetch32(access, input, offset + 8L)); 233 | // } // fetch0_7 234 | // k1 ^= fetch64(access, input, offset); 235 | // } // megaSwitch 236 | // 237 | // h1 ^= mixK1(k1); 238 | // h2 ^= mixK2(k2); 239 | // } 240 | return finalize(length, h1, h2); 241 | } 242 | 243 | private static class BigEndian extends MurmurHash_3 { 244 | private static final BigEndian INSTANCE = new BigEndian(); 245 | 246 | private BigEndian() { 247 | } 248 | 249 | @Override 250 | long fetch64(ReadAccess access, T in, long off) { 251 | return reverseBytes(super.fetch64(access, in, off)); 252 | } 253 | 254 | @Override 255 | int fetch32(ReadAccess access, T in, long off) { 256 | return Integer.reverseBytes(super.fetch32(access, in, off)); 257 | } 258 | 259 | @Override 260 | long toLittleEndian(long v) { 261 | return reverseBytes(v); 262 | } 263 | 264 | @Override 265 | int toLittleEndian(int v) { 266 | return Integer.reverseBytes(v); 267 | } 268 | 269 | @Override 270 | int toLittleEndianShort(int unsignedShort) { 271 | return ((unsignedShort & 0xFF) << 8) | (unsignedShort >> 8); 272 | } 273 | } 274 | 275 | private static class AsLongHashFunction extends LongHashFunction { 276 | public static final AsLongHashFunction INSTANCE = new AsLongHashFunction(); 277 | private static final long serialVersionUID = 0L; 278 | 279 | private Object readResolve() { 280 | return INSTANCE; 281 | } 282 | 283 | long seed() { 284 | return 0L; 285 | } 286 | 287 | long hashNativeLong(long nativeLong, long len) { 288 | long h1 = mixK1(nativeLong); 289 | long h2 = 0L; 290 | return MurmurHash_3.finalize(len, h1, h2); 291 | } 292 | 293 | @Override 294 | public long hashLong(long input) { 295 | return hashNativeLong(NATIVE_MURMUR.toLittleEndian(input), 8L); 296 | } 297 | 298 | @Override 299 | public long hashInt(int input) { 300 | return hashNativeLong(Primitives.unsignedInt(NATIVE_MURMUR.toLittleEndian(input)), 4L); 301 | } 302 | 303 | @Override 304 | public long hashShort(short input) { 305 | return hashNativeLong( 306 | NATIVE_MURMUR.toLittleEndianShort(Primitives.unsignedShort(input)), 2L); 307 | } 308 | 309 | @Override 310 | public long hashChar(char input) { 311 | return hashNativeLong(NATIVE_MURMUR.toLittleEndianShort(input), 2L); 312 | } 313 | 314 | @Override 315 | public long hashByte(byte input) { 316 | return hashNativeLong(Primitives.unsignedByte(input), 1L); 317 | } 318 | 319 | @Override 320 | public long hashVoid() { 321 | return 0L; 322 | } 323 | 324 | @Override 325 | public long hash(T input, ReadAccess access, long off, long len) { 326 | long seed = seed(); 327 | if (access.byteOrder(input) == LITTLE_ENDIAN) { 328 | return MurmurHash_3.INSTANCE.hash(seed, input, access, off, len); 329 | } else { 330 | return BigEndian.INSTANCE.hash(seed, input, access, off, len); 331 | } 332 | } 333 | } 334 | 335 | private static class AsLongHashFunctionSeeded extends AsLongHashFunction { 336 | private static final long serialVersionUID = 0L; 337 | 338 | private final long seed; 339 | private final transient long voidHash; 340 | 341 | private AsLongHashFunctionSeeded(long seed) { 342 | this.seed = seed; 343 | voidHash = MurmurHash_3.finalize(0L, seed, seed); 344 | } 345 | 346 | @Override 347 | long seed() { 348 | return seed; 349 | } 350 | 351 | @Override 352 | long hashNativeLong(long nativeLong, long len) { 353 | long seed = this.seed; 354 | long h1 = seed ^ mixK1(nativeLong); 355 | long h2 = seed; 356 | return MurmurHash_3.finalize(len, h1, h2); 357 | } 358 | 359 | @Override 360 | public long hashVoid() { 361 | return voidHash; 362 | } 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/hashing/Primitives.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | final class Primitives { 20 | 21 | private Primitives() { 22 | } 23 | 24 | static long unsignedInt(int i) { 25 | return i & 0xFFFFFFFFL; 26 | } 27 | 28 | static int unsignedShort(int s) { 29 | return s & 0xFFFF; 30 | } 31 | 32 | static int unsignedByte(int b) { 33 | return b & 0xFF; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/hashing/XxHash_r39.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import net.openhft.chronicle.algo.bytes.ReadAccess; 20 | 21 | import static java.nio.ByteOrder.LITTLE_ENDIAN; 22 | import static net.openhft.chronicle.algo.hashing.LongHashFunction.NATIVE_LITTLE_ENDIAN; 23 | 24 | /** 25 | * Adapted version of xxHash implementation from 26 | * https://github.com/Cyan4973/xxHash/releases/tag/r39, which is fully compatible with r40 though. 27 | * This implementation provides endian-independant hash values, 28 | * but it's slower on big-endian platforms. 29 | */ 30 | class XxHash_r39 { 31 | private static final XxHash_r39 INSTANCE = new XxHash_r39(); 32 | private static final XxHash_r39 NATIVE_XX = NATIVE_LITTLE_ENDIAN ? 33 | XxHash_r39.INSTANCE : BigEndian.INSTANCE; 34 | 35 | // Primes if treated as unsigned 36 | private static final long P1 = -7046029288634856825L; 37 | private static final long P2 = -4417276706812531889L; 38 | private static final long P3 = 1609587929392839161L; 39 | private static final long P4 = -8796714831421723037L; 40 | private static final long P5 = 2870177450012600261L; 41 | 42 | private XxHash_r39() { 43 | } 44 | 45 | private static long finalize(long hash) { 46 | hash ^= hash >>> 33; 47 | hash *= P2; 48 | hash ^= hash >>> 29; 49 | hash *= P3; 50 | hash ^= hash >>> 32; 51 | return hash; 52 | } 53 | 54 | public static LongHashFunction asLongHashFunctionWithoutSeed() { 55 | return AsLongHashFunction.SEEDLESS_INSTANCE; 56 | } 57 | 58 | public static LongHashFunction asLongHashFunctionWithSeed(long seed) { 59 | return new AsLongHashFunctionSeeded(seed); 60 | } 61 | 62 | long fetch64(ReadAccess access, T in, long off) { 63 | return access.readLong(in, off); 64 | } 65 | 66 | // long because of unsigned nature of original algorithm 67 | long fetch32(ReadAccess access, T in, long off) { 68 | return access.readUnsignedInt(in, off); 69 | } 70 | 71 | // int because of unsigned nature of original algorithm 72 | int fetch8(ReadAccess access, T in, long off) { 73 | return access.readUnsignedByte(in, off); 74 | } 75 | 76 | long toLittleEndian(long v) { 77 | return v; 78 | } 79 | 80 | int toLittleEndian(int v) { 81 | return v; 82 | } 83 | 84 | short toLittleEndian(short v) { 85 | return v; 86 | } 87 | 88 | public long xxHash64(long seed, T input, ReadAccess access, long off, long length) { 89 | long hash; 90 | long remaining = length; 91 | 92 | if (remaining >= 32) { 93 | long v1 = seed + P1 + P2; 94 | long v2 = seed + P2; 95 | long v3 = seed; 96 | long v4 = seed - P1; 97 | 98 | do { 99 | v1 += fetch64(access, input, off) * P2; 100 | v1 = Long.rotateLeft(v1, 31); 101 | v1 *= P1; 102 | 103 | v2 += fetch64(access, input, off + 8) * P2; 104 | v2 = Long.rotateLeft(v2, 31); 105 | v2 *= P1; 106 | 107 | v3 += fetch64(access, input, off + 16) * P2; 108 | v3 = Long.rotateLeft(v3, 31); 109 | v3 *= P1; 110 | 111 | v4 += fetch64(access, input, off + 24) * P2; 112 | v4 = Long.rotateLeft(v4, 31); 113 | v4 *= P1; 114 | 115 | off += 32; 116 | remaining -= 32; 117 | } while (remaining >= 32); 118 | 119 | hash = Long.rotateLeft(v1, 1) 120 | + Long.rotateLeft(v2, 7) 121 | + Long.rotateLeft(v3, 12) 122 | + Long.rotateLeft(v4, 18); 123 | 124 | v1 *= P2; 125 | v1 = Long.rotateLeft(v1, 31); 126 | v1 *= P1; 127 | hash ^= v1; 128 | hash = hash * P1 + P4; 129 | 130 | v2 *= P2; 131 | v2 = Long.rotateLeft(v2, 31); 132 | v2 *= P1; 133 | hash ^= v2; 134 | hash = hash * P1 + P4; 135 | 136 | v3 *= P2; 137 | v3 = Long.rotateLeft(v3, 31); 138 | v3 *= P1; 139 | hash ^= v3; 140 | hash = hash * P1 + P4; 141 | 142 | v4 *= P2; 143 | v4 = Long.rotateLeft(v4, 31); 144 | v4 *= P1; 145 | hash ^= v4; 146 | hash = hash * P1 + P4; 147 | } else { 148 | hash = seed + P5; 149 | } 150 | hash += length; 151 | 152 | while (remaining >= 8) { 153 | long k1 = fetch64(access, input, off); 154 | k1 *= P2; 155 | k1 = Long.rotateLeft(k1, 31); 156 | k1 *= P1; 157 | hash ^= k1; 158 | hash = Long.rotateLeft(hash, 27) * P1 + P4; 159 | off += 8; 160 | remaining -= 8; 161 | } 162 | if (remaining >= 4) { 163 | hash ^= fetch32(access, input, off) * P1; 164 | hash = Long.rotateLeft(hash, 23) * P2 + P3; 165 | off += 4; 166 | remaining -= 4; 167 | } 168 | while (remaining != 0) { 169 | hash ^= fetch8(access, input, off) * P5; 170 | hash = Long.rotateLeft(hash, 11) * P1; 171 | --remaining; 172 | ++off; 173 | } 174 | return finalize(hash); 175 | } 176 | 177 | private static class BigEndian extends XxHash_r39 { 178 | private static final BigEndian INSTANCE = new BigEndian(); 179 | 180 | private BigEndian() { 181 | } 182 | 183 | @Override 184 | long fetch64(ReadAccess access, T in, long off) { 185 | return Long.reverseBytes(super.fetch64(access, in, off)); 186 | } 187 | 188 | @Override 189 | long fetch32(ReadAccess access, T in, long off) { 190 | return Integer.reverseBytes(access.readInt(in, off)) & 0xFFFFFFFFL; 191 | } 192 | // fetch8 is not overloaded, because endianness doesn't matter for single byte 193 | 194 | @Override 195 | long toLittleEndian(long v) { 196 | return Long.reverseBytes(v); 197 | } 198 | 199 | @Override 200 | int toLittleEndian(int v) { 201 | return Integer.reverseBytes(v); 202 | } 203 | 204 | @Override 205 | short toLittleEndian(short v) { 206 | return Short.reverseBytes(v); 207 | } 208 | } 209 | 210 | private static class AsLongHashFunction extends LongHashFunction { 211 | public static final AsLongHashFunction SEEDLESS_INSTANCE = new AsLongHashFunction(); 212 | private static final long serialVersionUID = 0L; 213 | 214 | private Object readResolve() { 215 | return SEEDLESS_INSTANCE; 216 | } 217 | 218 | public long seed() { 219 | return 0L; 220 | } 221 | 222 | @Override 223 | public long hashLong(long input) { 224 | input = NATIVE_XX.toLittleEndian(input); 225 | long hash = seed() + P5 + 8; 226 | input *= P2; 227 | input = Long.rotateLeft(input, 31); 228 | input *= P1; 229 | hash ^= input; 230 | hash = Long.rotateLeft(hash, 27) * P1 + P4; 231 | return XxHash_r39.finalize(hash); 232 | } 233 | 234 | @Override 235 | public long hashInt(int input) { 236 | input = NATIVE_XX.toLittleEndian(input); 237 | long hash = seed() + P5 + 4; 238 | hash ^= Primitives.unsignedInt(input) * P1; 239 | hash = Long.rotateLeft(hash, 23) * P2 + P3; 240 | return XxHash_r39.finalize(hash); 241 | } 242 | 243 | @Override 244 | public long hashShort(short input) { 245 | input = NATIVE_XX.toLittleEndian(input); 246 | long hash = seed() + P5 + 2; 247 | hash ^= Primitives.unsignedByte(input) * P5; 248 | hash = Long.rotateLeft(hash, 11) * P1; 249 | hash ^= Primitives.unsignedByte(input >> 8) * P5; 250 | hash = Long.rotateLeft(hash, 11) * P1; 251 | return XxHash_r39.finalize(hash); 252 | } 253 | 254 | @Override 255 | public long hashChar(char input) { 256 | return hashShort((short) input); 257 | } 258 | 259 | @Override 260 | public long hashByte(byte input) { 261 | long hash = seed() + P5 + 1; 262 | hash ^= Primitives.unsignedByte(input) * P5; 263 | hash = Long.rotateLeft(hash, 11) * P1; 264 | return XxHash_r39.finalize(hash); 265 | } 266 | 267 | @Override 268 | public long hashVoid() { 269 | return XxHash_r39.finalize(P5); 270 | } 271 | 272 | @Override 273 | public long hash(T input, ReadAccess access, long off, long len) { 274 | long seed = seed(); 275 | if (access.byteOrder(input) == LITTLE_ENDIAN) { 276 | return XxHash_r39.INSTANCE.xxHash64(seed, input, access, off, len); 277 | } else { 278 | return BigEndian.INSTANCE.xxHash64(seed, input, access, off, len); 279 | } 280 | } 281 | } 282 | 283 | private static class AsLongHashFunctionSeeded extends AsLongHashFunction { 284 | private static final long serialVersionUID = 0L; 285 | private final long seed; 286 | private final long voidHash; 287 | 288 | private AsLongHashFunctionSeeded(long seed) { 289 | this.seed = seed; 290 | voidHash = XxHash_r39.finalize(seed + P5); 291 | } 292 | 293 | @Override 294 | public long seed() { 295 | return seed; 296 | } 297 | 298 | @Override 299 | public long hashVoid() { 300 | return voidHash; 301 | } 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/internal/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2022 chronicle.software 3 | * 4 | * https://chronicle.software 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /** 20 | * This package and any and all sub-packages contains strictly internal classes for this Chronicle library. 21 | * Internal classes shall never be used directly. 22 | *

23 | * Specifically, the following actions (including, but not limited to) are not allowed 24 | * on internal classes and packages: 25 | *

    26 | *
  • Casting to
  • 27 | *
  • Reflection of any kind
  • 28 | *
  • Explicit Serialize/deserialize
  • 29 | *
30 | *

31 | * The classes in this package and any sub-package are subject to 32 | * changes at any time for any reason. 33 | */ 34 | package net.openhft.chronicle.algo.internal; 35 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/AbstractReadWriteLockState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | public abstract class AbstractReadWriteLockState implements ReadWriteLockState { 20 | 21 | @Override 22 | public boolean tryLock() { 23 | return tryWriteLock(); 24 | } 25 | 26 | @Override 27 | public void unlock() { 28 | writeUnlock(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/AbstractReadWriteLockingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | public abstract class AbstractReadWriteLockingStrategy implements ReadWriteLockingStrategy { 22 | 23 | @Override 24 | public boolean tryLock(Access access, T t, long offset) { 25 | return tryWriteLock(access, t, offset); 26 | } 27 | 28 | @Override 29 | public void unlock(Access access, T t, long offset) { 30 | writeUnlock(access, t, offset); 31 | } 32 | 33 | @Override 34 | public boolean isReadLocked(long state) { 35 | return readLockCount(state) > 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/AcquisitionStrategies.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | import net.openhft.chronicle.core.Jvm; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public final class AcquisitionStrategies { 25 | 26 | private AcquisitionStrategies() { 27 | } 28 | 29 | public static 30 | AcquisitionStrategy spinLoop(long duration, TimeUnit unit) { 31 | return new SpinLoopAcquisitionStrategy<>(duration, unit); 32 | } 33 | 34 | public static 35 | AcquisitionStrategy spinLoopOrFail(long duration, TimeUnit unit) { 36 | return new SpinLoopOrFailAcquisitionStrategy<>(duration, unit); 37 | } 38 | 39 | public static 40 | AcquisitionStrategy spinLoopRegisteringWaitOrFail( 41 | long duration, TimeUnit unit) { 42 | return new SpinLoopWriteWithWaitsAcquisitionStrategy<>(duration, unit); 43 | } 44 | 45 | private static class SpinLoopAcquisitionStrategy 46 | implements AcquisitionStrategy { 47 | private final long durationNanos; 48 | 49 | private SpinLoopAcquisitionStrategy(long duration, TimeUnit unit) { 50 | durationNanos = unit.toNanos(duration); 51 | } 52 | 53 | @Override 54 | public boolean acquire(TryAcquireOperation operation, S strategy, 55 | Access access, T t, long offset) { 56 | if (operation.tryAcquire(strategy, access, t, offset)) 57 | return true; 58 | long deadLineNanos = System.nanoTime() + durationNanos; 59 | beforeLoop(strategy, access, t, offset); 60 | do { 61 | if (operation.tryAcquire(strategy, access, t, offset)) 62 | return true; 63 | Jvm.nanoPause(); 64 | } while (deadLineNanos - System.nanoTime() >= 0L); // overflow-cautious 65 | afterLoop(strategy, access, t, offset); 66 | return end(); 67 | } 68 | 69 | void beforeLoop(S strategy, Access access, T t, long offset) { 70 | } 71 | 72 | void afterLoop(S strategy, Access access, T t, long offset) { 73 | } 74 | 75 | boolean end() { 76 | return false; 77 | } 78 | } 79 | 80 | private static class SpinLoopOrFailAcquisitionStrategy 81 | extends SpinLoopAcquisitionStrategy { 82 | 83 | private SpinLoopOrFailAcquisitionStrategy(long duration, TimeUnit unit) { 84 | super(duration, unit); 85 | } 86 | 87 | @Override 88 | boolean end() { 89 | throw new IllegalStateException("Failed to acquire the lock"); 90 | } 91 | } 92 | 93 | private static class SpinLoopWriteWithWaitsAcquisitionStrategy< 94 | S extends ReadWriteWithWaitsLockingStrategy> 95 | extends SpinLoopOrFailAcquisitionStrategy { 96 | 97 | private SpinLoopWriteWithWaitsAcquisitionStrategy(long duration, TimeUnit unit) { 98 | super(duration, unit); 99 | } 100 | 101 | @Override 102 | void beforeLoop(S strategy, Access access, T t, long offset) { 103 | strategy.registerWait(access, t, offset); 104 | } 105 | 106 | @Override 107 | void afterLoop(S strategy, Access access, T t, long offset) { 108 | strategy.deregisterWait(access, t, offset); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/AcquisitionStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | public interface AcquisitionStrategy { 22 | 23 | boolean acquire( 24 | TryAcquireOperation operation, S strategy, 25 | Access access, T t, long offset) throws E; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/LockState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | public interface LockState { 20 | 21 | boolean tryLock(); 22 | 23 | void unlock(); 24 | 25 | void reset(); 26 | 27 | long getState(); 28 | 29 | LockingStrategy lockingStrategy(); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/LockingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | import net.openhft.chronicle.algo.bytes.ReadAccess; 21 | 22 | public interface LockingStrategy { 23 | 24 | boolean tryLock(Access access, T t, long offset); 25 | 26 | void unlock(Access access, T t, long offset); 27 | 28 | void reset(Access access, T t, long offset); 29 | 30 | long resetState(); 31 | 32 | long getState(ReadAccess access, T t, long offset); 33 | 34 | boolean isLocked(long state); 35 | 36 | int lockCount(long state); 37 | 38 | String toString(long state); 39 | 40 | int sizeInBytes(); 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/ReadWriteLockState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | public interface ReadWriteLockState extends LockState { 20 | 21 | boolean tryReadLock(); 22 | 23 | boolean tryWriteLock(); 24 | 25 | boolean tryUpgradeReadToWriteLock(); 26 | 27 | void readUnlock(); 28 | 29 | void writeUnlock(); 30 | 31 | void downgradeWriteToReadLock(); 32 | 33 | @Override 34 | ReadWriteLockingStrategy lockingStrategy(); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/ReadWriteLockingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | public interface ReadWriteLockingStrategy extends LockingStrategy { 22 | 23 | boolean tryReadLock(Access access, T t, long offset); 24 | 25 | boolean tryWriteLock(Access access, T t, long offset); 26 | 27 | boolean tryUpgradeReadToWriteLock(Access access, T t, long offset); 28 | 29 | void readUnlock(Access access, T t, long offset); 30 | 31 | void writeUnlock(Access access, T t, long offset); 32 | 33 | void downgradeWriteToReadLock(Access access, T t, long offset); 34 | 35 | boolean isReadLocked(long state); 36 | 37 | boolean isWriteLocked(long state); 38 | 39 | int readLockCount(long state); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/ReadWriteUpdateLockState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | public interface ReadWriteUpdateLockState extends ReadWriteLockState { 20 | 21 | boolean tryUpdateLock(); 22 | 23 | boolean tryUpgradeReadToUpdateLock(); 24 | 25 | boolean tryUpgradeUpdateToWriteLock(); 26 | 27 | void updateUnlock(); 28 | 29 | void downgradeUpdateToReadLock(); 30 | 31 | void downgradeWriteToUpdateLock(); 32 | 33 | @Override 34 | ReadWriteUpdateLockingStrategy lockingStrategy(); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/ReadWriteUpdateLockingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | /** 22 | * Logic of read-write-update lock state transitions. 23 | *

24 | * Read lock - could be several at the same time. 25 | * Update lock - doesn't block reads, but couldn't be several update locks at the same time 26 | * Write lock - exclusive 27 | */ 28 | public interface ReadWriteUpdateLockingStrategy extends ReadWriteLockingStrategy { 29 | 30 | boolean tryUpdateLock(Access access, T t, long offset); 31 | 32 | boolean tryUpgradeReadToUpdateLock(Access access, T t, long offset); 33 | 34 | boolean tryUpgradeUpdateToWriteLock(Access access, T t, long offset); 35 | 36 | void updateUnlock(Access access, T t, long offset); 37 | 38 | void downgradeUpdateToReadLock(Access access, T t, long offset); 39 | 40 | void downgradeWriteToUpdateLock(Access access, T t, long offset); 41 | 42 | boolean isUpdateLocked(long state); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/ReadWriteUpdateWithWaitsLockState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | public interface ReadWriteUpdateWithWaitsLockState 20 | extends ReadWriteUpdateLockState, ReadWriteWithWaitsLockState { 21 | 22 | boolean tryUpgradeUpdateToWriteLockAndDeregisterWait(); 23 | 24 | @Override 25 | ReadWriteUpdateWithWaitsLockingStrategy lockingStrategy(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/ReadWriteUpdateWithWaitsLockingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | public interface ReadWriteUpdateWithWaitsLockingStrategy 22 | extends ReadWriteUpdateLockingStrategy, ReadWriteWithWaitsLockingStrategy { 23 | 24 | boolean tryUpgradeUpdateToWriteLockAndDeregisterWait( 25 | Access access, T t, long offset); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/ReadWriteWithWaitsLockState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | public interface ReadWriteWithWaitsLockState extends ReadWriteLockState { 20 | 21 | void registerWait(); 22 | 23 | void deregisterWait(); 24 | 25 | boolean tryWriteLockAndDeregisterWait(); 26 | 27 | boolean tryUpgradeReadToWriteLockAndDeregisterWait(); 28 | 29 | void resetKeepingWaits(); 30 | 31 | @Override 32 | ReadWriteWithWaitsLockingStrategy lockingStrategy(); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/ReadWriteWithWaitsLockingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | public interface ReadWriteWithWaitsLockingStrategy extends ReadWriteLockingStrategy { 22 | 23 | void registerWait(Access access, T t, long offset); 24 | 25 | void deregisterWait(Access access, T t, long offset); 26 | 27 | boolean tryWriteLockAndDeregisterWait(Access access, T t, long offset); 28 | 29 | boolean tryUpgradeReadToWriteLockAndDeregisterWait( 30 | Access access, T t, long offset); 31 | 32 | void resetKeepingWaits(Access access, T t, long offset); 33 | 34 | int waitCount(long state); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/TryAcquireOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | public interface TryAcquireOperation { 22 | 23 | boolean tryAcquire(S strategy, Access access, T t, long offset); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/TryAcquireOperations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | 21 | public final class TryAcquireOperations { 22 | 23 | private static final TryAcquireOperation LOCK = 24 | new TryAcquireOperation() { 25 | @Override 26 | public boolean tryAcquire(LockingStrategy strategy, 27 | Access access, T obj, long offset) { 28 | return strategy.tryLock(access, obj, offset); 29 | } 30 | }; 31 | private static final TryAcquireOperation READ_LOCK = 32 | new TryAcquireOperation() { 33 | @Override 34 | public boolean tryAcquire(ReadWriteLockingStrategy strategy, 35 | Access access, T obj, long offset) { 36 | return strategy.tryReadLock(access, obj, offset); 37 | } 38 | }; 39 | private static final TryAcquireOperation UPGRADE_READ_TO_WRITE_LOCK = 40 | new TryAcquireOperation() { 41 | @Override 42 | public boolean tryAcquire(ReadWriteLockingStrategy strategy, 43 | Access access, T obj, long offset) { 44 | return strategy.tryUpgradeReadToWriteLock(access, obj, offset); 45 | } 46 | }; 47 | private static final TryAcquireOperation WRITE_LOCK = 48 | new TryAcquireOperation() { 49 | @Override 50 | public boolean tryAcquire(ReadWriteLockingStrategy strategy, 51 | Access access, T obj, long offset) { 52 | return strategy.tryWriteLock(access, obj, offset); 53 | } 54 | }; 55 | private static final TryAcquireOperation 56 | UPGRADE_READ_TO_WRITE_LOCK_AND_DEREGISTER_WAIT = 57 | new TryAcquireOperation() { 58 | @Override 59 | public boolean tryAcquire(ReadWriteWithWaitsLockingStrategy strategy, 60 | Access access, T obj, long offset) { 61 | return strategy.tryUpgradeReadToWriteLockAndDeregisterWait(access, obj, offset); 62 | } 63 | }; 64 | private static final TryAcquireOperation 65 | WRITE_LOCK_AND_DEREGISTER_WAIT = 66 | new TryAcquireOperation() { 67 | @Override 68 | public boolean tryAcquire(ReadWriteWithWaitsLockingStrategy strategy, 69 | Access access, T obj, long offset) { 70 | return strategy.tryWriteLockAndDeregisterWait(access, obj, offset); 71 | } 72 | }; 73 | private static final TryAcquireOperation UPDATE_LOCK = 74 | new TryAcquireOperation() { 75 | @Override 76 | public boolean tryAcquire(ReadWriteUpdateLockingStrategy strategy, 77 | Access access, T obj, long offset) { 78 | return strategy.tryUpdateLock(access, obj, offset); 79 | } 80 | }; 81 | private static final TryAcquireOperation 82 | UPGRADE_READ_TO_UPDATE_LOCK = 83 | new TryAcquireOperation() { 84 | @Override 85 | public boolean tryAcquire(ReadWriteUpdateLockingStrategy strategy, 86 | Access access, T obj, long offset) { 87 | return strategy.tryUpgradeReadToUpdateLock(access, obj, offset); 88 | } 89 | }; 90 | private static final TryAcquireOperation 91 | UPGRADE_UPDATE_TO_WRITE_LOCK = 92 | new TryAcquireOperation() { 93 | @Override 94 | public boolean tryAcquire(ReadWriteUpdateLockingStrategy strategy, 95 | Access access, T obj, long offset) { 96 | return strategy.tryUpgradeUpdateToWriteLock(access, obj, offset); 97 | } 98 | }; 99 | private static final TryAcquireOperation 100 | UPGRADE_UPDATE_TO_WRITE_LOCK_AND_DEREGISTER_WAIT = 101 | new TryAcquireOperation() { 102 | @Override 103 | public boolean tryAcquire(ReadWriteUpdateWithWaitsLockingStrategy strategy, 104 | Access access, T obj, long offset) { 105 | return strategy.tryUpgradeUpdateToWriteLockAndDeregisterWait( 106 | access, obj, offset); 107 | } 108 | }; 109 | 110 | private TryAcquireOperations() { 111 | } 112 | 113 | public static TryAcquireOperation lock() { 114 | return LOCK; 115 | } 116 | 117 | public static TryAcquireOperation readLock() { 118 | return READ_LOCK; 119 | } 120 | 121 | public static TryAcquireOperation upgradeReadToWriteLock() { 122 | return UPGRADE_READ_TO_WRITE_LOCK; 123 | } 124 | 125 | public static TryAcquireOperation writeLock() { 126 | return WRITE_LOCK; 127 | } 128 | 129 | public static TryAcquireOperation 130 | upgradeReadToWriteLockAndDeregisterWait() { 131 | return UPGRADE_READ_TO_WRITE_LOCK_AND_DEREGISTER_WAIT; 132 | } 133 | 134 | public static TryAcquireOperation 135 | writeLockAndDeregisterWait() { 136 | return WRITE_LOCK_AND_DEREGISTER_WAIT; 137 | } 138 | 139 | public static TryAcquireOperation updateLock() { 140 | return UPDATE_LOCK; 141 | } 142 | 143 | public static TryAcquireOperation upgradeReadToUpdateLock() { 144 | return UPGRADE_READ_TO_UPDATE_LOCK; 145 | } 146 | 147 | public static TryAcquireOperation upgradeUpdateToWriteLock() { 148 | return UPGRADE_UPDATE_TO_WRITE_LOCK; 149 | } 150 | 151 | public static TryAcquireOperation 152 | upgradeUpdateToWriteLockAndDeregisterWait() { 153 | 154 | return UPGRADE_UPDATE_TO_WRITE_LOCK_AND_DEREGISTER_WAIT; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/VanillaReadWriteUpdateWithWaitsLockingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | import net.openhft.chronicle.algo.bytes.ReadAccess; 21 | 22 | import static java.nio.ByteOrder.LITTLE_ENDIAN; 23 | import static java.nio.ByteOrder.nativeOrder; 24 | 25 | public final class VanillaReadWriteUpdateWithWaitsLockingStrategy 26 | extends AbstractReadWriteLockingStrategy 27 | implements ReadWriteUpdateWithWaitsLockingStrategy { 28 | 29 | static final long COUNT_WORD_OFFSET = 0L; 30 | static final long WAIT_WORD_OFFSET = COUNT_WORD_OFFSET + 4L; 31 | static final int COUNT_WORD_SHIFT = nativeOrder() == LITTLE_ENDIAN ? 0 : 32; 32 | static final int WAIT_WORD_SHIFT = nativeOrder() == LITTLE_ENDIAN ? 32 : 0; 33 | static final int READ_BITS = 30; 34 | static final int MAX_READ = (1 << READ_BITS) - 1; 35 | static final int READ_MASK = MAX_READ; 36 | static final int READ_PARTY = 1; 37 | static final int UPDATE_PARTY = 1 << READ_BITS; 38 | static final int WRITE_LOCKED_COUNT_WORD = UPDATE_PARTY << 1; 39 | static final int MAX_WAIT = Integer.MAX_VALUE; 40 | static final int WAIT_PARTY = 1; 41 | private static final long UNSIGNED_INT_MASK = 0xFFFFFFFFL; 42 | private static final ReadWriteUpdateWithWaitsLockingStrategy INSTANCE = 43 | new VanillaReadWriteUpdateWithWaitsLockingStrategy(); 44 | 45 | private VanillaReadWriteUpdateWithWaitsLockingStrategy() { 46 | } 47 | 48 | public static ReadWriteUpdateWithWaitsLockingStrategy instance() { 49 | return INSTANCE; 50 | } 51 | 52 | private static long getLockWord(ReadAccess access, T t, long offset) { 53 | return access.readVolatileLong(t, offset); 54 | } 55 | 56 | private static boolean casLockWord( 57 | Access access, T t, long offset, long expected, long x) { 58 | return access.compareAndSwapLong(t, offset, expected, x); 59 | } 60 | 61 | private static int countWord(long lockWord) { 62 | return (int) (lockWord >> COUNT_WORD_SHIFT); 63 | } 64 | 65 | private static int waitWord(long lockWord) { 66 | return (int) (lockWord >> WAIT_WORD_SHIFT); 67 | } 68 | 69 | private static long lockWord(int countWord, int waitWord) { 70 | return ((((long) countWord) & UNSIGNED_INT_MASK) << COUNT_WORD_SHIFT) | 71 | ((((long) waitWord) & UNSIGNED_INT_MASK) << WAIT_WORD_SHIFT); 72 | } 73 | 74 | private static int getCountWord(Access access, T t, long offset) { 75 | return access.readVolatileInt(t, offset + COUNT_WORD_OFFSET); 76 | } 77 | 78 | private static boolean casCountWord( 79 | Access access, T t, long offset, int expected, int x) { 80 | return access.compareAndSwapInt(t, offset + COUNT_WORD_OFFSET, expected, x); 81 | } 82 | 83 | private static void putCountWord( 84 | Access access, T t, long offset, int countWord) { 85 | access.writeOrderedInt(t, offset + COUNT_WORD_OFFSET, countWord); 86 | } 87 | 88 | private static boolean writeLocked(int countWord) { 89 | return countWord == WRITE_LOCKED_COUNT_WORD; 90 | } 91 | 92 | private static boolean updateLocked(int countWord) { 93 | return (countWord & UPDATE_PARTY) != 0; 94 | } 95 | 96 | private static void checkUpdateLocked(int countWord) { 97 | if (!updateLocked(countWord)) 98 | throw new IllegalMonitorStateException("Expected update lock"); 99 | } 100 | 101 | private static int readCount(int countWord) { 102 | return countWord & READ_MASK; 103 | } 104 | 105 | private static void checkReadLocked(int countWord) { 106 | if (readCount(countWord) <= 0) 107 | throw new IllegalMonitorStateException("Expected read lock"); 108 | } 109 | 110 | private static void checkReadCountForIncrement(int countWord) { 111 | if (readCount(countWord) == MAX_READ) { 112 | throw new IllegalMonitorStateException( 113 | "Lock count reached the limit of " + MAX_READ); 114 | } 115 | } 116 | 117 | private static int getWaitWord(Access access, T t, long offset) { 118 | return access.readVolatileInt(t, offset + WAIT_WORD_OFFSET); 119 | } 120 | 121 | private static boolean casWaitWord( 122 | Access access, T t, long offset, int expected, int x) { 123 | return access.compareAndSwapInt(t, offset + WAIT_WORD_OFFSET, expected, x); 124 | } 125 | 126 | private static void checkWaitWordForIncrement(int waitWord) { 127 | if (waitWord == MAX_WAIT) { 128 | throw new IllegalMonitorStateException( 129 | "Wait count reached the limit of " + MAX_WAIT); 130 | } 131 | } 132 | 133 | private static void checkWaitWordForDecrement(int waitWord) { 134 | if (waitWord == 0) { 135 | throw new IllegalMonitorStateException( 136 | "Wait count underflowed"); 137 | } 138 | } 139 | 140 | private static boolean tryWriteLockAndDeregisterWait0( 141 | Access access, T t, long offset, long lockWord) { 142 | int waitWord = waitWord(lockWord); 143 | checkWaitWordForDecrement(waitWord); 144 | return casLockWord(access, t, offset, lockWord, 145 | lockWord(WRITE_LOCKED_COUNT_WORD, waitWord - WAIT_PARTY)); 146 | } 147 | 148 | private static boolean checkExclusiveUpdateLocked(int countWord) { 149 | checkUpdateLocked(countWord); 150 | return countWord == UPDATE_PARTY; 151 | } 152 | 153 | private static void checkWriteLockedAndPut( 154 | Access access, T t, long offset, int countWord) { 155 | if (!casCountWord(access, t, offset, WRITE_LOCKED_COUNT_WORD, countWord)) 156 | throw new IllegalMonitorStateException("Expected write lock"); 157 | } 158 | 159 | @Override 160 | public long resetState() { 161 | return 0L; 162 | } 163 | 164 | @Override 165 | public void reset(Access access, T t, long offset) { 166 | access.writeOrderedLong(t, offset, 0L); 167 | } 168 | 169 | @Override 170 | public void resetKeepingWaits(Access access, T t, long offset) { 171 | putCountWord(access, t, offset, 0); 172 | } 173 | 174 | @Override 175 | public boolean tryReadLock(Access access, T t, long offset) { 176 | long lockWord = getLockWord(access, t, offset); 177 | int countWord = countWord(lockWord); 178 | if (!writeLocked(countWord) && waitWord(lockWord) == 0) { 179 | checkReadCountForIncrement(countWord); 180 | return casCountWord(access, t, offset, countWord, countWord + READ_PARTY); 181 | } 182 | return false; 183 | } 184 | 185 | @Override 186 | public boolean tryUpgradeReadToUpdateLock(Access access, T t, long offset) { 187 | int countWord = getCountWord(access, t, offset); 188 | checkReadLocked(countWord); 189 | return !updateLocked(countWord) && 190 | casCountWord(access, t, offset, countWord, countWord - READ_PARTY + UPDATE_PARTY); 191 | } 192 | 193 | @Override 194 | public boolean tryUpgradeReadToWriteLock(Access access, T t, long offset) { 195 | if (casCountWord(access, t, offset, READ_PARTY, WRITE_LOCKED_COUNT_WORD)) { 196 | return true; 197 | } else { 198 | int countWord = getCountWord(access, t, offset); 199 | checkReadLocked(countWord); 200 | return false; 201 | } 202 | } 203 | 204 | @Override 205 | public boolean tryUpgradeReadToWriteLockAndDeregisterWait( 206 | Access access, T t, long offset) { 207 | long lockWord = getLockWord(access, t, offset); 208 | int countWord = countWord(lockWord); 209 | checkReadLocked(countWord); 210 | return countWord == READ_PARTY && 211 | tryWriteLockAndDeregisterWait0(access, t, offset, lockWord); 212 | } 213 | 214 | @Override 215 | public boolean tryUpdateLock(Access access, T t, long offset) { 216 | long lockWord = getLockWord(access, t, offset); 217 | int countWord = countWord(lockWord); 218 | if (!updateLocked(countWord) && !writeLocked(countWord) && waitWord(lockWord) == 0) { 219 | return casCountWord(access, t, offset, countWord, countWord + UPDATE_PARTY); 220 | } 221 | return false; 222 | } 223 | 224 | @Override 225 | public boolean tryWriteLock(Access access, T t, long offset) { 226 | return casCountWord(access, t, offset, 0, WRITE_LOCKED_COUNT_WORD); 227 | } 228 | 229 | @Override 230 | public boolean tryWriteLockAndDeregisterWait( 231 | Access access, T t, long offset) { 232 | long lockWord = getLockWord(access, t, offset); 233 | int countWord = countWord(lockWord); 234 | return countWord == 0 && tryWriteLockAndDeregisterWait0(access, t, offset, lockWord); 235 | } 236 | 237 | @Override 238 | public void registerWait(Access access, T t, long offset) { 239 | while (true) { 240 | int waitWord = getWaitWord(access, t, offset); 241 | checkWaitWordForIncrement(waitWord); 242 | if (casWaitWord(access, t, offset, waitWord, waitWord + WAIT_PARTY)) 243 | return; 244 | } 245 | } 246 | 247 | @Override 248 | public void deregisterWait(Access access, T t, long offset) { 249 | while (true) { 250 | int waitWord = getWaitWord(access, t, offset); 251 | checkWaitWordForDecrement(waitWord); 252 | if (casWaitWord(access, t, offset, waitWord, waitWord - WAIT_PARTY)) 253 | return; 254 | } 255 | } 256 | 257 | @Override 258 | public boolean tryUpgradeUpdateToWriteLock(Access access, T t, long offset) { 259 | if (casCountWord(access, t, offset, UPDATE_PARTY, WRITE_LOCKED_COUNT_WORD)) { 260 | return true; 261 | } else { 262 | int countWord = getCountWord(access, t, offset); 263 | checkUpdateLocked(countWord); 264 | return false; 265 | } 266 | } 267 | 268 | @Override 269 | public boolean tryUpgradeUpdateToWriteLockAndDeregisterWait( 270 | Access access, T t, long offset) { 271 | long lockWord = getLockWord(access, t, offset); 272 | int countWord = countWord(lockWord); 273 | return checkExclusiveUpdateLocked(countWord) && 274 | tryWriteLockAndDeregisterWait0(access, t, offset, lockWord); 275 | } 276 | 277 | @Override 278 | public void readUnlock(Access access, T t, long offset) { 279 | while (true) { 280 | int countWord = getCountWord(access, t, offset); 281 | checkReadLocked(countWord); 282 | if (casCountWord(access, t, offset, countWord, countWord - READ_PARTY)) 283 | return; 284 | } 285 | } 286 | 287 | @Override 288 | public void updateUnlock(Access access, T t, long offset) { 289 | while (true) { 290 | int countWord = getCountWord(access, t, offset); 291 | checkUpdateLocked(countWord); 292 | if (casCountWord(access, t, offset, countWord, countWord - UPDATE_PARTY)) { 293 | return; 294 | } 295 | } 296 | } 297 | 298 | @Override 299 | public void downgradeUpdateToReadLock(Access access, T t, long offset) { 300 | while (true) { 301 | int countWord = getCountWord(access, t, offset); 302 | checkUpdateLocked(countWord); 303 | checkReadCountForIncrement(countWord); 304 | if (casCountWord(access, t, offset, countWord, countWord - UPDATE_PARTY + READ_PARTY)) { 305 | return; 306 | } 307 | } 308 | } 309 | 310 | @Override 311 | public void writeUnlock(Access access, T t, long offset) { 312 | checkWriteLockedAndPut(access, t, offset, 0); 313 | } 314 | 315 | @Override 316 | public void downgradeWriteToUpdateLock(Access access, T t, long offset) { 317 | checkWriteLockedAndPut(access, t, offset, UPDATE_PARTY); 318 | } 319 | 320 | @Override 321 | public boolean isUpdateLocked(long state) { 322 | return updateLocked(countWord(state)); 323 | } 324 | 325 | @Override 326 | public void downgradeWriteToReadLock(Access access, T t, long offset) { 327 | checkWriteLockedAndPut(access, t, offset, READ_PARTY); 328 | } 329 | 330 | @Override 331 | public long getState(ReadAccess access, T t, long offset) { 332 | return getLockWord(access, t, offset); 333 | } 334 | 335 | @Override 336 | public int readLockCount(long state) { 337 | return readCount(countWord(state)); 338 | } 339 | 340 | @Override 341 | public boolean isWriteLocked(long state) { 342 | return writeLocked(countWord(state)); 343 | } 344 | 345 | @Override 346 | public int waitCount(long state) { 347 | return waitWord(state); 348 | } 349 | 350 | @Override 351 | public boolean isLocked(long state) { 352 | return countWord(state) != 0; 353 | } 354 | 355 | @Override 356 | public int lockCount(long state) { 357 | int countWord = countWord(state); 358 | int lockCount = readCount(countWord); 359 | if (lockCount > 0) { 360 | return lockCount + (updateLocked(countWord) ? 1 : 0); 361 | } else { 362 | return writeLocked(countWord) ? 1 : 0; 363 | } 364 | } 365 | 366 | @Override 367 | public String toString(long state) { 368 | return "[read locks = " + readLockCount(state) + 369 | ", update locked = " + isUpdateLocked(state) + 370 | ", write locked = " + isWriteLocked(state) + 371 | ", waits = " + waitCount(state) + "]"; 372 | } 373 | 374 | @Override 375 | public int sizeInBytes() { 376 | return 8; 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /src/main/java/net/openhft/chronicle/algo/locks/VanillaReadWriteWithWaitsLockingStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.locks; 18 | 19 | import net.openhft.chronicle.algo.bytes.Access; 20 | import net.openhft.chronicle.algo.bytes.ReadAccess; 21 | 22 | public final class VanillaReadWriteWithWaitsLockingStrategy extends AbstractReadWriteLockingStrategy 23 | implements ReadWriteWithWaitsLockingStrategy { 24 | 25 | static final int RW_LOCK_LIMIT = 30; 26 | static final long RW_READ_LOCKED = 1L; 27 | static final long RW_WRITE_WAITING = 1L << RW_LOCK_LIMIT; 28 | static final long RW_WRITE_LOCKED = 1L << 2 * RW_LOCK_LIMIT; 29 | static final int RW_LOCK_MASK = (1 << RW_LOCK_LIMIT) - 1; 30 | private static final ReadWriteWithWaitsLockingStrategy INSTANCE = 31 | new VanillaReadWriteWithWaitsLockingStrategy(); 32 | 33 | private VanillaReadWriteWithWaitsLockingStrategy() { 34 | } 35 | 36 | public static ReadWriteWithWaitsLockingStrategy instance() { 37 | return INSTANCE; 38 | } 39 | 40 | static int rwReadLocked(long lock) { 41 | return (int) (lock & RW_LOCK_MASK); 42 | } 43 | 44 | static int rwWriteWaiting(long lock) { 45 | return (int) ((lock >>> RW_LOCK_LIMIT) & RW_LOCK_MASK); 46 | } 47 | 48 | static int rwWriteLocked(long lock) { 49 | return (int) (lock >>> (2 * RW_LOCK_LIMIT)); 50 | } 51 | 52 | static long read(ReadAccess access, T t, long offset) { 53 | return access.readVolatileLong(t, offset); 54 | } 55 | 56 | static boolean cas(Access access, T t, long offset, long expected, long x) { 57 | return access.compareAndSwapLong(t, offset, expected, x); 58 | } 59 | 60 | @Override 61 | public boolean tryReadLock(Access access, T t, long offset) { 62 | long lock = read(access, t, offset); 63 | int writersWaiting = rwWriteWaiting(lock); 64 | int writersLocked = rwWriteLocked(lock); 65 | // readers wait for waiting writers 66 | if (writersLocked <= 0 && writersWaiting <= 0) { 67 | // increment readers locked. 68 | int readersLocked = rwReadLocked(lock); 69 | if (readersLocked >= RW_LOCK_MASK) 70 | throw new IllegalMonitorStateException("readersLocked has reached a limit of " + 71 | readersLocked); 72 | return cas(access, t, offset, lock, lock + RW_READ_LOCKED); 73 | } 74 | return false; 75 | } 76 | 77 | @Override 78 | public boolean tryWriteLock(Access access, T t, long offset) { 79 | long lock = read(access, t, offset); 80 | int readersLocked = rwReadLocked(lock); 81 | int writersLocked = rwWriteLocked(lock); 82 | // writers don't wait for waiting readers. 83 | if (readersLocked <= 0 && writersLocked <= 0) { 84 | return cas(access, t, offset, lock, lock + RW_WRITE_LOCKED); 85 | } 86 | return false; 87 | } 88 | 89 | @Override 90 | public boolean tryUpgradeReadToWriteLock(Access access, T t, long offset) { 91 | throw new UnsupportedOperationException("not implemented yet"); 92 | } 93 | 94 | @Override 95 | public void readUnlock(Access access, T t, long offset) { 96 | for (; ; ) { 97 | long lock = read(access, t, offset); 98 | int readersLocked = rwReadLocked(lock); 99 | if (readersLocked <= 0) 100 | throw new IllegalMonitorStateException("readerLock underflow"); 101 | if (cas(access, t, offset, lock, lock - RW_READ_LOCKED)) 102 | return; 103 | } 104 | } 105 | 106 | @Override 107 | public void writeUnlock(Access access, T t, long offset) { 108 | for (; ; ) { 109 | long lock = read(access, t, offset); 110 | int writersLocked = rwWriteLocked(lock); 111 | if (writersLocked != 1) 112 | throw new IllegalMonitorStateException("writersLock underflow " + writersLocked); 113 | if (cas(access, t, offset, lock, lock - RW_WRITE_LOCKED)) 114 | return; 115 | } 116 | } 117 | 118 | @Override 119 | public void downgradeWriteToReadLock(Access access, T t, long offset) { 120 | throw new UnsupportedOperationException("not implemented yet"); 121 | } 122 | 123 | @Override 124 | public boolean isWriteLocked(long state) { 125 | return rwWriteLocked(state) > 0; 126 | } 127 | 128 | @Override 129 | public int readLockCount(long state) { 130 | return rwReadLocked(state); 131 | } 132 | 133 | @Override 134 | public void reset(Access access, T t, long offset) { 135 | access.writeOrderedLong(t, offset, 0L); 136 | } 137 | 138 | @Override 139 | public void resetKeepingWaits(Access access, T t, long offset) { 140 | while (true) { 141 | long lock = read(access, t, offset); 142 | long onlyWaits = lock & ((long) RW_LOCK_MASK) << RW_LOCK_LIMIT; 143 | if (cas(access, t, offset, lock, onlyWaits)) 144 | return; 145 | } 146 | } 147 | 148 | @Override 149 | public void registerWait(Access access, T t, long offset) { 150 | for (; ; ) { 151 | long lock = read(access, t, offset); 152 | int writersWaiting = rwWriteWaiting(lock); 153 | if (writersWaiting >= RW_LOCK_MASK) 154 | throw new IllegalMonitorStateException("writersWaiting has reached a limit of " + 155 | writersWaiting); 156 | if (cas(access, t, offset, lock, lock + RW_WRITE_WAITING)) 157 | break; 158 | } 159 | } 160 | 161 | @Override 162 | public void deregisterWait(Access access, T t, long offset) { 163 | for (; ; ) { 164 | long lock = read(access, t, offset); 165 | int writersWaiting = rwWriteWaiting(lock); 166 | if (writersWaiting <= 0) 167 | throw new IllegalMonitorStateException("writersWaiting has underflowed"); 168 | if (cas(access, t, offset, lock, lock - RW_WRITE_WAITING)) 169 | break; 170 | } 171 | } 172 | 173 | @Override 174 | public boolean tryWriteLockAndDeregisterWait( 175 | Access access, T t, long offset) { 176 | long lock = read(access, t, offset); 177 | int readersLocked = rwReadLocked(lock); 178 | int writersWaiting = rwWriteWaiting(lock); 179 | int writersLocked = rwWriteLocked(lock); 180 | if (readersLocked <= 0 && writersLocked <= 0) { 181 | // increment readers locked. 182 | if (writersWaiting <= 0) 183 | throw new IllegalMonitorStateException("writersWaiting has underflowed"); 184 | // add to the readLock count and decrease the readWaiting count. 185 | return cas(access, t, offset, lock, lock + RW_WRITE_LOCKED - RW_WRITE_WAITING); 186 | } 187 | return false; 188 | } 189 | 190 | @Override 191 | public boolean tryUpgradeReadToWriteLockAndDeregisterWait( 192 | Access access, T t, long offset) { 193 | throw new UnsupportedOperationException("not implemented yet"); 194 | } 195 | 196 | @Override 197 | public long resetState() { 198 | return 0L; 199 | } 200 | 201 | @Override 202 | public long getState(ReadAccess access, T t, long offset) { 203 | return read(access, t, offset); 204 | } 205 | 206 | @Override 207 | public int waitCount(long state) { 208 | return rwWriteWaiting(state); 209 | } 210 | 211 | @Override 212 | public boolean isLocked(long state) { 213 | return isReadLocked(state) || isWriteLocked(state); 214 | } 215 | 216 | @Override 217 | public int lockCount(long state) { 218 | return rwReadLocked(state) + rwWriteLocked(state); 219 | } 220 | 221 | @Override 222 | public String toString(long state) { 223 | return "[read locks = " + readLockCount(state) + 224 | ", write locked = " + isWriteLocked(state) + 225 | ", waits = " + waitCount(state) + "]"; 226 | } 227 | 228 | @Override 229 | public int sizeInBytes() { 230 | return 8; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/City64MoreTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import net.openhft.chronicle.algo.bytes.NativeAccess; 20 | import net.openhft.chronicle.bytes.NativeBytes; 21 | import org.junit.Ignore; 22 | import org.junit.Test; 23 | 24 | import java.security.SecureRandom; 25 | 26 | /** 27 | * Created by peter on 28/06/15. 28 | */ 29 | public class City64MoreTest { 30 | @Test 31 | @Ignore("Long running, avg score = 6834") 32 | public void testSmallRandomness() { 33 | long time = 0, timeCount = 0; 34 | long scoreSum = 0; 35 | for (int t = 1; t < 500; t++) { 36 | long[] hashs = new long[8192]; 37 | NativeBytes b = NativeBytes.nativeBytes(8); 38 | for (int i = 0; i < hashs.length; i++) { 39 | b.clear(); 40 | b.append(t); 41 | b.appendUtf8('-'); 42 | b.append(i); 43 | long start = System.nanoTime(); 44 | hashs[i] = LongHashFunction.city_1_1().hash((Object) null, NativeAccess.instance(), b.addressForRead(b.readPosition()), b.readRemaining()); 45 | time += System.nanoTime() - start; 46 | timeCount++; 47 | } 48 | long score = 0; 49 | for (int i = 0; i < hashs.length - 1; i++) 50 | for (int j = i + 1; j < hashs.length; j++) { 51 | long diff = hashs[j] ^ hashs[i]; 52 | int diffBC = Long.bitCount(diff); 53 | if (diffBC < 18) { 54 | long d = 1L << (17 - diffBC); 55 | score += d; 56 | } 57 | } 58 | scoreSum += score; 59 | if (t % 50 == 0) 60 | System.out.println(t + " - Score: " + score); 61 | } 62 | System.out.println("Average score: " + scoreSum / 500); 63 | System.out.printf("Average time %.3f us%n", time / timeCount / 1e3); 64 | } 65 | 66 | @Ignore("Long running, avg score = 6852") 67 | @Test 68 | public void testRandomness() { 69 | long time = 0, timeCount = 0; 70 | long scoreSum = 0; 71 | for (int t = 0; t < 500; t++) { 72 | long[] hashs = new long[8192]; 73 | NativeBytes b = NativeBytes.nativeBytes(hashs.length / 64); 74 | byte[] init = new byte[hashs.length / 64]; 75 | new SecureRandom().nextBytes(init); 76 | for (int i = 0; i < hashs.length; i++) { 77 | b.clear(); 78 | b.write(init); 79 | 80 | b.writeLong(i >> 6 << 3, 1L << i); 81 | b.readLimit(hashs.length / 8); 82 | long start = System.nanoTime(); 83 | hashs[i] = LongHashFunction.city_1_1().hash((Object) null, NativeAccess.instance(), b.addressForRead(b.readPosition()), b.readRemaining()); 84 | time += System.nanoTime() - start; 85 | timeCount++; 86 | } 87 | long score = 0; 88 | for (int i = 0; i < hashs.length - 1; i++) 89 | for (int j = i + 1; j < hashs.length; j++) { 90 | long diff = hashs[j] ^ hashs[i]; 91 | int diffBC = Long.bitCount(diff); 92 | if (diffBC < 18) { 93 | long d = 1L << (17 - diffBC); 94 | score += d; 95 | } 96 | } 97 | scoreSum += score; 98 | if (t % 50 == 0) 99 | System.out.println(t + " - Score: " + score); 100 | } 101 | System.out.println("Average score: " + scoreSum / 500); 102 | System.out.printf("Average time %.3f us%n", time / timeCount / 1e3); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/HashSearcherMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import java.util.Comparator; 20 | import java.util.Random; 21 | 22 | import static net.openhft.chronicle.algo.hashing.HashTesterMain.FTSE; 23 | 24 | /** 25 | * Created by peter on 14/09/15. 26 | */ 27 | public class HashSearcherMain { 28 | public static void main(String[] args) { 29 | Random rand = new Random(); 30 | int samples = 1_000_000; 31 | RandomOptimiser optimiser = new RandomOptimiser<>(() -> rand.nextInt() | 1, 32 | Comparator.comparing(Integer::toUnsignedLong), samples); 33 | int mask = (1 << 10) - 1; 34 | optimiser.randomSearch(i -> 35 | HashTesterRunner.performTest(c -> 36 | FTSE.stream().map(s -> 37 | hash(s, i) & mask).forEach(c))); 38 | 39 | System.out.println("hash:" + optimiser); 40 | 41 | optimiser.randomSearch(i -> 42 | HashTesterRunner.performTest(c -> 43 | FTSE.stream().map(s -> 44 | xorShift16(hash(s, i)) & mask).forEach(c))); 45 | 46 | System.out.println("xorShift16(hash):" + optimiser); 47 | 48 | optimiser.randomSearch(i -> 49 | HashTesterRunner.performTest(c -> 50 | FTSE.stream().map(s -> 51 | addShift16(hash(s, i)) & mask).forEach(c))); 52 | 53 | System.out.println("addShift16(hash):" + optimiser); 54 | 55 | optimiser.randomSearch(i -> 56 | HashTesterRunner.performTest(c -> 57 | FTSE.stream().map(s -> 58 | xorShift16n9(hash(s, i)) & mask).forEach(c))); 59 | System.out.println("xorShift16n9(hash): " + optimiser); 60 | } 61 | 62 | public static int hash(String s, int multiplier) { 63 | int h = 0; 64 | for (int i = 0; i < s.length(); i++) { 65 | h = multiplier * h + s.charAt(i); 66 | } 67 | return h; 68 | } 69 | 70 | private static int xorShift16(int hash) { 71 | return hash ^ (hash >> 16); 72 | } 73 | 74 | private static int addShift16(int hash) { 75 | return hash + (hash >> 16); 76 | } 77 | 78 | private static int xorShift16n9(int hash) { 79 | hash ^= (hash >>> 16); 80 | hash ^= (hash >>> 9); 81 | return hash; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/HashTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Created by peter on 14/09/15. 26 | */ 27 | @Retention(RetentionPolicy.RUNTIME) 28 | @Target(ElementType.METHOD) 29 | @interface HashTest { 30 | String value(); // description 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/HashTesterMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import java.net.URI; 20 | import java.nio.file.Files; 21 | import java.nio.file.Paths; 22 | import java.util.Set; 23 | import java.util.function.Consumer; 24 | import java.util.stream.Collectors; 25 | 26 | /** 27 | * Created by peter on 14/09/15. 28 | * For a post of customizing hashing strategies. 29 | */ 30 | public class HashTesterMain { 31 | static final Set FTSE; 32 | 33 | static { 34 | try { 35 | URI uri = HashTesterMain.class.getClassLoader().getResource("ftse350.csv").toURI(); 36 | FTSE = Files.lines(Paths.get(uri)).map(l -> l.split(",", 2)[0]).collect(Collectors.toSet()); 37 | } catch (Exception e) { 38 | throw new AssertionError(e); 39 | } 40 | } 41 | 42 | public static void main(String[] args) { 43 | new HashTesterRunner(HashTesterMain.class).run(); 44 | } 45 | 46 | @HashTest("String.hashCode()") 47 | public static void generateStringHashCode(Consumer hashes) { 48 | FTSE.stream().map(String::hashCode).forEach(hashes); 49 | } 50 | 51 | @HashTest("String.hashCode() mask 9 bits") 52 | public static void generateStringHashCodeAnd511(Consumer hashes) { 53 | FTSE.stream().map(s -> s.hashCode() & ((1 << 9) - 1)).forEach(hashes); 54 | } 55 | 56 | @HashTest("String.hashCode() mask 10 bits") 57 | public static void generateStringHashCodeAnd1023(Consumer hashes) { 58 | FTSE.stream().map(s -> s.hashCode() & ((1 << 10) - 1)).forEach(hashes); 59 | } 60 | 61 | @HashTest("String.hashCode() mask 11 bits") 62 | public static void generateStringHashCodeAnd2047(Consumer hashes) { 63 | FTSE.stream().map(s -> s.hashCode() & ((1 << 11) - 1)).forEach(hashes); 64 | } 65 | 66 | @HashTest("String.hashCode() mask 12 bits") 67 | public static void generateStringHashCodeAnd4095(Consumer hashes) { 68 | FTSE.stream().map(s -> s.hashCode() & ((1 << 12) - 1)).forEach(hashes); 69 | } 70 | 71 | @HashTest("String.hashCode() mask 13 bits") 72 | public static void generateStringHashCodeAnd8191(Consumer hashes) { 73 | FTSE.stream().map(s -> s.hashCode() & ((1 << 13) - 1)).forEach(hashes); 74 | } 75 | 76 | @HashTest("String.hashCode() mask 14 bits") 77 | public static void generateStringHashCodeAnd14(Consumer hashes) { 78 | FTSE.stream().map(s -> s.hashCode() & ((1 << 14) - 1)).forEach(hashes); 79 | } 80 | 81 | @HashTest("String.hashCode() mask 15 bits") 82 | public static void generateStringHashCodeAnd15(Consumer hashes) { 83 | FTSE.stream().map(s -> s.hashCode() & ((1 << 15) - 1)).forEach(hashes); 84 | } 85 | 86 | @HashTest("String.hashCode() mask 16 bits") 87 | public static void generateStringHashCodeAnd16(Consumer hashes) { 88 | FTSE.stream().map(s -> s.hashCode() & ((1 << 16) - 1)).forEach(hashes); 89 | } 90 | 91 | @HashTest("HashMap.hash(String.hashCode()) mask 9") 92 | public static void generateStringHashCodeXorShift(Consumer hashes) { 93 | FTSE.stream().map(s -> hashMap_hash(s.hashCode()) & ((1 << 9) - 1)).forEach(hashes); 94 | } 95 | 96 | @HashTest("HashMap.hash(String.hashCode()) mask 10") 97 | public static void generateStringHashCodeXorShiftAnd1023(Consumer hashes) { 98 | FTSE.stream().map(s -> hashMap_hash(s.hashCode()) & ((1 << 10) - 1)).forEach(hashes); 99 | } 100 | 101 | @HashTest("HashMap.hash(String.hashCode()) mask 11") 102 | public static void generateStringHashCodeXorShiftAnd2047(Consumer hashes) { 103 | FTSE.stream().map(s -> hashMap_hash(s.hashCode()) & ((1 << 11) - 1)).forEach(hashes); 104 | } 105 | 106 | @HashTest("HashMap.hash(String.hashCode()) mask 12") 107 | public static void generateStringHashCodeXorShiftAnd12(Consumer hashes) { 108 | FTSE.stream().map(s -> hashMap_hash(s.hashCode()) & ((1 << 12) - 1)).forEach(hashes); 109 | } 110 | 111 | @HashTest("HashMap.hash(String.hashCode()) mask 13") 112 | public static void generateStringHashCodeXorShiftAnd13(Consumer hashes) { 113 | FTSE.stream().map(s -> hashMap_hash(s.hashCode()) & ((1 << 13) - 1)).forEach(hashes); 114 | } 115 | 116 | @HashTest("HashMap.hash(String.hashCode()) mask 14") 117 | public static void generateStringHashCodeXorShiftAnd14(Consumer hashes) { 118 | FTSE.stream().map(s -> hashMap_hash(s.hashCode()) & ((1 << 14) - 1)).forEach(hashes); 119 | } 120 | 121 | @HashTest("HashMap.hash(String.hashCode()) mask 15") 122 | public static void generateStringHashCodeXorShiftAnd15(Consumer hashes) { 123 | FTSE.stream().map(s -> hashMap_hash(s.hashCode()) & ((1 << 15) - 1)).forEach(hashes); 124 | } 125 | 126 | @HashTest("HashMap.hash(String.hashCode()) mask 16") 127 | public static void generateStringHashCodeXorShiftAnd16(Consumer hashes) { 128 | FTSE.stream().map(s -> hashMap_hash(s.hashCode()) & ((1 << 16) - 1)).forEach(hashes); 129 | } 130 | 131 | @HashTest("hashCode(String, 1) & 511") 132 | public static void generateMultiplierHash1And511(Consumer hashes) { 133 | FTSE.stream().map(s -> hash(s, 1) & 511).forEach(hashes); 134 | } 135 | 136 | @HashTest("hashCode(String, 2) & 511") 137 | public static void generateMultiplierHash2And511(Consumer hashes) { 138 | FTSE.stream().map(s -> hash(s, 2) & 511).forEach(hashes); 139 | } 140 | 141 | @HashTest("hashCode(String, 3) & 511") 142 | public static void generateMultiplierHash3And511(Consumer hashes) { 143 | FTSE.stream().map(s -> hash(s, 3) & 511).forEach(hashes); 144 | } 145 | 146 | @HashTest("hashCode(String, 4) & 511") 147 | public static void generateMultiplierHash4And511(Consumer hashes) { 148 | FTSE.stream().map(s -> hash(s, 4) & 511).forEach(hashes); 149 | } 150 | 151 | @HashTest("hashCode(String, 5) & 511") 152 | public static void generateMultiplierHash5And511(Consumer hashes) { 153 | FTSE.stream().map(s -> hash(s, 5) & 511).forEach(hashes); 154 | } 155 | 156 | @HashTest("hashCode(String, 6) & 511") 157 | public static void generateMultiplierHash6And511(Consumer hashes) { 158 | FTSE.stream().map(s -> hash(s, 6) & 511).forEach(hashes); 159 | } 160 | 161 | @HashTest("hashCode(String, 7) & 511") 162 | public static void generateMultiplierHash7And511(Consumer hashes) { 163 | FTSE.stream().map(s -> hash(s, 7) & 511).forEach(hashes); 164 | } 165 | 166 | @HashTest("hashCode(String, 8) & 511") 167 | public static void generateMultiplierHash8And511(Consumer hashes) { 168 | FTSE.stream().map(s -> hash(s, 8) & 511).forEach(hashes); 169 | } 170 | 171 | @HashTest("hashCode(String, 9) & 511") 172 | public static void generateMultiplierHash9And511(Consumer hashes) { 173 | FTSE.stream().map(s -> hash(s, 9) & 511).forEach(hashes); 174 | } 175 | 176 | @HashTest("hashCode(String, 10) & 511") 177 | public static void generateMultiplierHash10And511(Consumer hashes) { 178 | FTSE.stream().map(s -> hash(s, 10) & 511).forEach(hashes); 179 | } 180 | 181 | @HashTest("hashCode(String, 11) & 511") 182 | public static void generateMultiplierHash11And511(Consumer hashes) { 183 | FTSE.stream().map(s -> hash(s, 11) & 511).forEach(hashes); 184 | } 185 | 186 | public static int hashMap_hash(Object key) { 187 | int h; 188 | return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 189 | } 190 | 191 | public static int hash(String s, int multiplier) { 192 | int h = 0; 193 | for (int i = 0; i < s.length(); i++) 194 | h = multiplier * h + s.charAt(i); 195 | return h; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/HashTesterRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import java.lang.reflect.Method; 20 | import java.util.Collections; 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | import java.util.concurrent.atomic.AtomicLong; 26 | import java.util.function.Consumer; 27 | 28 | /** 29 | * Created by peter on 14/09/15. 30 | * For a post of customizing hashing strategies. 31 | */ 32 | public class HashTesterRunner implements Runnable { 33 | private final Class testerMainClass; 34 | 35 | public HashTesterRunner(Class testerMainClass) { 36 | this.testerMainClass = testerMainClass; 37 | } 38 | 39 | private static void performTest(HashTest hashTest, Method method) { 40 | AtomicLong counter = new AtomicLong(); 41 | Set set = new HashSet<>(); 42 | Consumer consumer = o -> { 43 | set.add(o); 44 | counter.incrementAndGet(); 45 | }; 46 | try { 47 | method.invoke(null, consumer); 48 | 49 | System.out.println(hashTest.value() + ": { hashes: " + counter + ", collisions: " + (counter.get() - set.size()) + " }"); 50 | } catch (Exception e) { 51 | System.err.println(hashTest.value() + ": Failed"); 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | public static int performTest(Consumer> consumer2) { 57 | AtomicInteger counter = new AtomicInteger(); 58 | Set set = Collections.newSetFromMap(new ConcurrentHashMap<>()); 59 | Consumer consumer = o -> { 60 | set.add(o); 61 | counter.incrementAndGet(); 62 | }; 63 | 64 | consumer2.accept(consumer); 65 | return (counter.get() - set.size()); 66 | } 67 | 68 | @Override 69 | public void run() { 70 | for (Method method : testerMainClass.getDeclaredMethods()) { 71 | HashTest hashTest = method.getAnnotation(HashTest.class); 72 | if (hashTest != null) 73 | performTest(hashTest, method); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/LongHashFunctionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.nio.ByteOrder; 21 | 22 | import static java.nio.ByteOrder.*; 23 | import static net.openhft.chronicle.algo.bytes.Accessor.checkedCharSequenceAccess; 24 | import static net.openhft.chronicle.core.UnsafeMemory.MEMORY; 25 | import static org.junit.Assert.assertEquals; 26 | import static org.junit.Assert.assertNotEquals; 27 | 28 | public class LongHashFunctionTest { 29 | 30 | private static ByteOrder nonNativeOrder() { 31 | return nativeOrder() == LITTLE_ENDIAN ? BIG_ENDIAN : LITTLE_ENDIAN; 32 | } 33 | 34 | public static void test(LongHashFunction f, byte[] data, long eh) { 35 | int len = data.length; 36 | testVoid(f, eh, len); 37 | testBoolean(f, len); 38 | ByteBuffer bb = ByteBuffer.wrap(data).order(nativeOrder()); 39 | testPrimitives(f, eh, len, bb); 40 | testArrays(f, data, eh, len, bb); 41 | testByteBuffers(f, eh, len, bb); 42 | testCharSequences(f, eh, len, bb); 43 | testMemory(f, eh, len, bb); 44 | } 45 | 46 | private static void testVoid(LongHashFunction f, long eh, int len) { 47 | if (len == 0) 48 | assertEquals("void", eh, f.hashVoid()); 49 | } 50 | 51 | public static void testBoolean(LongHashFunction f, int len) { 52 | if (len != 1) 53 | return; 54 | for (boolean b : new boolean[]{true, false}) { 55 | boolean[] a = {b}; 56 | long single = f.hashBoolean(b); 57 | assertEquals(single, f.hashBooleans(a)); 58 | } 59 | } 60 | 61 | private static void testPrimitives(LongHashFunction f, long eh, int len, ByteBuffer bb) { 62 | if (len == 1) 63 | assertEquals("byte hash", eh, f.hashByte(bb.get(0))); 64 | 65 | if (len == 2) { 66 | assertEquals("short hash", eh, f.hashShort(bb.getShort(0))); 67 | assertEquals("char hash", eh, f.hashChar(bb.getChar(0))); 68 | } 69 | if (len == 4) 70 | assertEquals("int hash", eh, f.hashInt(bb.getInt(0))); 71 | 72 | if (len == 8) 73 | assertEquals("long hash", eh, f.hashLong(bb.getLong(0))); 74 | } 75 | 76 | private static void testArrays(LongHashFunction f, byte[] data, long eh, int len, 77 | ByteBuffer bb) { 78 | assertEquals("byte array", eh, f.hashBytes(data)); 79 | 80 | byte[] data2 = new byte[len + 2]; 81 | System.arraycopy(data, 0, data2, 1, len); 82 | assertEquals("byte array off len", eh, f.hashBytes(data2, 1, len)); 83 | 84 | if ((len & 1) == 0) { 85 | int shortLen = len / 2; 86 | 87 | short[] shorts = new short[shortLen]; 88 | bb.asShortBuffer().get(shorts); 89 | assertEquals("short array", eh, f.hashShorts(shorts)); 90 | 91 | short[] shorts2 = new short[shortLen + 2]; 92 | System.arraycopy(shorts, 0, shorts2, 1, shortLen); 93 | assertEquals("short array off len", eh, f.hashShorts(shorts2, 1, shortLen)); 94 | 95 | char[] chars = new char[shortLen]; 96 | bb.asCharBuffer().get(chars); 97 | assertEquals("char array", eh, f.hashChars(chars)); 98 | 99 | char[] chars2 = new char[shortLen + 2]; 100 | System.arraycopy(chars, 0, chars2, 1, shortLen); 101 | assertEquals("char array off len", eh, f.hashChars(chars2, 1, shortLen)); 102 | } 103 | if ((len & 3) == 0) { 104 | int intLen = len / 4; 105 | int[] ints = new int[intLen]; 106 | bb.asIntBuffer().get(ints); 107 | assertEquals("int array", eh, f.hashInts(ints)); 108 | 109 | int[] ints2 = new int[intLen + 2]; 110 | System.arraycopy(ints, 0, ints2, 1, intLen); 111 | assertEquals("int array off len", eh, f.hashInts(ints2, 1, intLen)); 112 | } 113 | if ((len & 7) == 0) { 114 | int longLen = len / 8; 115 | long[] longs = new long[longLen]; 116 | bb.asLongBuffer().get(longs); 117 | assertEquals("long array", eh, f.hashLongs(longs)); 118 | 119 | long[] longs2 = new long[longLen + 2]; 120 | System.arraycopy(longs, 0, longs2, 1, longLen); 121 | assertEquals("long array off len", eh, f.hashLongs(longs2, 1, longLen)); 122 | } 123 | } 124 | 125 | private static void testByteBuffers(LongHashFunction f, long eh, int len, ByteBuffer bb) { 126 | bb.order(LITTLE_ENDIAN); 127 | assertEquals("byte buffer little endian", eh, f.hashBytes(bb)); 128 | ByteBuffer bb2 = ByteBuffer.allocate(len + 2).order(LITTLE_ENDIAN); 129 | bb2.position(1); 130 | bb2.put(bb); 131 | assertEquals("byte buffer little endian off len", eh, f.hashBytes(bb2, 1, len)); 132 | 133 | bb.order(BIG_ENDIAN).clear(); 134 | 135 | assertEquals("byte buffer big endian", eh, f.hashBytes(bb)); 136 | bb2.order(BIG_ENDIAN); 137 | assertEquals("byte buffer big endian off len", eh, f.hashBytes(bb2, 1, len)); 138 | 139 | bb.order(nativeOrder()).clear(); 140 | } 141 | 142 | private static void testCharSequences(LongHashFunction f, long eh, int len, ByteBuffer bb) { 143 | if ((len & 1) == 0) { 144 | String s = bb.asCharBuffer().toString(); 145 | for (int i = 0; i < s.length(); i++) 146 | if (!Character.isValidCodePoint(s.charAt(i))) 147 | return; 148 | assertEquals("string", eh, f.hashChars(s)); 149 | 150 | StringBuilder sb = new StringBuilder(); 151 | sb.append(s); 152 | assertEquals("string builder", eh, f.hashChars(sb)); 153 | 154 | sb.insert(0, 'a'); 155 | sb.append('b'); 156 | assertEquals("string builder off len", eh, f.hashChars(sb, 1, len / 2)); 157 | 158 | // Test for OpenJDK < 7u6, where substring wasn't copied char[] array 159 | assertEquals("substring", eh, f.hashChars(sb.substring(1, len / 2 + 1))); 160 | 161 | if (len >= 6) { 162 | bb.order(nonNativeOrder()); 163 | String s2 = bb.asCharBuffer().toString(); 164 | assertEquals(s.charAt(0), Character.reverseBytes(bb.getChar(0))); 165 | assertNotEquals("string wrong order", eh, f.hashChars(s2)); 166 | 167 | assertEquals("string wrong order fixed", eh, 168 | f.hash(checkedCharSequenceAccess(nonNativeOrder()), s2, 0, len / 2)); 169 | 170 | bb.order(nativeOrder()).clear(); 171 | } 172 | } 173 | } 174 | 175 | private static void testMemory(LongHashFunction f, long eh, int len, ByteBuffer bb) { 176 | ByteBuffer directBB = ByteBuffer.allocateDirect(len); 177 | directBB.put(bb); 178 | assertEquals("memory", eh, f.hashMemory(MEMORY.address(directBB), len)); 179 | bb.clear(); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/MurmurHash3MoreTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import net.openhft.chronicle.algo.bytes.NativeAccess; 20 | import net.openhft.chronicle.bytes.NativeBytes; 21 | import org.junit.Ignore; 22 | import org.junit.Test; 23 | 24 | import java.security.SecureRandom; 25 | 26 | /** 27 | * Created by peter on 28/06/15. 28 | */ 29 | public class MurmurHash3MoreTest { 30 | @Test 31 | @Ignore("Long running, avg score = 6994") 32 | public void testSmallRandomness() { 33 | long time = 0, timeCount = 0; 34 | long scoreSum = 0; 35 | for (int t = 1; t < 500; t++) { 36 | long[] hashs = new long[8192]; 37 | NativeBytes b = NativeBytes.nativeBytes(8); 38 | for (int i = 0; i < hashs.length; i++) { 39 | b.clear(); 40 | b.append(t); 41 | b.appendUtf8('-'); 42 | b.append(i); 43 | long start = System.nanoTime(); 44 | hashs[i] = LongHashFunction.murmur_3().hash((Object) null, NativeAccess.instance(), b.addressForRead(b.readPosition()), b.readRemaining()); 45 | time += System.nanoTime() - start; 46 | timeCount++; 47 | } 48 | long score = 0; 49 | for (int i = 0; i < hashs.length - 1; i++) 50 | for (int j = i + 1; j < hashs.length; j++) { 51 | long diff = hashs[j] ^ hashs[i]; 52 | int diffBC = Long.bitCount(diff); 53 | if (diffBC < 18) { 54 | long d = 1L << (17 - diffBC); 55 | score += d; 56 | } 57 | } 58 | scoreSum += score; 59 | if (t % 50 == 0) 60 | System.out.println(t + " - Score: " + score); 61 | } 62 | System.out.println("Average score: " + scoreSum / 500); 63 | System.out.printf("Average time %.3f us%n", time / timeCount / 1e3); 64 | } 65 | 66 | @Ignore("Long running, avg score = 6836") 67 | @Test 68 | public void testRandomness() { 69 | long time = 0, timeCount = 0; 70 | long scoreSum = 0; 71 | for (int t = 0; t < 500; t++) { 72 | long[] hashs = new long[8192]; 73 | NativeBytes b = NativeBytes.nativeBytes(hashs.length / 64); 74 | byte[] init = new byte[hashs.length / 64]; 75 | new SecureRandom().nextBytes(init); 76 | for (int i = 0; i < hashs.length; i++) { 77 | b.clear(); 78 | b.write(init); 79 | 80 | b.writeLong(i >> 6 << 3, 1L << i); 81 | b.readLimit(hashs.length / 8); 82 | long start = System.nanoTime(); 83 | hashs[i] = LongHashFunction.murmur_3().hash((Object) null, NativeAccess.instance(), b.addressForRead(b.readPosition()), b.readRemaining()); 84 | time += System.nanoTime() - start; 85 | timeCount++; 86 | } 87 | long score = 0; 88 | for (int i = 0; i < hashs.length - 1; i++) 89 | for (int j = i + 1; j < hashs.length; j++) { 90 | long diff = hashs[j] ^ hashs[i]; 91 | int diffBC = Long.bitCount(diff); 92 | if (diffBC < 18) { 93 | long d = 1L << (17 - diffBC); 94 | score += d; 95 | } 96 | } 97 | scoreSum += score; 98 | if (t % 50 == 0) 99 | System.out.println(t + " - Score: " + score); 100 | } 101 | System.out.println("Average score: " + scoreSum / 500); 102 | System.out.printf("Average time %.3f us%n", time / timeCount / 1e3); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/MurmurHash3Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2020 chronicle.software 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import com.google.common.hash.HashFunction; 20 | import com.google.common.hash.Hashing; 21 | import org.junit.Test; 22 | 23 | import java.util.Arrays; 24 | import java.util.Random; 25 | 26 | public class MurmurHash3Test { 27 | 28 | @Test 29 | public void testMurmurWithoutSeed() { 30 | testMurmur(LongHashFunction.murmur_3(), Hashing.murmur3_128()); 31 | } 32 | 33 | @Test 34 | public void testMurmurWithSeed() { 35 | testMurmur(LongHashFunction.murmur_3(42L), Hashing.murmur3_128(42)); 36 | } 37 | 38 | private void testMurmur(LongHashFunction tested, HashFunction referenceFromGuava) { 39 | byte[] testData = new byte[1024]; 40 | new Random().nextBytes(testData); 41 | for (int i = 0; i < testData.length; i++) { 42 | byte[] data = Arrays.copyOf(testData, i); 43 | LongHashFunctionTest.test(tested, data, referenceFromGuava.hashBytes(data).asLong()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/algo/hashing/RandomOptimiser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2020 chronicle.software 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | package net.openhft.chronicle.algo.hashing; 18 | 19 | import java.util.Comparator; 20 | import java.util.function.Function; 21 | import java.util.function.Supplier; 22 | import java.util.stream.IntStream; 23 | 24 | /** 25 | * Created by peter on 14/09/15. 26 | */ 27 | public class RandomOptimiser { 28 | final Supplier inputSupplier; 29 | private final Comparator tieBreaker; 30 | private final int samples; 31 | T lowest; 32 | int lowestScore = Integer.MAX_VALUE; 33 | T highest; 34 | int highestScore = Integer.MIN_VALUE; 35 | 36 | public RandomOptimiser(Supplier inputSupplier, Comparator tieBreaker, int samples) { 37 | this.inputSupplier = inputSupplier; 38 | this.tieBreaker = tieBreaker; 39 | this.samples = samples; 40 | } 41 | 42 | public void randomSearch(Function test) { 43 | lowest = highest = null; 44 | lowestScore = Integer.MAX_VALUE; 45 | highestScore = Integer.MIN_VALUE; 46 | IntStream.range(0, samples).parallel().forEach(i -> { 47 | T num = inputSupplier.get(); 48 | int score = test.apply(num); 49 | synchronized (this) { 50 | if (lowestScore > score || (lowestScore == score && tieBreaker.compare(lowest, num) > 0)) { 51 | lowestScore = score; 52 | lowest = num; 53 | } 54 | if (highestScore < score || (highestScore == score && tieBreaker.compare(highest, num) > 0)) { 55 | highestScore = score; 56 | highest = num; 57 | } 58 | } 59 | }); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "RandomOptimiser{" + 65 | "samples=" + samples + 66 | ", lowest=" + lowest + 67 | ", lowestScore=" + lowestScore + 68 | ", highest=" + highest + 69 | ", highestScore=" + highestScore + 70 | '}'; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/net/openhft/chronicle/map/locks/ChronicleStampedLockTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2022 chronicle.software 3 | * 4 | * https://chronicle.software 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package net.openhft.chronicle.map.locks; 20 | 21 | import org.junit.Assert; 22 | import org.junit.Test; 23 | 24 | //import static org.junit.jupiter.api.Assertions.*; 25 | 26 | public class ChronicleStampedLockTest { 27 | 28 | @Test 29 | public void tryOptimisticRead() { 30 | System.out.println("A test of ChronicleStampedLock::tryOptimisticRead()"); 31 | Assert.assertEquals(Boolean.TRUE, true); 32 | } 33 | 34 | @Test 35 | public void validate() { 36 | System.out.println("A test of ChronicleStampedLock::validate()"); 37 | Assert.assertEquals(Boolean.TRUE, true); 38 | } 39 | 40 | @Test 41 | public void tryWriteLock() { 42 | System.out.println("A test of ChronicleStampedLock::tryWriteLock()"); 43 | Assert.assertEquals(Boolean.TRUE, true); 44 | } 45 | 46 | @Test 47 | public void tryReadLock() { 48 | System.out.println("A test of ChronicleStampedLock::tryReadLock()"); 49 | Assert.assertEquals(Boolean.TRUE, true); 50 | } 51 | 52 | @Test 53 | public void writeLock() { 54 | System.out.println("A test of ChronicleStampedLock::writeLock()"); 55 | Assert.assertEquals(Boolean.TRUE, true); 56 | } 57 | 58 | @Test 59 | public void readLock() { 60 | System.out.println("A test of ChronicleStampedLock::readLock()"); 61 | Assert.assertEquals(Boolean.TRUE, true); 62 | } 63 | 64 | @Test 65 | public void unlock() { 66 | System.out.println("A test of ChronicleStampedLock::unlock()"); 67 | Assert.assertEquals(Boolean.TRUE, true); 68 | } 69 | 70 | @Test 71 | public void unlockRead() { 72 | System.out.println("A test of ChronicleStampedLock::unlockRead()"); 73 | Assert.assertEquals(Boolean.TRUE, true); 74 | } 75 | 76 | @Test 77 | public void unlockWrite() { 78 | System.out.println("A test of ChronicleStampedLock::unlockWrite()"); 79 | Assert.assertEquals(Boolean.TRUE, true); 80 | } 81 | 82 | @Test 83 | public void getReadLockCount() { 84 | System.out.println("A test of ChronicleStampedLock::getReadLockCount()"); 85 | Assert.assertEquals(Boolean.TRUE, true); 86 | } 87 | 88 | @Test 89 | public void isReadLocked() { 90 | System.out.println("A test of ChronicleStampedLock::isReadLocked()"); 91 | Assert.assertEquals(Boolean.TRUE, true); 92 | } 93 | 94 | @Test 95 | public void offHeapLock() { 96 | System.out.println("A test of ChronicleStampedLock::offHeapLock()"); 97 | Assert.assertEquals(Boolean.TRUE, true); 98 | } 99 | 100 | @Test 101 | public void offHeapLockReaderCount() { 102 | System.out.println("A test of ChronicleStampedLock::offHeapLockReaderCount()"); 103 | Assert.assertEquals(Boolean.TRUE, true); 104 | } 105 | } 106 | --------------------------------------------------------------------------------