diff --git a/build.gradle b/build.gradle index 29bc657..244ff3f 100644 --- a/build.gradle +++ b/build.gradle @@ -347,6 +347,64 @@ project(':mmap-queue') { } } +project(':mmap-dictionary') { + dependencies { + api "org.agrona:agrona:${agronaVersion}" + api "org.slf4j:slf4j-api:${slf4jVersion}" + api project(':mmap-region') + + testImplementation "org.hdrhistogram:HdrHistogram:${hdrHistogramVersion}" + testRuntimeOnly "org.slf4j:slf4j-simple:${slf4jVersion}" + } + + jar { + bundle { + // workaround for https://github.com/bndtools/bnd/issues/6346 + properties.put("project.group", provider({project.group})) + bnd """ + Automatic-Module-Name: org.tools4j.mmap-dictionary + Bundle-Name: org.tools4j.mmap-dictionary + Bundle-SymbolicName: org.tools4j.mmap-dictionary + Implementation-Title: mmap + Implementation-Vendor: tools4j.org + Implementation-Version: ${projVersion} + -exportcontents: org.tools4j.mmap, org.tools4j.mmap.* + # Suppress headers that reduce reproducibility. + -reproducible: true + -noextraheaders: true + """ + } + } + + java { + withSourcesJar() + withJavadocJar() + } + + publishing { + publications { + mmapQueue(MavenPublication) { + from components.java + pom(projectPom) + } + } + + repositories { + maven { + url = !isReleaseVersion ? snapshotsRepoUrl : releasesRepoUrl + credentials { + username = ossrhUsername + password = ossrhPassword + } + } + } + } + + signing { + sign publishing.publications.mmapQueue + } +} + tasks.register('testReport', TestReport) { destinationDirectory = file("${rootDir}/reports/allTests") diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DeletePredicate.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DeletePredicate.java new file mode 100644 index 0000000..2faaff2 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DeletePredicate.java @@ -0,0 +1,29 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +@FunctionalInterface +public interface DeletePredicate { + boolean test(KeyValuePair keyValue); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DeletingContext.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DeletingContext.java new file mode 100644 index 0000000..ea1c057 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DeletingContext.java @@ -0,0 +1,65 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.agrona.MutableDirectBuffer; + +public interface DeletingContext extends AutoCloseable { + /** + * Aborts deleting the key/value pair + */ + void abort(); + + /** + * @return true if the context is closed. + */ + boolean isClosed(); + + /** + * Aborts deleting if not closed or committed yet. + */ + @Override + default void close() { + if (!isClosed()) { + abort(); + } + } + + interface Key extends DeletingContext { + MutableDirectBuffer keyBuffer(); + + Result delete(int keyLength); + Result deleteIfMatching(int keyLength, DeletePredicate condition); + } + + interface Result extends KeyValueResult { + /** + * @return true if the key/value pair was present in the dictionary when performing or attempting + * the delete operation + */ + @Override + boolean isPresent(); + boolean isDeleted(); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Dictionary.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Dictionary.java new file mode 100644 index 0000000..77389d1 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Dictionary.java @@ -0,0 +1,58 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.tools4j.mmap.dictionary.config.DictionaryConfig; +import org.tools4j.mmap.dictionary.config.ReaderConfig; +import org.tools4j.mmap.dictionary.config.UpdaterConfig; +import org.tools4j.mmap.dictionary.impl.DictionaryImpl; + +import java.io.File; + +public interface Dictionary extends AutoCloseable { + Updater createUpdater(); + Updater createUpdater(UpdaterConfig config); + + Lookup createLookup(); + Lookup createLookup(ReaderConfig config); + + DictionaryIterator createIterator(); + DictionaryIterator createIterator(ReaderConfig config); + + boolean isClosed(); + + /** + * Closes the dictionary and all updaters, lookups and iterators created via this dictionary. + */ + @Override + void close(); + + static Dictionary create(final File file) { + return new DictionaryImpl(file); + } + + static Dictionary create(final File file, final DictionaryConfig config) { + return new DictionaryImpl(file, config); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DictionaryIterator.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DictionaryIterator.java new file mode 100644 index 0000000..fd8aa1a --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/DictionaryIterator.java @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +public interface DictionaryIterator extends AutoCloseable { + IteratingContext.KeyValuePairs iteratingKeyValuePairs(); + IteratingContext.Keys iteratingKeys(); + IteratingContext.Values iteratingValues(); + + long size(); + boolean isEmpty(); + + boolean isClosed(); + + /** + * Closes this reader. + */ + @Override + void close(); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/IteratingContext.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/IteratingContext.java new file mode 100644 index 0000000..3dedf8c --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/IteratingContext.java @@ -0,0 +1,39 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.agrona.DirectBuffer; + +public interface IteratingContext extends Iterable { + boolean isClosed(); + + /** + * Closes this context. + */ + void close(); + + interface KeyValuePairs extends IteratingContext, AutoCloseable {} + interface Keys extends IteratingContext, AutoCloseable {} + interface Values extends IteratingContext, AutoCloseable {} +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValueIterable.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValueIterable.java new file mode 100644 index 0000000..ff3da3f --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValueIterable.java @@ -0,0 +1,47 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.agrona.DirectBuffer; + +import java.util.Iterator; + +public interface KeyValueIterable extends Iterable, AutoCloseable { + @Override + Iterator iterator(); + + Iterable keys(); + Iterable values(); + + long size(); + boolean isEmpty(); + + boolean isClosed(); + + /** + * Closes this reader. + */ + @Override + void close(); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValuePair.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValuePair.java new file mode 100644 index 0000000..25f4a2c --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValuePair.java @@ -0,0 +1,34 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.agrona.DirectBuffer; + +/** + * Representation of a key/value pair, with key and value data accessible through {@link #key()} and {@link #value()}. + */ +public interface KeyValuePair { + DirectBuffer key(); + DirectBuffer value(); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValueResult.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValueResult.java new file mode 100644 index 0000000..046dac7 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/KeyValueResult.java @@ -0,0 +1,42 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +public interface KeyValueResult extends KeyValuePair, AutoCloseable { + /** + * @return true if the key/value pair is present in the dictionary + */ + boolean isPresent(); + + /** + * @return true if the result is closed. + */ + boolean isClosed(); + + /** + * Closes the result and unwraps the buffers + */ + @Override + void close(); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Lookup.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Lookup.java new file mode 100644 index 0000000..975a49b --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Lookup.java @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.agrona.DirectBuffer; + +import java.nio.ByteBuffer; + +public interface Lookup extends AutoCloseable { + boolean containsKey(byte[] key); + boolean containsKey(byte[] key, int keyOffset, int keyLength); + boolean containsKey(ByteBuffer key); + boolean containsKey(ByteBuffer key, int keyLength); + boolean containsKey(ByteBuffer key, int keyOffset, int keyLength); + boolean containsKey(DirectBuffer key); + boolean containsKey(DirectBuffer key, int keyOffset, int keyLength); + + LookupContext.Result get(byte[] key); + LookupContext.Result get(byte[] key, int keyOffset, int keyLength); + LookupContext.Result get(ByteBuffer key); + LookupContext.Result get(ByteBuffer key, int keyLength); + LookupContext.Result get(ByteBuffer key, int keyOffset, int keyLength); + LookupContext.Result get(DirectBuffer key); + LookupContext.Result get(DirectBuffer key, int keyOffset, int keyLength); + + LookupContext.Key getting(); + + /** + * @return true if this lookup is closed + */ + boolean isClosed(); + + /** + * Closes the lookup. + */ + @Override + void close(); + +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/LookupContext.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/LookupContext.java new file mode 100644 index 0000000..2a464b9 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/LookupContext.java @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.agrona.MutableDirectBuffer; + +public interface LookupContext extends AutoCloseable { + /** + * @return true if the context is closed. + */ + boolean isClosed(); + + /** + * Closes the lookup context and unwraps the buffer + */ + @Override + void close(); + + interface Key extends LookupContext { + void ensureCapacity(int capacity); + MutableDirectBuffer keyBuffer(); + boolean containsKey(int keyLength); + Result lookup(int keyLength); + } + + interface Result extends KeyValueResult { + boolean isPresent(); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/UpdatePredicate.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/UpdatePredicate.java new file mode 100644 index 0000000..0b02ab9 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/UpdatePredicate.java @@ -0,0 +1,29 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +@FunctionalInterface +public interface UpdatePredicate { + boolean test(KeyValuePair oldPair, KeyValuePair newPair); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Updater.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Updater.java new file mode 100644 index 0000000..b7ef735 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/Updater.java @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.agrona.DirectBuffer; + +public interface Updater extends AutoCloseable { + void put(DirectBuffer key, DirectBuffer value); + UpdatingContext.Result putIfAbsent(DirectBuffer key, DirectBuffer value); + UpdatingContext.Result putIfMatching(DirectBuffer key, DirectBuffer value, UpdatePredicate condition); + + DeletingContext.Result delete(DirectBuffer key); + DeletingContext.Result deleteIfMatching(DirectBuffer key, DeletePredicate condition); + + UpdatingContext.Key updating(int keyCapacity, int valueCapacity); + UpdatingContext.Value updating(DirectBuffer key, int valueCapacity); + DeletingContext.Key deleting(); + + /** + * @return true if this updater is closed + */ + boolean isClosed(); + + /** + * Closes the updater. + */ + @Override + void close(); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/UpdatingContext.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/UpdatingContext.java new file mode 100644 index 0000000..1f9fbab --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/api/UpdatingContext.java @@ -0,0 +1,77 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.api; + +import org.agrona.DirectBuffer; +import org.agrona.MutableDirectBuffer; + +public interface UpdatingContext extends AutoCloseable { + /** + * Aborts updating the key/value pair + */ + void abort(); + + /** + * @return true if the context is closed. + */ + boolean isClosed(); + + /** + * Aborts updating if not closed or committed yet. + */ + @Override + default void close() { + if (!isClosed()) { + abort(); + } + } + + interface Key extends UpdatingContext { + void ensureCapacity(int capacity); + MutableDirectBuffer keyBuffer(); + Value commitKey(int keyLength); + } + + interface Value extends UpdatingContext { + void ensureCapacity(int capacity); + DirectBuffer keyBuffer(); + MutableDirectBuffer valueBuffer(); + + Result put(int valueLength); + Result putIfAbsent(int valueLength); + Result putIfMatching(int valueLength, UpdatePredicate condition); + } + + interface Result extends KeyValueResult { + @Override + boolean isPresent(); + boolean isUpdated(); + boolean hasOldValue(); + + /** + * @return the buffer with the old value before updating, never null + */ + DirectBuffer oldValue(); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfig.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfig.java new file mode 100644 index 0000000..64bc310 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfig.java @@ -0,0 +1,57 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.config; + +import org.tools4j.mmap.region.api.AccessMode; + +public interface DictionaryConfig { + AccessMode accessMode(); + int maxUpdaters(); + long maxHeaderFileSize(); + long maxPayloadFileSize(); + boolean expandHeaderFile(); + boolean expandPayloadFiles(); + boolean rollHeaderFile(); + boolean rollPayloadFiles(); + int headerFilesToCreateAhead(); + int payloadFilesToCreateAhead(); + + UpdaterConfig updaterConfig(); + ReaderConfig lookupConfig(); + ReaderConfig iteratorConfig(); + + DictionaryConfig toImmutableDictionaryConfig(); + +// static DictionaryConfigurator configure() { +// return DictionaryConfigurator.configure(); +// } +// +// static DictionaryConfigurator configure(final DictionaryConfig defaults) { +// return DictionaryConfigurator.configure(defaults); +// } +// +// static DictionaryConfig getDefault() { +// return DictionaryConfigurations.defaultDictionaryConfig(); +// } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfigurations.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfigurations.java new file mode 100644 index 0000000..4f41177 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfigurations.java @@ -0,0 +1,28 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.config; + +public enum DictionaryConfigurations { + ; +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfigurator.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfigurator.java new file mode 100644 index 0000000..91f5bcf --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/DictionaryConfigurator.java @@ -0,0 +1,68 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.config; + +import org.tools4j.mmap.region.api.AccessMode; +import org.tools4j.mmap.region.config.MappingStrategy; +import org.tools4j.mmap.region.config.MappingStrategyConfig; +import org.tools4j.mmap.region.config.MappingStrategyConfigurator; + +import java.util.function.Consumer; + +public interface DictionaryConfigurator extends DictionaryConfig { + DictionaryConfigurator accessMode(AccessMode accessMode); + DictionaryConfigurator maxUpdaters(int maxUpdaters); + DictionaryConfigurator maxHeaderFileSize(long maxHeaderFileSize); + DictionaryConfigurator maxPayloadFileSize(long maxPayloadFileSize); + DictionaryConfigurator expandHeaderFile(boolean expandHeaderFile); + DictionaryConfigurator expandPayloadFiles(boolean expandPayloadFiles); + DictionaryConfigurator rollHeaderFile(boolean rollHeaderFile); + DictionaryConfigurator rollPayloadFiles(boolean rollPayloadFiles); + DictionaryConfigurator headerFilesToCreateAhead(int headerFilesToCreateAhead); + DictionaryConfigurator payloadFilesToCreateAhead(int payloadFilesToCreateAhead); + DictionaryConfigurator mappingStrategy(MappingStrategy mappingStrategy); + DictionaryConfigurator mappingStrategy(MappingStrategyConfig mappingStrategyConfig); + DictionaryConfigurator mappingStrategy(Consumer configurator); + DictionaryConfigurator headerMappingStrategy(MappingStrategy mappingStrategy); + DictionaryConfigurator headerMappingStrategy(MappingStrategyConfig mappingStrategyConfig); + DictionaryConfigurator headerMappingStrategy(Consumer configurator); + DictionaryConfigurator payloadMappingStrategy(MappingStrategy mappingStrategy); + DictionaryConfigurator payloadMappingStrategy(MappingStrategyConfig mappingStrategyConfig); + DictionaryConfigurator payloadMappingStrategy(Consumer configurator); + DictionaryConfigurator updaterConfig(UpdaterConfig config); + DictionaryConfigurator updaterConfig(Consumer configurator); + DictionaryConfigurator lookupConfig(ReaderConfig config); + DictionaryConfigurator lookupConfig(Consumer configurator); + DictionaryConfigurator iteratorConfig(ReaderConfig config); + DictionaryConfigurator iteratorConfig(Consumer configurator); + DictionaryConfigurator reset(); + +// static DictionaryConfigurator configure() { +// return new DictionaryConfiguratorImpl(); +// } +// +// static DictionaryConfigurator configure(final DictionaryConfig defaults) { +// return new DictionaryConfiguratorImpl(defaults); +// } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/ReaderConfig.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/ReaderConfig.java new file mode 100644 index 0000000..ccf85c9 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/ReaderConfig.java @@ -0,0 +1,48 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.config; + +import org.tools4j.mmap.region.config.MappingStrategy; + +public interface ReaderConfig { + MappingStrategy sectorMappingStrategy(); + MappingStrategy payloadMappingStrategy(); + ReaderConfig toImmutableReaderConfig(); + +// static ReaderConfigurator configureLookup() { +// return ReaderConfigurator.configureLookup(); +// } +// +// static ReaderConfigurator configureIterator() { +// return ReaderConfigurator.configureIterator(); +// } +// +// static ReaderConfig getDefaultLookupConfig() { +// return DictionaryConfigurations.defaultLookupConfig(); +// } +// +// static ReaderConfig getDefaultIteratorConfig() { +// return DictionaryConfigurations.defaultIteratorConfig(); +// } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/ReaderConfigurator.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/ReaderConfigurator.java new file mode 100644 index 0000000..600c6a2 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/ReaderConfigurator.java @@ -0,0 +1,57 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.config; + +import org.tools4j.mmap.region.config.MappingStrategy; +import org.tools4j.mmap.region.config.MappingStrategyConfig; +import org.tools4j.mmap.region.config.MappingStrategyConfigurator; + +import java.util.function.Consumer; + +public interface ReaderConfigurator extends ReaderConfig { + ReaderConfigurator mappingStrategy(MappingStrategy strategy); + ReaderConfigurator mappingStrategy(MappingStrategyConfig config); + ReaderConfigurator mappingStrategy(Consumer configurator); + ReaderConfigurator headerMappingStrategy(MappingStrategy strategy); + ReaderConfigurator headerMappingStrategy(MappingStrategyConfig config); + ReaderConfigurator headerMappingStrategy(Consumer configurator); + ReaderConfigurator payloadMappingStrategy(MappingStrategy strategy); + ReaderConfigurator payloadMappingStrategy(MappingStrategyConfig config); + ReaderConfigurator payloadMappingStrategy(Consumer configurator); + ReaderConfigurator closeHeaderFiles(boolean closeHeaderFiles); + ReaderConfigurator closePayloadFiles(boolean closePayloadFiles); + ReaderConfigurator reset(); + +// static ReaderConfigurator configureLookup() { +// return ReaderConfiguratorImpl.createLookupConfigurator(); +// } +// +// static ReaderConfigurator configureIterator() { +// return ReaderConfiguratorImpl.createIteratorConfigurator(); +// } +// +// static ReaderConfigurator configure(final ReaderConfig defaults) { +// return ReaderConfiguratorImpl.createConfigurator(defaults); +// } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/UpdaterConfig.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/UpdaterConfig.java new file mode 100644 index 0000000..4b7743a --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/UpdaterConfig.java @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.config; + +import org.tools4j.mmap.region.config.MappingStrategy; + +public interface UpdaterConfig { + MappingStrategy sectorMappingStrategy(); + MappingStrategy ownPayloadMappingStrategy(); + MappingStrategy otherPayloadMappingStrategy(); + UpdaterConfig toImmutableUpdaterConfig(); + +// static UpdaterConfigurator configure() { +// return UpdaterConfigurator.configure(); +// } +// +// static UpdaterConfig getDefault() { +// return DictionaryConfigurations.defaultUpdaterConfig(); +// } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/UpdaterConfigurator.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/UpdaterConfigurator.java new file mode 100644 index 0000000..3df63aa --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/config/UpdaterConfigurator.java @@ -0,0 +1,51 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.config; + +import org.tools4j.mmap.region.config.MappingStrategy; +import org.tools4j.mmap.region.config.MappingStrategyConfig; +import org.tools4j.mmap.region.config.MappingStrategyConfigurator; + +import java.util.function.Consumer; + +public interface UpdaterConfigurator extends UpdaterConfig { + UpdaterConfigurator mappingStrategy(MappingStrategy strategy); + UpdaterConfigurator mappingStrategy(MappingStrategyConfig config); + UpdaterConfigurator mappingStrategy(Consumer configurator); + UpdaterConfigurator headerMappingStrategy(MappingStrategy strategy); + UpdaterConfigurator headerMappingStrategy(MappingStrategyConfig config); + UpdaterConfigurator headerMappingStrategy(Consumer configurator); + UpdaterConfigurator payloadMappingStrategy(MappingStrategy strategy); + UpdaterConfigurator payloadMappingStrategy(MappingStrategyConfig config); + UpdaterConfigurator payloadMappingStrategy(Consumer configurator); + UpdaterConfigurator reset(); + +// static UpdaterConfigurator configure() { +// return new UpdaterConfiguratorImpl(); +// } +// +// static UpdaterConfigurator configure(final UpdaterConfig defaults) { +// return new UpdaterConfiguratorImpl(defaults); +// } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/CompareAndSet.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/CompareAndSet.java new file mode 100644 index 0000000..c5f8634 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/CompareAndSet.java @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.concurrent.AtomicBuffer; + +/** + * Compare and set utility + */ +public enum CompareAndSet { + ; + static long incrementAndGet(final AtomicBuffer buffer, final int index) { + long current, next; + do { + current = buffer.getLongVolatile(index); + next = current + 1; + } while (next >= 0 && !buffer.compareAndSetLong(index, current, next)); + return next; + } + + static long decrementAndGet(final AtomicBuffer buffer, final int index) { + long current, next; + do { + current = buffer.getLongVolatile(index); + next = current - 1; + } while (next >= 0 && !buffer.compareAndSetLong(index, current, next)); + return next; + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DataDescriptor.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DataDescriptor.java new file mode 100644 index 0000000..b58f30b --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DataDescriptor.java @@ -0,0 +1,125 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.tools4j.mmap.region.api.OffsetMapping; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static org.tools4j.mmap.dictionary.impl.Exceptions.mappingMoveException; +import static org.tools4j.mmap.dictionary.impl.Exceptions.mappingMoveToNextRegionException; + +/** + * Describes the layout of entries in the data file: + *

+ * For keys, we have + *

+
+ 0         1         2         3         4         5         6
+ 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |                           Key Hash                            |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ |         Payload Size          |       (unused padding)        |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ |                       Key Payload Data                        |
+ |                              ...                              |
+ +=======+=======+=======+=======+=======+=======+=======+=======+
+
+ * 
+ *

+ * For values, we have + *

+
+ 0         1         2         3         4         5         6
+ 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |         Payload Size          |       (unused padding)        |
+ +-------+-------+-------+-------+-------+-------+-------+-------+
+ |                      Value Payload Data                       |
+ |                              ...                              |
+ +=======+=======+=======+=======+=======+=======+=======+=======+
+
+ * 
+ */ +public enum DataDescriptor { + ; + private static final int PAYLOAD_SIZE_LENGTH = Integer.BYTES; + + public static final String NAME = "data"; + public static final int KEY_HASH_OFFSET = 0; + public static final int KEY_HASH_LENGTH = Long.BYTES; + public static final int KEY_PAYLOAD_SIZE_OFFSET = KEY_HASH_OFFSET + KEY_HASH_LENGTH; + public static final int KEY_PAYLOAD_SIZE_LENGTH = PAYLOAD_SIZE_LENGTH; + public static final int KEY_PADDING_OFFSET = KEY_PAYLOAD_SIZE_OFFSET + KEY_PAYLOAD_SIZE_LENGTH; + public static final int KEY_PADDING_LENGTH = Integer.BYTES; + public static final int KEY_PAYLOAD_OFFSET = KEY_PADDING_OFFSET + KEY_PADDING_LENGTH; + + public static final int VALUE_PAYLOAD_SIZE_OFFSET = 0; + public static final int VALUE_PAYLOAD_SIZE_LENGTH = PAYLOAD_SIZE_LENGTH; + public static final int VALUE_PADDING_OFFSET = VALUE_PAYLOAD_SIZE_OFFSET + VALUE_PAYLOAD_SIZE_LENGTH; + public static final int VALUE_PADDING_LENGTH = Integer.BYTES; + public static final int VALUE_PAYLOAD_OFFSET = VALUE_PADDING_OFFSET + VALUE_PADDING_LENGTH; + + public long moveAndGetKeyHash(final OffsetMapping mapping, final long keyPosition) { + final long position = keyPosition + KEY_HASH_OFFSET; + if (mapping.moveTo(position)) { + return mapping.buffer().getLong(0, LITTLE_ENDIAN); + } + throw mappingMoveException(NAME, position); + } + + public int moveToKeyPayload(final OffsetMapping mapping, final long keyPosition) { + return moveToPayload(mapping, keyPosition + KEY_PAYLOAD_SIZE_OFFSET); + } + + public int moveToValuePayload(final OffsetMapping mapping, final long valuePosition) { + return moveToPayload(mapping, valuePosition + VALUE_PAYLOAD_OFFSET); + } + + private int moveToPayload(final OffsetMapping mapping, final long position) { + if (mapping.moveTo(position)) { + ensureBytesAvailable(mapping, PAYLOAD_SIZE_LENGTH); + final int payloadSize = mapping.buffer().getInt(0); + mapping.moveBy(PAYLOAD_SIZE_LENGTH); + ensureBytesAvailable(mapping, payloadSize); + return payloadSize; + } + throw mappingMoveException(NAME, position); + } + + private void ensureBytesAvailable(final OffsetMapping mapping, final int minBytesAvailable) { + if (mapping.bytesAvailable() >= minBytesAvailable) { + return; + } + final long position = mapping.position(); + if (mapping.moveToNextRegion() && mapping.bytesAvailable() >= minBytesAvailable) { + return; + } + if (minBytesAvailable > mapping.regionSize()) { + throw new IllegalStateException("Min bytes available " + minBytesAvailable + " exceeds region size " + + mapping.regionSize()); + } + throw mappingMoveToNextRegionException(NAME, mapping.regionMetrics(), position); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictFiles.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictFiles.java new file mode 100644 index 0000000..825582b --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictFiles.java @@ -0,0 +1,94 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import java.io.File; +import java.util.function.IntFunction; + +import static java.util.Objects.requireNonNull; +import static org.tools4j.mmap.dictionary.impl.SectorDescriptor.MAX_SECTORS; + +/** + * Files used for a dictionary. + */ +final class DictFiles { + public static final String FILE_ENDING = ".mmd"; + + private final File dictionaryFile; + private final File indexFile; + private final File idPoolFile; + private final File[] sectorFiles; + private final IntFunction sectorFileFactory; + private final File[] payloadFiles; + private final IntFunction payloadFileFactory; + + public DictFiles(final File dictionaryFile, final int maxUpdaters) { + this.dictionaryFile = requireNonNull(dictionaryFile); + this.indexFile = new File(dictionaryFile, dictionaryFile.getName() + "_idx.mmd"); + this.idPoolFile = new File(dictionaryFile, dictionaryFile.getName() + "_ids.mmd"); + this.sectorFiles = new File[MAX_SECTORS]; + this.sectorFileFactory = sector -> new File(dictionaryFile, dictionaryFile.getName() + "_sec_" + sector + ".mmd"); + this.payloadFiles = new File[maxUpdaters]; + this.payloadFileFactory = updaterId -> new File(dictionaryFile, dictionaryFile.getName() + "_dat_" + updaterId + ".mmd"); + } + + public String dictionaryName() { + return dictionaryFile.getName(); + } + + public File indexFile() { + return indexFile; + } + + public File idPoolFile() { + return idPoolFile; + } + + public File sectorFile(final int sector) { + File sectorFile = sectorFiles[sector]; + if (sectorFile == null) { + sectorFile = sectorFileFactory.apply(sector); + sectorFiles[sector] = sectorFile; + } + return sectorFile; + } + + public File payloadFile(final int appenderId) { + File payloadFile = payloadFiles[appenderId]; + if (payloadFile == null) { + payloadFile = payloadFileFactory.apply(appenderId); + payloadFiles[appenderId] = payloadFile; + } + return payloadFile; + } + + public File[] listFiles() { + return dictionaryFile.listFiles((dir, name) -> name != null && name.equals(FILE_ENDING)); + } + + @Override + public String toString() { + return "DictFiles:dictionary=" + dictionaryName(); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictMappingConfigs.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictMappingConfigs.java new file mode 100644 index 0000000..145f037 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictMappingConfigs.java @@ -0,0 +1,153 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.tools4j.mmap.dictionary.config.DictionaryConfig; +import org.tools4j.mmap.dictionary.config.ReaderConfig; +import org.tools4j.mmap.dictionary.config.UpdaterConfig; +import org.tools4j.mmap.region.api.AccessMode; +import org.tools4j.mmap.region.config.MappingConfig; +import org.tools4j.mmap.region.config.MappingStrategy; +import org.tools4j.mmap.region.impl.MappingConfigImpl; + +import static java.util.Objects.requireNonNull; + +enum DictMappingConfigs { + ; + + static MappingConfig sectorMappingConfig(final DictionaryConfig dictionaryConfig, final UpdaterConfig updaterConfig) { + return sectorMappingConfig(dictionaryConfig, updaterConfig.sectorMappingStrategy()); + } + + static MappingConfig sectorMappingConfig(final DictionaryConfig dictionaryConfig, final ReaderConfig readerConfig) { + return sectorMappingConfig(dictionaryConfig, readerConfig.sectorMappingStrategy()); + } + + static MappingConfig sectorMappingConfig(final DictionaryConfig dictConfig, final MappingStrategy mappingStrategy) { + requireNonNull(dictConfig); + requireNonNull(mappingStrategy); + return new MappingConfig() { + @Override + public long maxFileSize() { + return dictConfig.maxHeaderFileSize(); + } + + @Override + public boolean expandFile() { + return dictConfig.expandHeaderFile(); + } + + @Override + public boolean rollFiles() { + return dictConfig.rollHeaderFile(); + } + + @Override + public boolean closeFiles() { + return false; + } + + @Override + public int filesToCreateAhead() { + return dictConfig.headerFilesToCreateAhead(); + } + + @Override + public MappingStrategy mappingStrategy() { + return mappingStrategy; + } + + @Override + public MappingConfig toImmutableMappingConfig() { + final DictionaryConfig immutableConfig = dictConfig.toImmutableDictionaryConfig(); + return dictConfig == immutableConfig ? this : sectorMappingConfig(immutableConfig, mappingStrategy); + } + + @Override + public String toString() { + return MappingConfigImpl.toString("sectorMappingConfig", this); + } + }; + } + + static MappingConfig payloadMappingConfig(final DictionaryConfig dictionaryConfig, + final UpdaterConfig updaterConfig, + final AccessMode accessMode) { + return payloadMappingConfig(dictionaryConfig, accessMode == AccessMode.READ_WRITE + ? updaterConfig.ownPayloadMappingStrategy() + : updaterConfig.otherPayloadMappingStrategy()); + } + + static MappingConfig payloadMappingConfig(final DictionaryConfig dictionaryConfig, final ReaderConfig readerConfig) { + return payloadMappingConfig(dictionaryConfig, readerConfig.payloadMappingStrategy()); + } + + static MappingConfig payloadMappingConfig(final DictionaryConfig dictConfig, + final MappingStrategy mappingStrategy) { + requireNonNull(dictConfig); + requireNonNull(mappingStrategy); + return new MappingConfig() { + @Override + public long maxFileSize() { + return dictConfig.maxPayloadFileSize(); + } + + @Override + public boolean expandFile() { + return dictConfig.expandPayloadFiles(); + } + + @Override + public boolean rollFiles() { + return dictConfig.rollPayloadFiles(); + } + + @Override + public boolean closeFiles() { + return false; + } + + @Override + public int filesToCreateAhead() { + return dictConfig.payloadFilesToCreateAhead(); + } + + @Override + public MappingStrategy mappingStrategy() { + return mappingStrategy; + } + + @Override + public MappingConfig toImmutableMappingConfig() { + final DictionaryConfig immutableConfig = dictConfig.toImmutableDictionaryConfig(); + return dictConfig == immutableConfig ? this : sectorMappingConfig(immutableConfig, mappingStrategy); + } + + @Override + public String toString() { + return MappingConfigImpl.toString("PayloadMappingConfig", this); + } + }; + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictionaryImpl.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictionaryImpl.java new file mode 100644 index 0000000..e41cdb3 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/DictionaryImpl.java @@ -0,0 +1,198 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.CloseHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.tools4j.mmap.dictionary.api.Dictionary; +import org.tools4j.mmap.dictionary.api.DictionaryIterator; +import org.tools4j.mmap.dictionary.api.Lookup; +import org.tools4j.mmap.dictionary.api.Updater; +import org.tools4j.mmap.dictionary.config.DictionaryConfig; +import org.tools4j.mmap.dictionary.config.ReaderConfig; +import org.tools4j.mmap.dictionary.config.UpdaterConfig; +import org.tools4j.mmap.region.api.AccessMode; +import org.tools4j.mmap.region.config.MappingStrategy; +import org.tools4j.mmap.region.impl.IdPool; +import org.tools4j.mmap.region.impl.IdPool256; +import org.tools4j.mmap.region.impl.IdPool64; + +import java.io.File; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; + +/** + * Implementation of {@link Dictionary} that allows a multiple writing threads and + * multiple reading threads, when each thread creates their own instances of + * {@link #createUpdater() updater}, {@link #createLookup() lookup} and {@link #createIterator() iterator}. + */ +public final class DictionaryImpl implements Dictionary { + private static final Logger LOGGER = LoggerFactory.getLogger(DictionaryImpl.class); + + private final DictFiles files; + private final DictionaryConfig config; + private final Function lookupFactory; + private final Function iteratorFactory; + private final Function updaterFoactory; + private final AtomicBoolean closed = new AtomicBoolean(); + private final Queue closeables = new ConcurrentLinkedQueue<>(); + + public DictionaryImpl(final File file) { +// this(file, DictionaryConfig.getDefault()); + this(file, null); + } + + public DictionaryImpl(final File file, final DictionaryConfig dictionaryConfig) { + final AccessMode accessMode = dictionaryConfig.accessMode(); + final int maxUpdaters = dictionaryConfig.maxUpdaters(); + this.files = new DictFiles(file, maxUpdaters); + this.config = dictionaryConfig.toImmutableDictionaryConfig(); + if (accessMode == AccessMode.READ_WRITE_CLEAR) { + deleteDictionaryFiles(); + } + if (!file.exists() && accessMode != AccessMode.READ_ONLY) { + createDictionaryDir(file); + } + + final IdPool idPool = open(updaterIdPool(files, maxUpdaters)); + this.lookupFactory = readerConfig -> null; + this.iteratorFactory = readerConfig -> null; + this.updaterFoactory = updaterConfig -> null; +// this.lookupFactory = lookupConfig -> open(new LookupImpl( +// dictionaryNameIfNotClosed(), +// ReaderMappings.create(files, config, lookupConfig) +// )); +// this.iteratorFactory = iteratorConfig -> open(new DictionaryIteratorImpl( +// dictionaryNameIfNotClosed(), +// ReaderMappings.create(files, config, iteratorConfig) +// )); +// this.updaterFoactory = accessMode == AccessMode.READ_ONLY ? +// updaterConfig -> {throw new IllegalStateException( +// "Cannot open updater in read-only mode for dictionary " + dictionaryNameIfNotClosed()); +// } : +// updaterConfig -> open(new UpdaterImpl( +// dictionaryNameIfNotClosed(), +// UpdaterMappings.create(files, idPool, config, updaterConfig), +// enableCopyFromPreviousRegion(updaterConfig) +// )); + } + + private void deleteDictionaryFiles() { + for (final File file : files.listFiles()) { + final boolean deleted = file.delete(); + assert deleted : files.dictionaryName(); + } + } + + private void createDictionaryDir(final File file) { + if (!file.mkdir()) { + throw new IllegalArgumentException("Parent directory does not exist: " + file); + } + } + + private static boolean enableCopyFromPreviousRegion(final UpdaterConfig updaterConfig) { + final MappingStrategy mappingStrategy = updaterConfig.ownPayloadMappingStrategy(); + final int cacheSie = mappingStrategy.cacheSize(); + final int mapAhead = mappingStrategy.asyncOptions().isPresent() ? + mappingStrategy.asyncOptions().get().regionsToMapAhead() : 0; + return cacheSie > Math.max(1, mapAhead + 1); + } + + private static IdPool updaterIdPool(final DictFiles dictFiles, final int maxUpdaters) { + if (maxUpdaters <= IdPool64.MAX_IDS) { + return new IdPool64(dictFiles.idPoolFile()); + } + if (maxUpdaters <= IdPool256.MAX_IDS) { + return new IdPool256(dictFiles.idPoolFile()); + } + throw new IllegalArgumentException("Invalid value for max updaters: " + maxUpdaters); + } + + private T open(final T closeable) { + closeables.add(closeable); + return closeable; + } + + @Override + public Updater createUpdater() { + return createUpdater(config.updaterConfig()); + } + + @Override + public Updater createUpdater(final UpdaterConfig config) { + return updaterFoactory.apply(config); + } + + @Override + public Lookup createLookup() { + return createLookup(config.lookupConfig()); + } + + @Override + public Lookup createLookup(final ReaderConfig config) { + return lookupFactory.apply(config); + } + + @Override + public DictionaryIterator createIterator() { + return createIterator(config.iteratorConfig()); + } + + @Override + public DictionaryIterator createIterator(final ReaderConfig config) { + return iteratorFactory.apply(config); + } + + private String dictionaryNameIfNotClosed() { + if (isClosed()) { + return files.dictionaryName(); + } + throw new IllegalStateException("Dictionary is closed: " + files.dictionaryName()); + } + + + @Override + public boolean isClosed() { + return closed.get(); + } + + @Override + public void close() { + if (closed.compareAndSet(false, true)) { + CloseHelper.quietCloseAll(closeables); + closeables.clear(); + LOGGER.info("Closed dictionary: {}", files.dictionaryName()); + } + } + + @Override + public String toString() { + return "DictionaryImpl" + + ":queue=" + files.dictionaryName() + + "|closed=" + isClosed(); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Exceptions.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Exceptions.java new file mode 100644 index 0000000..1fa79e6 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Exceptions.java @@ -0,0 +1,70 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.tools4j.mmap.region.api.RegionMetrics; + +enum Exceptions { + ; + + static IllegalArgumentException invalidIndexException(final String name, final long index) { + return new IllegalArgumentException("Invalid index for " + name + ": " + index); + } + +// static IllegalStateException payloadPositionExceedsMaxException(final UpdaterImpl updater, final long position) { +// throw new IllegalStateException("Moving " + updater.updaterName() + ".payload to position " + position + +// " exceeds max allowed position " + MAX_PAYLOAD_POSITION); +// } +// +// static IllegalStateException headerMoveException(final UpdaterImpl updater, final long position) { +// return mappingMoveException(updater.updaterName() + ".header", position); +// } +// +// static IllegalStateException payloadMoveException(final UpdaterImpl updater, final long position) { +// return mappingMoveException(updater.updaterName() + ".payload", position); +// } +// +// static IllegalStateException payloadMoveException(final LookupImpl lookup, +// final int updaterId, +// final long position) { +// return mappingMoveException(lookup.lookupName() + "-" + updaterId + ".payload", position); +// } +// +// static IllegalStateException payloadMoveException(final DictionaryIteratorImpl iterator, +// final int updaterId, +// final long position) { +// return mappingMoveException(iterator.iteratorName() + "-" + updaterId + ".payload", position); +// } + + static IllegalStateException mappingMoveToNextRegionException(final String name, + final RegionMetrics metrics, + final long position) { + final long regionIndex = metrics.regionIndex(position); + throw mappingMoveException("data", metrics.regionPositionByIndex(regionIndex + 1)); + } + + static IllegalStateException mappingMoveException(final String name, final long position) { + return new IllegalStateException("Moving " + name + " mapping to position " + position + " failed"); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Hash.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Hash.java new file mode 100644 index 0000000..16702f0 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Hash.java @@ -0,0 +1,143 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.DirectBuffer; + +import static java.lang.Long.rotateLeft; +import static java.nio.ByteOrder.LITTLE_ENDIAN; + +/** + * Contains fast static hash code functions. + */ +public enum Hash { + ; + private static final long PRIME64_1 = -7046029288634856825L; //11400714785074694791 + private static final long PRIME64_2 = -4417276706812531889L; //14029467366897019727 + private static final long PRIME64_3 = 1609587929392839161L; + private static final long PRIME64_4 = -8796714831421723037L; //9650029242287828579 + private static final long PRIME64_5 = 2870177450012600261L; + + /** + * Fast XXH64 has as per https://github.com/Cyan4973/xxHash + * + * @param buffer buffer with data to hash + * @param offset offset in buffer + * @param length length of data + * @return the XXH64 hash code for given data with zero seed + */ + public static long xxHash64(final DirectBuffer buffer, final int offset, final int length) { + return xxHash64(0L, buffer, offset, length); + } + + /** + * Fast XXH64 has as per https://github.com/Cyan4973/xxHash + * + * @param seed seed for hash code calculation + * @param buffer buffer with data to hash + * @param offset offset in buffer + * @param length length of data + * @return the XXH64 hash code for given seed and data + */ + @SuppressWarnings("PointlessArithmeticExpression") + public static long xxHash64(final long seed, final DirectBuffer buffer, final int offset, final int length) { + final int end = offset + length; + int off = offset; + long h64; + + if (length >= 32) { + final int limit = end - 32; + long v1 = seed + PRIME64_1 + PRIME64_2; + long v2 = seed + PRIME64_2; + long v3 = seed + 0; + long v4 = seed - PRIME64_1; + do { + v1 += buffer.getLong(off, LITTLE_ENDIAN) * PRIME64_2; + v1 = rotateLeft(v1, 31); + v1 *= PRIME64_1; + off += 8; + + v2 += buffer.getLong(off, LITTLE_ENDIAN) * PRIME64_2; + v2 = rotateLeft(v2, 31); + v2 *= PRIME64_1; + off += 8; + + v3 += buffer.getLong(off, LITTLE_ENDIAN) * PRIME64_2; + v3 = rotateLeft(v3, 31); + v3 *= PRIME64_1; + off += 8; + + v4 += buffer.getLong(off, LITTLE_ENDIAN) * PRIME64_2; + v4 = rotateLeft(v4, 31); + v4 *= PRIME64_1; + off += 8; + } while (off <= limit); + + h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); + + v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; + h64 = h64 * PRIME64_1 + PRIME64_4; + + v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; + h64 = h64 * PRIME64_1 + PRIME64_4; + + v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; + h64 = h64 * PRIME64_1 + PRIME64_4; + + v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; + h64 = h64 * PRIME64_1 + PRIME64_4; + } else { + h64 = seed + PRIME64_5; + } + + h64 += length; + + while (off <= end - 8) { + long k1 = buffer.getLong(off, LITTLE_ENDIAN); + k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; + h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; + off += 8; + } + + if (off <= end - 4) { + h64 ^= (buffer.getInt(off, LITTLE_ENDIAN) & 0xFFFFFFFFL) * PRIME64_1; + h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; + off += 4; + } + + while (off < end) { + h64 ^= (buffer.getByte(off) & 0xFF) * PRIME64_5; + h64 = rotateLeft(h64, 11) * PRIME64_1; + ++off; + } + + h64 ^= h64 >>> 33; + h64 *= PRIME64_2; + h64 ^= h64 >>> 29; + h64 *= PRIME64_3; + h64 ^= h64 >>> 32; + + return h64; + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Headers.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Headers.java new file mode 100644 index 0000000..e93c0bc --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/Headers.java @@ -0,0 +1,147 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.tools4j.mmap.region.impl.IdPool256; + +/** + * Methods to encode and decode header values, and related methods. + */ +enum Headers { + ; + public static final int HEADER_LENGTH = Long.BYTES; + private static final int HEADER_SHIFT = 3; + + public static final long NULL_HEADER = 0L; + public static final long TOMBSTONE_BIT = 1L<<63; + public static final long TOMBSTONE_HEADER = -1L; + + /** + * Header position cannot be negative and must be a multiple of {@link #HEADER_LENGTH} + */ + private static final long HEADER_POSITION_MASK = Long.MAX_VALUE & -HEADER_LENGTH; + + /** + * This is the logarithm base 2 of {@link #PAYLOAD_GRANULARITY} used to shift the payload position to erase the + * unused zero bits. + */ + public static final int PAYLOAD_GRANULARITY_BITS = 3; + + /** + * Payload granularity is 8 bytes, which gives good word alignment and very little wasted storage. + * Note that we always store an integer for the payload size. This means that at most 4 bytes are wasted to padding + * which only happens for zero payload entries. + */ + public static final long PAYLOAD_GRANULARITY = 1L << PAYLOAD_GRANULARITY_BITS; + /** + * One byte for updater ID + */ + public static final int UPDATER_ID_BITS = Byte.SIZE; + /** + * At most 256 updaters, whose IDs can for instance be managed by {@link IdPool256} + */ + public static final int MAX_UPDATERS = 1 << UPDATER_ID_BITS; + /** + * Maximum allowed updater ID is 255. + */ + public static final int MAX_UPDATER_ID = MAX_UPDATERS - 1; + /** + * Minimum allowed updater ID is 0. + */ + public static final int MIN_UPDATER_ID = 0; + + private static final long UPDATER_ID_MASK = MAX_UPDATERS - 1; + private static final long UPDATER_ID_HEADER_MASK = UPDATER_ID_MASK; + private static final int ADJUSTED_POSITION_SHIFT = UPDATER_ID_BITS - PAYLOAD_GRANULARITY_BITS; + private static final long ADJUSTED_POSITION_HEADER_MASK = ~UPDATER_ID_HEADER_MASK; + private static final long ADJUSTED_POSITION_MASK = ADJUSTED_POSITION_HEADER_MASK >>> ADJUSTED_POSITION_SHIFT; + + private static final long PAYLOAD_POSITION_ADJUSTMENT = PAYLOAD_GRANULARITY; + /** + * Max payload position is 576,460,752,303,423,472, which is > 500,000 terabytes. + */ + public static final long MAX_PAYLOAD_POSITION = ADJUSTED_POSITION_MASK - PAYLOAD_POSITION_ADJUSTMENT; + + public static int updaterId(final long header) { + return (int) (header & UPDATER_ID_HEADER_MASK); + } + + public static long payloadPosition(final long header) { + return ((header & ADJUSTED_POSITION_HEADER_MASK) >>> ADJUSTED_POSITION_SHIFT) - PAYLOAD_POSITION_ADJUSTMENT; + } + + private static long payloadBytesWithPadding(final int payloadBytes) { + return ((payloadBytes + PAYLOAD_GRANULARITY - 1) >>> PAYLOAD_GRANULARITY_BITS) << PAYLOAD_GRANULARITY_BITS; + } + + public static long nextPayloadPosition(final long currentPayloadPosition, final int currentPayloadBytes) { + assert validPayloadPosition(currentPayloadPosition) : "currentPayloadPosition must be valid"; + assert currentPayloadBytes >= 0 : "currentPayloadBytes must not be negative"; + return currentPayloadPosition + payloadBytesWithPadding(currentPayloadBytes); + } + + public static boolean validUpdaterId(final int updaterId) { + return updaterId == (updaterId & UPDATER_ID_MASK); + } + + public static boolean validPayloadPosition(final long payloadPosition) { + final long adjustedPosition = payloadPosition + PAYLOAD_POSITION_ADJUSTMENT; + return payloadPosition >= 0 && adjustedPosition == (adjustedPosition & ADJUSTED_POSITION_MASK); + } + + public static boolean validHeaderPosition(final long headerPosition) { + return headerPosition == (headerPosition & HEADER_POSITION_MASK); + } + + public static int validateUpdaterId(final int updaterId) { + if (validUpdaterId(updaterId)) { + return updaterId; + } + if (updaterId > MAX_UPDATER_ID) { + throw new IllegalArgumentException("Updater ID " + updaterId + " exceeds max allowed value " + + MAX_UPDATER_ID); + } + //should never happen: negative value + throw new IllegalArgumentException("Invalid updater ID " + updaterId); + } + + public static long validatePayloadPosition(final long payloadPosition) { + if (validPayloadPosition(payloadPosition)) { + return payloadPosition; + } + if (payloadPosition > MAX_PAYLOAD_POSITION) { + throw new IllegalArgumentException("Payload position " + payloadPosition + " exceeds max allowed value " + + MAX_PAYLOAD_POSITION); + } + //should never happen: negative or not multiple of PAYLOAD_GRANULARITY + throw new IllegalArgumentException("Invalid payload position " + payloadPosition); + } + + public static long header(final int updaterId, final long payloadPosition) { + assert validUpdaterId(updaterId) : "updaterId is invalid"; + assert validPayloadPosition(payloadPosition) : "payloadPosition is invalid"; + final long adjustedPosition = payloadPosition + PAYLOAD_POSITION_ADJUSTMENT; + return (updaterId & UPDATER_ID_HEADER_MASK) | ((adjustedPosition << ADJUSTED_POSITION_SHIFT) & ADJUSTED_POSITION_HEADER_MASK); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/IndexDescriptor.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/IndexDescriptor.java new file mode 100644 index 0000000..8994e35 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/IndexDescriptor.java @@ -0,0 +1,180 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.DirectBuffer; +import org.agrona.concurrent.AtomicBuffer; + +import static org.tools4j.mmap.dictionary.impl.SectorDescriptor.MAX_SECTORS; + +/** + * Describes the layout of the dictionary index file: + *
+
+    0         1         2         3         4         5         6
+    0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |            (Unused)           |      First Sector Entry       |
+    +=======+=======+=======+=======+=======+=======+=======+=======+
+    |                     Sector Start Position                     |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                          Active Slots                         |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                           Used Slots                          |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |    Log2 Sector Slots (Bits)   |        Next Sector Entry      |
+    +=======+=======+=======+=======+=======+=======+=======+=======+
+    |                     Sector Start Position                     |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                          Active Slots                         |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                           Used Slots                          |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |    Log2 Sector Slots (Bits)   |       Next Sector Entry       |
+    +=======+=======+=======+=======+=======+=======+=======+=======+
+    |                              ...                              |
+
+ * 
+ */ +public enum IndexDescriptor { + ; + //offsets from start of file + public static final int UNUSED_OFFSET = 0; + public static final int UNUSED_LENGTH = Integer.BYTES; + public static final int FIRST_ENTRY_OFFSET = UNUSED_OFFSET + UNUSED_LENGTH; + public static final int FIRST_ENTRY_LENGTH = Integer.BYTES; + public static final int ENTRY_0_OFFSET = FIRST_ENTRY_OFFSET + FIRST_ENTRY_LENGTH; + + //offsets relative to sector entry offset + public static final int START_POSITION_OFFSET = 0; + public static final int START_POSITION_LENGTH = Long.BYTES; + public static final int ACTIVE_SLOTS_OFFSET = START_POSITION_OFFSET + START_POSITION_LENGTH; + public static final int ACTIVE_SLOTS_LENGTH = Long.BYTES; + public static final int USED_SLOTS_OFFSET = ACTIVE_SLOTS_OFFSET + ACTIVE_SLOTS_LENGTH; + public static final int USED_SLOTS_LENGTH = Long.BYTES; + public static final int SECTOR_SLOTS_OFFSET = USED_SLOTS_OFFSET + USED_SLOTS_LENGTH; + public static final int SECTOR_SLOTS_LENGTH = Integer.BYTES; + public static final int NEXT_ENTRY_OFFSET = SECTOR_SLOTS_OFFSET + SECTOR_SLOTS_LENGTH; + public static final int NEXT_ENTRY_LENGTH = Integer.BYTES; + + public static final int ENTRY_LENGTH = START_POSITION_LENGTH + ACTIVE_SLOTS_LENGTH + USED_SLOTS_LENGTH + + SECTOR_SLOTS_LENGTH + NEXT_ENTRY_LENGTH; + + public static final int FILE_SIZE = ENTRY_0_OFFSET + MAX_SECTORS * ENTRY_LENGTH; + + static int firstSectorIndexVolatile(final AtomicBuffer buffer) { + return buffer.getIntVolatile(FIRST_ENTRY_OFFSET); + } + + static int sectorOffset(final int sector) { + assert sector >= 0 : "invalid sector"; + return ENTRY_0_OFFSET + sector * ENTRY_LENGTH; + } + + static long sectorStartPosition(final DirectBuffer buffer, final int sector) { + return buffer.getLong(sectorOffset(sector) + START_POSITION_OFFSET); + } + + static long sectorActiveSlotsVolatile(final AtomicBuffer buffer, final int sector) { + return buffer.getLongVolatile(sectorOffset(sector) + ACTIVE_SLOTS_OFFSET); + } + + static long sectorActiveSlotsIncrementAndGet(final AtomicBuffer buffer, final int sector) { + final int offset = sectorOffset(sector) + ACTIVE_SLOTS_OFFSET; + final long slots = CompareAndSet.incrementAndGet(buffer, offset); + if (slots >= 0) { + return slots; + } + throw new IllegalStateException("Cannot increment active slots for sector " + sector + " to " + slots); + } + + static long sectorUsedSlotsVolatile(final AtomicBuffer buffer, final int sector) { + return buffer.getLongVolatile(sectorOffset(sector) + USED_SLOTS_OFFSET); + } + + static long sectorUsedSlotsIncrementAndGet(final AtomicBuffer buffer, final int sector) { + final int offset = sectorOffset(sector) + USED_SLOTS_OFFSET; + final long slots = CompareAndSet.incrementAndGet(buffer, offset); + if (slots >= 0) { + return slots; + } + throw new IllegalStateException("Cannot increment used slots for sector " + sector + " to " + slots); + } + + static long sectorUsedSlotsDecrementAndGet(final AtomicBuffer buffer, final int sector) { + final int offset = sectorOffset(sector) + USED_SLOTS_OFFSET; + final long slots = CompareAndSet.decrementAndGet(buffer, offset); + if (slots >= 0) { + return slots; + } + throw new IllegalStateException("Cannot decrement used slots for sector " + sector + " to " + slots); + } + + static int sectorSlotsInBits(final DirectBuffer buffer, final int sector) { + return buffer.getInt(sectorOffset(sector) + SECTOR_SLOTS_OFFSET); + } + + static int nextSectorVolatile(final AtomicBuffer buffer, final int sector) { + return buffer.getIntVolatile(sectorOffset(sector) + NEXT_ENTRY_OFFSET); + } + + static boolean sectorInit(final AtomicBuffer buffer, + final int sector, + final long startPosition, + final int sectorSlotsInBits, + final boolean performChecks) { + //assert sector: done in sectorOffset(..) + assert startPosition >= 0 : "invalid startPosition"; + assert sectorSlotsInBits >= 0 : "invalid sectorSlotsInBits"; + final int offset = sectorOffset(sector); + if (performChecks) { + long value; + if ((value = buffer.getLongVolatile(offset + USED_SLOTS_OFFSET)) != 0) { + throw new IllegalArgumentException("Used slots for sector " + sector + " should be zero but is " + value); + } + if ((value = buffer.getLongVolatile(offset + ACTIVE_SLOTS_OFFSET)) != 0) { + throw new IllegalArgumentException("Active slots for sector " + sector + " should be zero but is " + value); + } + if ((value = buffer.getIntVolatile(offset + NEXT_ENTRY_OFFSET)) != 0) { + throw new IllegalArgumentException("Next sector for sector " + sector + " should be zero but is " + value); + } + } + buffer.putLong(offset + START_POSITION_OFFSET, startPosition); + buffer.putLong(offset + SECTOR_SLOTS_OFFSET, sectorSlotsInBits); + final int prevOffset = sector == 0 ? FIRST_ENTRY_OFFSET : (sectorOffset(sector - 1) + NEXT_ENTRY_OFFSET); + + //NOTE: - returning false below means another thread beat us to it + // - it is highly likely that it set exactly the same value that we wanted to set + // - if it is not the same value, then the next section was created and has already been outgrown + final boolean prevSet = buffer.compareAndSetInt(prevOffset, 0, sector); + if (!prevSet && performChecks) { + final int current = buffer.getIntVolatile(prevOffset); + if (current != sector) { + throw new IllegalStateException("Expected previous sector to point to " + sector + + " but it points to " + current); + } + } + return prevSet; + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/ReaderMappings.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/ReaderMappings.java new file mode 100644 index 0000000..712eea0 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/ReaderMappings.java @@ -0,0 +1,144 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.collections.Hashing; +import org.agrona.collections.Int2ObjectHashMap; +import org.tools4j.mmap.dictionary.config.DictionaryConfig; +import org.tools4j.mmap.dictionary.config.ReaderConfig; +import org.tools4j.mmap.region.api.AccessMode; +import org.tools4j.mmap.region.api.Mapping; +import org.tools4j.mmap.region.api.Mappings; +import org.tools4j.mmap.region.api.OffsetMapping; +import org.tools4j.mmap.region.config.MappingConfig; + +import java.util.function.IntFunction; + +import static java.util.Objects.requireNonNull; +import static org.tools4j.mmap.dictionary.impl.DictMappingConfigs.payloadMappingConfig; +import static org.tools4j.mmap.dictionary.impl.Headers.MAX_UPDATERS; +import static org.tools4j.mmap.dictionary.impl.SectorDescriptor.MAX_SECTORS; + +/** + * Mappings for dictionary readers. Note that some mappings still have write access so the + * reader can help with copying of key/value headers and hashes to new sectors. + */ +interface ReaderMappings extends AutoCloseable { + /** + * Returns the read/write mapping with the sector index. + * @return mapping with sector index + */ + Mapping index(); + + /** + * Returns the read/write mapping with sector data containing keys and value headers and hashes. + * + * @param sector the sector, referencing an entry from the index file + * @return mapping with sector data + */ + OffsetMapping sector(int sector); + + /** + * Returns the read-only mapping with payload data containing key and value payload data owned by the updater + * specified by ID. + * + * @param updaterId the ID of the updater writing the given data + * @return mapping with key and value payload data + */ + OffsetMapping payload(int updaterId); + + boolean isClosed(); + + @Override + void close(); + + /** + * Factory method for reader mappings. + * + * @param dictFiles the dictionary files + * @param dictionaryConfig the dictionary configuration settings + * @param readerConfig configuration for reader mappings + * @return a new reader mappings instance + */ + static ReaderMappings create(final DictFiles dictFiles, + final DictionaryConfig dictionaryConfig, + final ReaderConfig readerConfig) { + requireNonNull(dictFiles); + final DictionaryConfig queueCfg = dictionaryConfig.toImmutableDictionaryConfig(); + final ReaderConfig readerCfg = readerConfig.toImmutableReaderConfig(); + + return new ReaderMappings() { + final MappingConfig sectorCfg = DictMappingConfigs.sectorMappingConfig(queueCfg, readerCfg); + final MappingConfig payloadCfg = payloadMappingConfig(queueCfg, readerCfg); + final Mapping index = Mappings.fixedSizeMapping(dictFiles.indexFile(), IndexDescriptor.FILE_SIZE, + AccessMode.READ_WRITE); + final Int2ObjectHashMap sectorMappings = new Int2ObjectHashMap<>(MAX_SECTORS, + Hashing.DEFAULT_LOAD_FACTOR); + final IntFunction sectorMappingFactory = sector -> Mappings.offsetMapping( + dictFiles.sectorFile(sector), AccessMode.READ_WRITE, sectorCfg); + + final Int2ObjectHashMap payloadMappings = new Int2ObjectHashMap<>(MAX_UPDATERS, + Hashing.DEFAULT_LOAD_FACTOR); + final IntFunction payloadMappingFactory = updaterId -> Mappings.offsetMapping( + dictFiles.sectorFile(updaterId), AccessMode.READ_ONLY, payloadCfg); + + @Override + public Mapping index() { + return index; + } + + @Override + public OffsetMapping sector(final int sector) { + return sectorMappings.computeIfAbsent(sector, sectorMappingFactory); + } + + @Override + public OffsetMapping payload(final int updaterId) { + return payloadMappings.computeIfAbsent(updaterId, payloadMappingFactory); + } + + @Override + public boolean isClosed() { + return index.isClosed(); + } + + @Override + public void close() { + if (!isClosed()) { + index.close(); + payloadMappings.forEachInt((appenderId, mapping) -> mapping.close()); + payloadMappings.clear(); + } + } + + @Override + public String toString() { + return "ReaderMappings" + + ":dictionary=" + dictFiles.dictionaryName() + + "|closed=" + isClosed(); + } + }; + } + +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/SectorDescriptor.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/SectorDescriptor.java new file mode 100644 index 0000000..6d007fe --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/SectorDescriptor.java @@ -0,0 +1,172 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.BitUtil; +import org.tools4j.mmap.region.api.OffsetMapping; +import org.tools4j.mmap.region.impl.Constants; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static org.tools4j.mmap.dictionary.impl.Exceptions.mappingMoveException; + +/** + * Describes the layout of a sector: + *
+
+    0         1         2         3         4         5         6
+    0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                          Key Header                           |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                         Value Header                          |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                          Key Header                           |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                         Value Header                          |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                              ...                              |
+    +=======+=======+=======+=======+=======+=======+=======+=======+
+    |                           Key Hash                            |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                           Key Hash                            |
+    +-------+-------+-------+-------+-------+-------+-------+-------+
+    |                              ...                              |
+    +=======+=======+=======+=======+=======+=======+=======+=======+
+
+ * 
+ */ +enum SectorDescriptor { + ; + /** KVH stands for key-value-hash */ + public static final String NAME = "kvh"; + public static final int KEY_OFFSET = 0; + public static final int KEY_LENGTH = Long.BYTES; + public static final int VALUE_OFFSET = KEY_OFFSET + KEY_LENGTH; + public static final int VALUE_LENGTH = Long.BYTES; + public static final int KEY_VALUE_LENGTH = KEY_LENGTH + VALUE_LENGTH; + public static final int KEY_VALUE_SHIFT = 4; + + public static final int HASH_OFFSET = 0; + public static final int HASH_LENGTH = Long.BYTES; + public static final int HASH_SHIFT = 3; + + /** + * Ignoring the power-of-two constraint, the maximum number of sectorSlots would be + *
+     *   sectorSlots =      HEADER_POSITION_MASK / (KEY_LENGTH + VALUE_LENGTH + HASH_LENGTH)
+     *         = 9,223,372,036,854,775,800 / 24
+     *         =   384,307,168,202,282,325
+     * 
+ * The next smaller power of two is 2^58 = 288,230,376,151,711,744 + */ + public static final int MAX_SLOTS_BITS = 58; + + /** + * Maximum number of sectorSlots is 2^58 = 288,230,376,151,711,744. + *

+ * For calculation details see {@link #MAX_SLOTS_BITS} + */ + public static final long MAX_SLOTS = 1L< + * As have to store key, value and hash we need at least 3x the minimum region size. + * This translates to 512 x 3 x 8 = 12K bytes, which means we have 2^9 = 512 sectorSlots. + */ + public static final int MIN_SLOTS_BITS = 9; + + /** + * Minimum number of sectorSlots is 2^9 = 512. + *

+ * For calculation details see {@link #MIN_SLOTS_BITS} + */ + public static final long MIN_SLOTS = 1L << MIN_SLOTS_BITS; + + /** + * Minimum file size for sector data file is MIN_SLOTS * 3 * 8 = 12K bytes. + *

+ * For calculation details see {@link #MIN_SLOTS_BITS} + */ + public static final long MIN_SECTOR_SIZE = (MIN_SLOTS << KEY_VALUE_SHIFT) + (MIN_SLOTS << HASH_SHIFT); + + /** + * Minimum file size for sector data file is MAX_SLOTS * 3 * 8, which results in the astronomical number of + * 6,917,529,027,641,081,856 bytes, or almost 7 million terabytes. + *

+ * For calculation details see {@link #MAX_SLOTS_BITS}. + */ + public static final long MAX_SECTOR_SIZE = (MAX_SLOTS << KEY_VALUE_SHIFT) + (MAX_SLOTS << HASH_SHIFT); + + /** + * Maximum number of sectors is 50. + */ + public static final int MAX_SECTORS = MAX_SLOTS_BITS - MIN_SLOTS_BITS + 1; + + static long length(final long sectorSlots) { + return (sectorSlots << KEY_VALUE_SHIFT) + (sectorSlots << HASH_SHIFT); + } + + static long keyValueOffset(final long slot) { + return slot << KEY_VALUE_SHIFT; + } + + static long hashOffset(final long sectorSlots, final long slot) { + assert slot < sectorSlots : "slot exceeds sectorSlots"; + return (sectorSlots << KEY_VALUE_SHIFT) + (slot << HASH_SHIFT); + } + + static long slot(final long hash, final long sectorSlots) { + assert BitUtil.isPowerOfTwo(sectorSlots) : "sectorSlots must be a power of two"; + return hash & (sectorSlots - 1); + } + + static long moveToAndGetKeyHeaderVolatile(final OffsetMapping mapping, final long startPosition, final long slot) { + final long position = startPosition + keyValueOffset(slot) + KEY_OFFSET; + return moveToAndGetVolatile(mapping, position); + } + + static long moveToAndGetValueHeaderVolatile(final OffsetMapping mapping, final long startPosition, final long slot) { + final long position = startPosition + keyValueOffset(slot) + VALUE_OFFSET; + return moveToAndGetVolatile(mapping, position); + } + + static long moveToAndGetHash(final OffsetMapping mapping, + final long startPosition, + final long sectorSlots, + final long slot) { + final long position = startPosition + hashOffset(sectorSlots, slot) + HASH_OFFSET; + if (mapping.moveTo(position)) { + return mapping.buffer().getLong(0, LITTLE_ENDIAN); + } + throw mappingMoveException(NAME, position); + } + + private static long moveToAndGetVolatile(final OffsetMapping mapping, final long position) { + if (mapping.moveTo(position)) { + return mapping.buffer().getLongVolatile(0); + } + throw mappingMoveException(NAME, position); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/SectorFlyweight.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/SectorFlyweight.java new file mode 100644 index 0000000..faff450 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/SectorFlyweight.java @@ -0,0 +1,127 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.concurrent.AtomicBuffer; +import org.tools4j.mmap.region.api.Mapping; +import org.tools4j.mmap.region.api.OffsetMapping; + +import static java.util.Objects.requireNonNull; + +public class SectorFlyweight { + private Mapping indexMapping; + private OffsetMapping headerMapping; + + private int sector; + private long startPosition; + private int slotsInBits; + private long slots; + + public void wrapFirstSector(final Mapping indexMapping, final OffsetMapping headerMapping) { + final int sector = IndexDescriptor.firstSectorIndexVolatile(indexMapping.buffer()); + wrapSector(indexMapping, headerMapping, sector); + } + + public void wrapSector(final Mapping indexMapping, final OffsetMapping headerMapping, final int sector) { + this.indexMapping = requireNonNull(indexMapping); + this.headerMapping = requireNonNull(headerMapping); + this.startPosition = IndexDescriptor.sectorStartPosition(indexMapping.buffer(), sector); + this.slotsInBits = IndexDescriptor.sectorSlotsInBits(indexMapping.buffer(), sector); + this.slots = 1L << slotsInBits; + } + + public int sector() { + return sector; + } + + public int nextSectorVolatile() { + return IndexDescriptor.nextSectorVolatile(indexMapping.buffer(), sector); + } + + public int nextSectorInitAndGet(final boolean performChecks) { + final AtomicBuffer buffer = indexMapping.buffer(); + final int nextSector = sector + 1; + final int nextSlotsInBits = slotsInBits + 1; + final long nextStartPosition = startPosition + SectorDescriptor.length(slots); + if (IndexDescriptor.sectorInit(buffer, nextSector, nextStartPosition, nextSlotsInBits, performChecks)) { + return nextSector; + } + return nextSectorVolatile(); + } + + public int slotsInBits() { + return slotsInBits; + } + + public long slots() { + return slots; + } + + public long slot(final long hash) { + return SectorDescriptor.slot(hash, slots); + } + + public long moveToAndGetKeyHeaderVolatile(final long slot) { + return SectorDescriptor.moveToAndGetKeyHeaderVolatile(headerMapping, startPosition, slot); + } + + public long moveToAndGetValueHeaderVolatile(final long slot) { + return SectorDescriptor.moveToAndGetValueHeaderVolatile(headerMapping, startPosition, slot); + } + + public long moveToAndGetHash(final long slot) { + return SectorDescriptor.moveToAndGetHash(headerMapping, startPosition, slots, slot); + } + + public long activeSlotsVolatile() { + return IndexDescriptor.sectorActiveSlotsVolatile(indexMapping.buffer(), sector); + } + + public long activeSlotsIncrementAndGet() { + return IndexDescriptor.sectorActiveSlotsIncrementAndGet(indexMapping.buffer(), sector); + } + + public long usedSlotsVolatile() { + return IndexDescriptor.sectorUsedSlotsVolatile(indexMapping.buffer(), sector); + } + + public long usedSlotsIncrementAndGet() { + return IndexDescriptor.sectorUsedSlotsIncrementAndGet(indexMapping.buffer(), sector); + } + + public long usedSlotsDecrementAndGet() { + return IndexDescriptor.sectorUsedSlotsDecrementAndGet(indexMapping.buffer(), sector); + } + + @Override + public String toString() { + return "SectorFlyweight" + + ":sector=" + sector + + "|startPosition=" + startPosition + + "|slotsInBits=" + slotsInBits + + "|slots=" + slots + + "|activeSlots=" + activeSlotsVolatile() + + "|usedSlots=" + usedSlotsVolatile(); + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/UpdaterImpl.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/UpdaterImpl.java new file mode 100644 index 0000000..3ddd6aa --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/UpdaterImpl.java @@ -0,0 +1,573 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +///* +// * The MIT License (MIT) +// * +// * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) +// * +// * Permission is hereby granted, free of charge, to any person obtaining a copy +// * of this software and associated documentation files (the "Software"), to deal +// * in the Software without restriction, including without limitation the rights +// * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// * copies of the Software, and to permit persons to whom the Software is +// * furnished to do so, subject to the following conditions: +// * +// * The above copyright notice and this permission notice shall be included in all +// * copies or substantial portions of the Software. +// * +// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// * SOFTWARE. +// */ +//package org.tools4j.mmap.dictionary.impl; +// +//import org.agrona.DirectBuffer; +//import org.agrona.MutableDirectBuffer; +//import org.agrona.concurrent.AtomicBuffer; +//import org.agrona.concurrent.UnsafeBuffer; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.tools4j.mmap.dictionary.api.DeletePredicate; +//import org.tools4j.mmap.dictionary.api.DeletingContext; +//import org.tools4j.mmap.dictionary.api.UpdatePredicate; +//import org.tools4j.mmap.dictionary.api.Updater; +//import org.tools4j.mmap.dictionary.api.UpdatingContext; +//import org.tools4j.mmap.dictionary.api.UpdatingContext.Key; +//import org.tools4j.mmap.dictionary.api.UpdatingContext.Value; +//import org.tools4j.mmap.region.api.OffsetMapping; +//import org.tools4j.mmap.region.impl.EmptyBuffer; +// +//import java.nio.ByteBuffer; +// +//import static java.util.Objects.requireNonNull; +//import static org.tools4j.mmap.dictionary.impl.Headers.NULL_HEADER; +// +//final class UpdaterImpl implements Updater { +// private static final Logger LOGGER = LoggerFactory.getLogger(UpdaterImpl.class); +// +// private final String dictionaryName; +// private final UpdaterMappings mappings; +// private final int updaterId; +// private final OffsetMapping header; +// private final OffsetMapping payload; +// private final boolean enableCopyFromPreviousRegion; +// private final UpdatingContextImpl updContext; +// private final DeletingContextImpl delContext; +// private boolean closed; +// +// public UpdaterImpl(final String dictionaryName, +// final UpdaterMappings mappings, +// final boolean enableCopyFromPreviousRegion) { +// this.dictionaryName = requireNonNull(dictionaryName); +// this.mappings = requireNonNull(mappings); +// this.updaterId = mappings.updaterId(); +// this.header = requireNonNull(mappings.header()); +// this.payload = requireNonNull(mappings.payload()); +// this.enableCopyFromPreviousRegion = enableCopyFromPreviousRegion; +// this.updContext = new UpdatingContextImpl(this); +// } +// +// private int maxKeyLength() { +// //regionSize - hash - keyLen +// return payload.regionSize() - Long.BYTES - Integer.BYTES; +// } +// +// private int maxValueLength() { +// //regionSize - valueLen +// return payload.regionSize() - Integer.BYTES; +// } +// +// @Override +// public void put(final DirectBuffer key, final DirectBuffer value) { +// put(key, value, null, (valCtxt, valLen, xtra) -> valCtxt.put(valLen)) +// .close(); +// } +// +// @Override +// public UpdatingContext.Result putIfAbsent(final DirectBuffer key, final DirectBuffer value) { +// return put(key, value, null, (valCtxt, valLen, xtra) -> valCtxt.putIfAbsent(valLen)); +// } +// +// @Override +// public UpdatingContext.Result putIfMatching(final DirectBuffer key, final DirectBuffer value, final UpdatePredicate condition) { +// return put(key, value, condition, UpdatingContext.Value::putIfMatching); +// } +// +// @FunctionalInterface +// private interface PutOperation { +// UpdatingContext.Result put(UpdatingContext.Value valueContext, int valueLength, X extra); +// } +// +// private UpdatingContext.Result put(final DirectBuffer key, +// final DirectBuffer value, +// final X extra, +// final PutOperation operation) { +// final int valLen = value.capacity(); +// try (final UpdatingContext.Value valueContext = updating(key, valLen)) { +// valueContext.valueBuffer().putBytes(0, value, 0, valLen); +// return operation.put(valueContext, valLen, extra); +// } +// } +// +// @Override +// public DeletingContext.Result delete(final DirectBuffer key) { +// return delete(key, null, (keyContext, keyLength, extra) -> keyContext.delete(keyLength)); +// } +// +// @Override +// public DeletingContext.Result deleteIfMatching(final DirectBuffer key, final DeletePredicate condition) { +// return delete(key, condition, DeletingContext.Key::deleteIfMatching); +// } +// +// @FunctionalInterface +// private interface DeleteOperation { +// DeletingContext.Result delete(DeletingContext.Key keyContext, X extra); +// } +// +// private DeletingContext.Result delete(final DirectBuffer key, +// final X extra, +// final DeleteOperation operation) { +// checkNotClosed(); +// try (final DeletingContext.Key keyContext = delContext.init(key, 0, key.capacity())) { +// return operation.delete(keyContext, extra); +// } +// } +// +// @Override +// public Value updating(final DirectBuffer key, final int valueCapacity) { +// checkNotClosed(); +// return updContext.init(key, valueCapacity); +// } +// +// @Override +// public Key updating(final int keyCapacity, final int valueCapacity) { +// checkNotClosed(); +// return updContext.init(keyCapacity, valueCapacity); +// } +// +// @Override +// public DeletingContext.Key deleting() { +// return deletingContext.init(); +// } +// +// private long appendEntry(final long payloadPosition, final int payloadLength) { +// checkNotClosed(); +// final OffsetMapping hdr = header; +// final AtomicBuffer buf = hdr.buffer(); +// final long headerValue = Headers.header(updaterId, payloadPosition); +// long index = endIndex; +// checkIndexNotExceedingMax(index); +// while (!buf.compareAndSetLong(0, NULL_HEADER, headerValue)) { +// do { +// index++; +// endIndex = index; +// checkIndexNotExceedingMax(index); +// if (!Headers.moveToHeaderIndex(hdr, index)) { +// throw headerMoveException(this, Headers.headerPositionForIndex(index)); +// } +// } while (buf.getLongVolatile(0) != NULL_HEADER); +// } +// final long nextIndex = index + 1; +// endIndex = nextIndex;//NOTE: may exceed MAX, but we check when appending (see above) +// if (nextIndex <= Index.MAX) { +// if (!Headers.moveToHeaderIndex(hdr, nextIndex)) { +// throw headerMoveException(this, Headers.headerPositionForIndex(index)); +// } +// } +// lastOwnHeader = headerValue; +// lastOwnPayloadLength = payloadLength; +// return index; +// } +// +// private void checkNotClosed() { +// if (isClosed()) { +// throw new IllegalStateException("Updater " + updaterName() + " is closed"); +// } +// } +// +// @Override +// public boolean isClosed() { +// return closed; +// } +// +// @Override +// public void close() { +// if (!isClosed()) { +// closed = true; +// mappings.close(); +// LOGGER.info("Updater closed: {}", updaterName()); +// } +// } +// +// private static final class UpdatingContextImpl implements UpdatingContext.Key, UpdatingContext.Value { +// final UpdaterImpl updater; +// final OffsetMapping payload; +// final MutableDirectBuffer buffer = new UnsafeBuffer(0, 0); +// int maxKeyLength = -1; +// int maxValueLength = -1; +// +// UpdatingContextImpl(final UpdaterImpl updater) { +// this.updater = requireNonNull(updater); +// this.payload = requireNonNull(updater.payload); +// } +// +// private void validateKeyCapacity(final int capacity) { +// if (capacity > updater.maxKeyLength()) { +// throw new IllegalArgumentException("Capacity " + capacity + " exceeds maximum allowed key size " + +// updater.maxKeyLength()); +// } +// } +// private void validateValueCapacity(final int capacity) { +// if (capacity > updater.maxValueLength()) { +// throw new IllegalArgumentException("Capacity " + capacity + " exceeds maximum allowed value size " + +// updater.maxValueLength()); +// } +// } +// +// UpdatingContext init(final int keyCapacity, final int valueCapacity) { +// if (!isClosed()) { +// abort(); +// throw new IllegalStateException("Updating context has not been closed"); +// } +// validateKeyCapacity(keyCapacity); +// validateValueCapacity(valueCapacity); +// this.maxKeyLength = initPayloadBuffer(updater.lastOwnHeader, updater.lastOwnPayloadLength, Math.max(0, keyCapacity)); +// return this; +// } +// +// private int initPayloadBuffer(final long lastOwnHeader, final int lastOwnPayloadLen, final int capacity) { +// assert capacity >= 0 && capacity <= maxCapacity; +// final OffsetMapping pld = payload; +// final int minRequired = capacity + Integer.BYTES; +// final long payloadPosition = lastOwnHeader == NULL_HEADER ? 0L : +// Headers.nextPayloadPosition(Headers.payloadPosition(lastOwnHeader), lastOwnPayloadLen); +// if (!pld.moveTo(payloadPosition)) { +// throw payloadMoveException(updater, payloadPosition); +// } +// if (pld.bytesAvailable() < minRequired) { +// moveToNextPayloadRegion(pld); +// } +// if (pld.position() > MAX_PAYLOAD_POSITION) { +// throw payloadPositionExceedsMaxException(updater, payloadPosition); +// } +// buffer.wrap(pld.buffer(), Integer.BYTES, capacity); +// return capacity; +// } +// +// private void moveToNextPayloadRegion(final OffsetMapping payload) { +// if (!payload.moveToNextRegion()) { +// final long regionStartPosition = payload.regionStartPosition() + payload.regionSize(); +// throw payloadMoveException(updater, regionStartPosition); +// } +// } +// +// @Override +// public void ensureCapacity(final int capacity) { +// final int max = maxLength; +// if (max < 0) { +// throw new IllegalStateException("Appending context is closed"); +// } +// if (capacity <= max) { +// return; +// } +// validateCapacity(capacity); +// final int minRequired = capacity + Integer.BYTES; +// final OffsetMapping pld = payload; +// if (pld.bytesAvailable() < minRequired) { +// if (!updater.enableCopyFromPreviousRegion) { +// throw new IllegalStateException("Need to enable payload region cache (for async no less than map-ahead + 2) to fully support ensureCapacity(..)"); +// } +// moveToNextPayloadRegion(pld); +// //NOTE: copy data from buffer to the mapping buffer +// // --> the buffer is still wrapped at old region address +// // --> old region address is still mapped because a region cache is in use +// pld.buffer().getBytes(Integer.BYTES, buffer, 0, max); +// } +// buffer.wrap(pld.buffer(), Integer.BYTES, capacity); +// maxLength = capacity; +// } +// +// @Override +// public MutableDirectBuffer buffer() { +// return buffer; +// } +// +// @Override +// public void abort() { +// this.maxLength = -1; +// } +// +// @Override +// public long commit(final int length) { +// final int max = maxLength; +// maxLength = -1; +// buffer.wrap(0, 0); +// validateLength(length, max); +// final OffsetMapping pld = payload; +// pld.buffer().putInt(0, length); +// return updater.appendEntry(pld.position(), length + Integer.BYTES); +// } +// +// static void validateLength(final int length, final int maxLength) { +// if (length < 0) { +// throw new IllegalArgumentException("Length cannot be negative: " + length); +// } else if (length > maxLength) { +// if (maxLength >= 0) { +// throw new IllegalArgumentException("Length " + length + " exceeds max length " + maxLength); +// } +// throw new IllegalStateException("Appending context is closed"); +// } +// } +// +// @Override +// public boolean isClosed() { +// return maxLength < 0; +// } +// +// @Override +// public String toString() { +// return "UpdatingContextImpl" + +// ":dictionary=" + updater.dictionaryName + +// "|updaterId=" + updater.updaterId + +// "|closed=" + isClosed(); +// } +// } +// +// private static final class DeletingContextImpl implements DeletingContext.Key { +// final UpdaterImpl updater; +// final DeleteResult result; +// final MutableDirectBuffer defaultBuffer; +// final MutableDirectBuffer wrapBuffer = new UnsafeBuffer(0, 0); +// MutableDirectBuffer currentBuffer; +// +// DeletingContextImpl(final UpdaterImpl updater) { +// this.updater = requireNonNull(updater); +// this.result = new DeleteResult(updater); +// this.defaultBuffer = new UnsafeBuffer(ByteBuffer.allocateDirect(updater.maxKeyLength())); +// } +// +// DeletingContextImpl init() { +// result.close(); +// return init(defaultBuffer); +// } +// +// DeletingContextImpl init(final MutableDirectBuffer key, final int offset, final int keyLength) { +// if (keyLength > updater.maxKeyLength()) { +// throw new IllegalArgumentException("Key length " + keyLength + " exceeds max allowed key length " + +// updater.maxKeyLength()); +// } +// result.close(); +// wrapBuffer.wrap(key, offset, keyLength); +// return init(wrapBuffer); +// } +// +// private DeletingContextImpl init(final MutableDirectBuffer keyBuffer) { +// if (!isClosed()) { +// abort(); +// throw new IllegalStateException("Deleting context has not been closed"); +// } +// this.currentBuffer = requireNonNull(keyBuffer); +// return this; +// } +// +// @Override +// public MutableDirectBuffer keyBuffer() { +// return currentBuffer != null ? currentBuffer : wrapBuffer; +// } +// +// @Override +// public void abort() { +// final DirectBuffer buf = currentBuffer; +// if (buf != null) { +// if (buf == wrapBuffer) { +// wrapBuffer.wrap(0, 0); +// } +// this.currentBuffer = null; +// } +// } +// +// @Override +// public Result delete(final int keyLength) { +// return deleteIfMatching(keyLength, null); +// } +// +// @Override +// public Result deleteIfMatching(final int keyLength, final DeletePredicate condition) { +// if (isClosed()) { +// throw new IllegalStateException("DeleteContext is closed"); +// } +// DirectBuffer keyBuf, wrapBuf; +// if ((keyBuf = currentBuffer) != (wrapBuf = wrapBuffer)) { +// wrapBuf.wrap(keyBuf, 0, keyLength); +// keyBuf = wrapBuf; +// } else { +// assert keyBuf.capacity() == keyLength : "invalid keyLength"; +// } +// currentBuffer = null; +// final long index = updater.findIndexForKey(keyBuf); +// if (index < 0) { +// return result.keyNotFound(keyBuf); +// } +// final OffsetMapping hdr = updater.header; +// final long keyHeader = Headers.moveAndGetKeyHeader(hdr, index); +// final long valHeader = Headers.moveAndGetValueHeader(hdr, index); +// if (valHeader ) { +// final long valueHeader = +// } +// return null; +// } +// +// @Override +// public long commit(final int length) { +// final int max = maxLength; +// maxLength = -1; +// buffer.wrap(0, 0); +// validateLength(length, max); +// final OffsetMapping pld = payload; +// pld.buffer().putInt(0, length); +// return updater.appendEntry(pld.position(), length + Integer.BYTES); +// } +// +// static void validateLength(final int length, final int maxLength) { +// if (length < 0) { +// throw new IllegalArgumentException("Length cannot be negative: " + length); +// } else if (length > maxLength) { +// if (maxLength >= 0) { +// throw new IllegalArgumentException("Length " + length + " exceeds max length " + maxLength); +// } +// throw new IllegalStateException("Appending context is closed"); +// } +// } +// +// @Override +// public boolean isClosed() { +// return currentBuffer == null; +// } +// +// @Override +// public String toString() { +// return "DeletingContextImpl" + +// ":dictionary=" + updater.dictionaryName + +// "|updaterId=" + updater.updaterId + +// "|closed=" + isClosed(); +// } +// +// static final class DeleteResult implements DeletingContext.Result { +// final UpdaterImpl updater; +// final DirectBuffer keyWrapper = new UnsafeBuffer(0, 0); +// final DirectBuffer valueWrapper = new UnsafeBuffer(0, 0); +// DirectBuffer key; +// DirectBuffer value; +// boolean present; +// +// DeleteResult(final UpdaterImpl updater) { +// this.updater = updater; +// } +// +// DeleteResult keyNotFound(final DirectBuffer key) { +// return init(key, EmptyBuffer.INSTANCE, false); +// } +// +// DeleteResult keyFoundWithTombstoneValue(final long header) { +// return init(key, EmptyBuffer.INSTANCE, true); +// } +// +// DeleteResult keyFoundWithValue(final DirectBuffer key, final DirectBuffer) { +// return init(key, EmptyBuffer.INSTANCE, true); +// } +// +// DeleteResult init(final DirectBuffer key, final DirectBuffer value, final boolean present) { +// this.key = requireNonNull(key); +// this.value = requireNonNull(value); +// this.present = present; +// return this; +// } +// +// @Override +// public DirectBuffer key() { +// return key; +// } +// +// @Override +// public DirectBuffer value() { +// return value; +// } +// +// @Override +// public boolean isPresent() { +// return present; +// } +// +// @Override +// public boolean isDeleted() { +// return value != EmptyBuffer.INSTANCE; +// } +// +// @Override +// public boolean isClosed() { +// return key == EmptyBuffer.INSTANCE; +// } +// +// @Override +// public void close() { +// if (!isClosed()) { +// DirectBuffer buf; +// if ((buf = keyWrapper) == key) { +// buf.wrap(0, 0); +// } +// if ((buf = valueWrapper) == value) { +// buf.wrap(0, 0); +// } +// key = EmptyBuffer.INSTANCE; +// value = EmptyBuffer.INSTANCE; +// present = false; +// } +// } +// +// @Override +// public String toString() { +// return "DeleteResult" + +// ":dictionary=" + updater.dictionaryName + +// "|updaterId=" + updater.updaterId + +// "|present=" + present + +// "|deleted=" + isDeleted() + +// "|closed=" + isClosed(); +// } +// } +// } +// +// String updaterName() { +// return dictionaryName + ".updater-" + updaterId; +// } +// +// @Override +// public String toString() { +// return "UpdaterImpl:dictionary=" + dictionaryName + "|updaterId=" + updaterId + "|closed=" + closed; +// } +// +//} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/UpdaterMappings.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/UpdaterMappings.java new file mode 100644 index 0000000..91934f0 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/impl/UpdaterMappings.java @@ -0,0 +1,177 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.collections.Hashing; +import org.agrona.collections.Int2ObjectHashMap; +import org.tools4j.mmap.dictionary.config.DictionaryConfig; +import org.tools4j.mmap.dictionary.config.UpdaterConfig; +import org.tools4j.mmap.region.api.AccessMode; +import org.tools4j.mmap.region.api.Mapping; +import org.tools4j.mmap.region.api.Mappings; +import org.tools4j.mmap.region.api.OffsetMapping; +import org.tools4j.mmap.region.config.MappingConfig; +import org.tools4j.mmap.region.impl.IdPool; + +import java.util.function.IntFunction; + +import static java.util.Objects.requireNonNull; +import static org.tools4j.mmap.dictionary.impl.DictMappingConfigs.payloadMappingConfig; +import static org.tools4j.mmap.dictionary.impl.Headers.MAX_UPDATERS; +import static org.tools4j.mmap.dictionary.impl.SectorDescriptor.MAX_SECTORS; + +/** + * Mappings for dictionary updaters. + */ +interface UpdaterMappings extends AutoCloseable { + /** + * Returns the updater ID. + * @return the updater ID + */ + int updaterId(); + + /** + * Returns the read/write mapping with the sector index. + * @return mapping with sector index + */ + Mapping index(); + + /** + * Returns the read/write mapping with sector data containing keys and value headers and hashes. + * + * @param sector the sector, referencing an entry from the index file + * @return mapping with sector data + */ + OffsetMapping sector(int sector); + + /** + * Returns the read-only mapping with payload data containing key and value payload data owned by the updater + * specified by ID. + * + * @param updaterId the ID of the updater writing the given data + * @return mapping with key and value payload data + */ + OffsetMapping payload(int updaterId); + + /** + * Returns the read/write mapping with payload data containing key and value payload data of the updater specified + * by {@link #updaterId()}. + * + * @return mapping with key and value payload data + */ + OffsetMapping payload(); + + boolean isClosed(); + + @Override + void close(); + + /** + * Factory method for updater mappings. + * + * @param dictFiles the dictionary files + * @param updaterIdPool the updater ID pool + * @param dictionaryConfig the dictionary configuration settings + * @param updaterConfig configuration for updater mappings + * @return a new updater mappings instance + */ + static UpdaterMappings create(final DictFiles dictFiles, + final IdPool updaterIdPool, + final DictionaryConfig dictionaryConfig, + final UpdaterConfig updaterConfig) { + requireNonNull(dictFiles); + requireNonNull(dictionaryConfig); + requireNonNull(updaterIdPool); + final DictionaryConfig queueCfg = dictionaryConfig.toImmutableDictionaryConfig(); + final UpdaterConfig updaterCfg = updaterConfig.toImmutableUpdaterConfig(); + + return new UpdaterMappings() { + final int updaterId = updaterIdPool.acquire(); + final MappingConfig sectorCfg = DictMappingConfigs.sectorMappingConfig(queueCfg, updaterCfg); + final MappingConfig roPayloadCfg = payloadMappingConfig(queueCfg, updaterCfg, AccessMode.READ_ONLY); + final MappingConfig rwPayloadCfg = payloadMappingConfig(queueCfg, updaterCfg, AccessMode.READ_WRITE); + final Mapping index = Mappings.fixedSizeMapping(dictFiles.indexFile(), IndexDescriptor.FILE_SIZE, + AccessMode.READ_WRITE); + final Int2ObjectHashMap sectorMappings = new Int2ObjectHashMap<>(MAX_SECTORS, + Hashing.DEFAULT_LOAD_FACTOR); + final IntFunction sectorMappingFactory = sector -> Mappings.offsetMapping( + dictFiles.sectorFile(sector), AccessMode.READ_WRITE, sectorCfg); + + final Int2ObjectHashMap payloadMappings = new Int2ObjectHashMap<>(MAX_UPDATERS, + Hashing.DEFAULT_LOAD_FACTOR); + final IntFunction payloadMappingFactory = updaterId -> Mappings.offsetMapping( + dictFiles.sectorFile(updaterId), AccessMode.READ_ONLY, roPayloadCfg); + + final OffsetMapping payload = Mappings.offsetMapping(dictFiles.payloadFile(updaterId), + AccessMode.READ_WRITE, rwPayloadCfg); + + public int updaterId() { + return updaterId; + } + + @Override + public Mapping index() { + return index; + } + + @Override + public OffsetMapping sector(final int sector) { + return sectorMappings.computeIfAbsent(sector, sectorMappingFactory); + } + + @Override + public OffsetMapping payload(final int updaterId) { + return payloadMappings.computeIfAbsent(updaterId, payloadMappingFactory); + } + + @Override + public OffsetMapping payload() { + return payload; + } + + @Override + public boolean isClosed() { + return index.isClosed(); + } + + @Override + public void close() { + if (!isClosed()) { + index.close(); + payload.close(); + updaterIdPool.release(updaterId); + } + } + + @Override + public String toString() { + return "UpdaterMappings" + + ":dictionary=" + dictFiles.dictionaryName() + + "|updaterId=" + updaterId + + "|closed=" + isClosed(); + } + }; + } + +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/map/DictionaryMap.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/map/DictionaryMap.java new file mode 100644 index 0000000..86b8f9b --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/map/DictionaryMap.java @@ -0,0 +1,911 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.map; + +import org.agrona.DirectBuffer; +import org.agrona.ExpandableArrayBuffer; +import org.agrona.MutableDirectBuffer; +import org.tools4j.mmap.dictionary.api.DeletingContext; +import org.tools4j.mmap.dictionary.api.Dictionary; +import org.tools4j.mmap.dictionary.api.DictionaryIterator; +import org.tools4j.mmap.dictionary.api.IteratingContext; +import org.tools4j.mmap.dictionary.api.KeyValuePair; +import org.tools4j.mmap.dictionary.api.Lookup; +import org.tools4j.mmap.dictionary.api.LookupContext; +import org.tools4j.mmap.dictionary.api.UpdatePredicate; +import org.tools4j.mmap.dictionary.api.Updater; +import org.tools4j.mmap.dictionary.api.UpdatingContext; +import org.tools4j.mmap.dictionary.marshal.Marshaller; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +public class DictionaryMap implements ConcurrentMap, AutoCloseable { + + private final Dictionary dictionary; + private final Class keyType; + private final Marshaller keyMarshaller; + private final Marshaller valueMarshaller; + private final ThreadLocal updater; + private final ThreadLocal lookup; + private final ThreadLocal iterator; + + private transient KeySetView keySet; + private transient ValuesView values; + private transient EntrySetView entrySet; + + public DictionaryMap(final Dictionary dictionary, + final Class keyType, + final Marshaller keyMarshaller, + final Marshaller valueMarshaller) { + this.dictionary = requireNonNull(dictionary); + this.keyType = requireNonNull(keyType); + this.keyMarshaller = requireNonNull(keyMarshaller); + this.valueMarshaller = requireNonNull(valueMarshaller); + this.updater = ThreadLocal.withInitial(dictionary::createUpdater); + this.lookup = ThreadLocal.withInitial(dictionary::createLookup); + this.iterator = ThreadLocal.withInitial(dictionary::createIterator); + } + + @Override + public void close() { + dictionary.close(); + } + + public long sizeAsLong() { + return iterator.get().size(); + } + + @Override + public int size() { + final long size = sizeAsLong(); + return size <= Integer.MAX_VALUE ? (int)size : Integer.MAX_VALUE; + } + + @Override + public boolean isEmpty() { + return iterator.get().isEmpty(); + } + + @Override + public boolean containsKey(final Object key) { + if (!keyType.isInstance(key) && key != null) { + return false; + } + final K typedKey = keyType.cast(key); + try (final LookupContext.Key keyContext = lookup.get().getting()) { + final int len = keyMarshaller.marshal(typedKey, keyContext.keyBuffer()); + return keyContext.containsKey(len); + } + } + + @Override + public boolean containsValue(final Object value) { + try (final IteratingContext.Values values = iterator.get().iteratingValues()) { + for (final DirectBuffer valueBuffer : values) { + final V candidate = valueMarshaller.unmarshal(valueBuffer); + if (Objects.equals(value, candidate)) { + return true; + } + } + } + return false; + } + + @Override + public V get(final Object key) { + return getOrDefault(key, null); + } + + @Override + public V getOrDefault(final Object key, final V defaultValue) { + if (!keyType.isInstance(key) && key != null) { + return null; + } + final K typedKey = keyType.cast(key); + try (final LookupContext.Key keyContext = lookup.get().getting()) { + final int len = keyMarshaller.marshal(typedKey, keyContext.keyBuffer()); + final LookupContext.Result result = keyContext.lookup(len); + return result.isPresent() ? valueMarshaller.unmarshal(result.value()) : defaultValue; + } + } + + private T get(final K key, final BiFunction, ? super LookupContext.Result, T> result) { + try (final LookupContext.Key keyContext = lookup.get().getting()) { + final int len = keyMarshaller.marshal(key, keyContext.keyBuffer()); + final LookupContext.Result lookupResult = keyContext.lookup(len); + return result.apply(this, lookupResult); + } + } + + @Override + public V put(final K key, final V value) { + return put(key, value, (map, result) -> + result.hasOldValue() ? valueMarshaller.unmarshal(result.oldValue()) : null); + } + + private T put(final K key, + final V value, + final BiFunction, ? super UpdatingContext.Result, T> result) { + try (final UpdatingContext.Key keyContext = updater.get() + .updating(keyMarshaller.maxByteCapacity(), valueMarshaller.maxByteCapacity())) { + final int keyLen = keyMarshaller.marshal(key, keyContext.keyBuffer()); + try (final UpdatingContext.Value valueContext = keyContext.commitKey(keyLen)) { + final int valueLen = valueMarshaller.marshal(value, valueContext.valueBuffer()); + final UpdatingContext.Result updateResult = valueContext.put(valueLen); + return result.apply(this, updateResult); + } + } + } + + @Override + public V putIfAbsent(final K key, final V value) { + try (final UpdatingContext.Key keyContext = updater.get() + .updating(keyMarshaller.maxByteCapacity(), valueMarshaller.maxByteCapacity())) { + final int keyLen = keyMarshaller.marshal(key, keyContext.keyBuffer()); + try (final UpdatingContext.Value valueContext = keyContext.commitKey(keyLen)) { + final int valueLen = valueMarshaller.marshal(value, valueContext.valueBuffer()); + final UpdatingContext.Result result = valueContext.putIfAbsent(valueLen); + return result.isUpdated() ? value : valueMarshaller.unmarshal(result.value()); + } + } + } + + private T putIfMatching(final K key, + final V value, + final UpdatePredicate condition, + final BiFunction, ? super UpdatingContext.Result, T> result) { + try (final UpdatingContext.Key keyContext = updater.get() + .updating(keyMarshaller.maxByteCapacity(), valueMarshaller.maxByteCapacity())) { + final int keyLen = keyMarshaller.marshal(key, keyContext.keyBuffer()); + try (final UpdatingContext.Value valueContext = keyContext.commitKey(keyLen)) { + final int valueLen = valueMarshaller.marshal(value, valueContext.valueBuffer()); + return result.apply(this, valueContext.putIfMatching(valueLen, condition)); + } + } + } + + @Override + public V remove(final Object key) { + if (!keyType.isInstance(key) && key != null) { + return null; + } + return remove(keyType.cast(key), (map, result) -> + result.isDeleted() ? map.valueMarshaller.unmarshal(result.value()) : null); + } + + private T remove(final K key, + final BiFunction, ? super DeletingContext.Result, T> result) { + try (final DeletingContext.Key context = updater.get().deleting()) { + final int keyLen = keyMarshaller.marshal(key, context.keyBuffer()); + final DeletingContext.Result deleteResult = context.delete(keyLen); + return result.apply(this, deleteResult); + } + } + + @Override + public boolean remove(final Object key, final Object value) { + if (!keyType.isInstance(key) && key != null) { + return false; + } + final K typedKey = keyType.cast(key); + try (final DeletingContext.Key context = updater.get().deleting()) { + final int keyLen = keyMarshaller.marshal(typedKey, context.keyBuffer()); + final DeletingContext.Result result = context.deleteIfMatching(keyLen, keyValue -> { + final V current = valueMarshaller.unmarshal(keyValue.value()); + return Objects.equals(value, current); + }); + return result.isDeleted(); + } + } + + @Override + public V replace(final K key, final V value) { + return putIfMatching(key, value, (kvOld, kvNew) -> kvOld != null, (map, result) -> + result.hasOldValue() ? map.valueMarshaller.unmarshal(result.oldValue()) : null + ); + } + + @Override + public boolean replace(final K key, final V oldValue, final V newValue) { + return putIfMatching(key, newValue, (kvOld, kvNew) -> { + if (kvOld == null) { + return false; + } + final V old = valueMarshaller.unmarshal(kvOld.value()); + return Objects.equals(old, oldValue); + }, (map, result) -> result.isUpdated()); + } + + @Override + public void putAll(final Map map) { + for (final Entry entry : map.entrySet()) { + put(entry.getKey(), entry.getValue(), (dictMap, result) -> null); + } + } + + @Override + public void clear() { + try (final IteratingContext.Keys keys = iterator.get().iteratingKeys()) { + for (final DirectBuffer key : keys) { + updater.get().delete(key); + } + } + } + + @Override + public Set keySet() { + final KeySetView ks; + return (ks = keySet) != null ? ks : (keySet = new KeySetView<>(this)); + } + + @Override + public Collection values() { + final ValuesView vs; + return (vs = values) != null ? vs : (values = new ValuesView<>(this)); + } + + @Override + public Set> entrySet() { + final EntrySetView es; + return (es = entrySet) != null ? es : (entrySet = new EntrySetView<>(this)); + } + + @Override + public void forEach(final BiConsumer action) { + requireNonNull(action); + try (final IteratingContext.KeyValuePairs pairs = iterator.get().iteratingKeyValuePairs()) { + for (final KeyValuePair kv : pairs) { + final K key = keyMarshaller.unmarshal(kv.key()); + final V value = valueMarshaller.unmarshal(kv.value()); + action.accept(key, value); + } + } + } + + @Override + public void replaceAll(final BiFunction function) { + requireNonNull(function); + final int valueCapacity = valueMarshaller.maxByteCapacity(); + try (final IteratingContext.Keys keys = iterator.get().iteratingKeys()) { + for (final DirectBuffer keyBuf : keys) { + final K key = keyMarshaller.unmarshal(keyBuf); + boolean repeat; + do { + try (final UpdatingContext.Value context = updater.get().updating(keyBuf, valueCapacity)) { + final V oldValue = valueMarshaller.unmarshal(context.valueBuffer()); + final V newValue = function.apply(key, oldValue); + final int valueLen = valueMarshaller.marshal(newValue, context.valueBuffer()); + final UpdatingContext.Result result = context.putIfMatching(valueLen, (oldPair, newPair) -> { + if (oldPair == null) { + //has been deleted + return false; + } + final V curValue = valueMarshaller.unmarshal(oldPair.value()); + return Objects.equals(oldPair, curValue); + }); + repeat = result.isPresent() && !result.isUpdated(); + } + } while (repeat); + } + } + } + + @Override + public V computeIfAbsent(final K key, final Function mappingFunction) { + return ConcurrentMap.super.computeIfAbsent(key, mappingFunction); + } + + @Override + public V computeIfPresent(final K key, final BiFunction remappingFunction) { + return ConcurrentMap.super.computeIfPresent(key, remappingFunction); + } + + @Override + public V compute(final K key, final BiFunction remappingFunction) { + return ConcurrentMap.super.compute(key, remappingFunction); + } + + @Override + public V merge(final K key, final V value, final BiFunction remappingFunction) { + return ConcurrentMap.super.merge(key, value, remappingFunction); + } + + /* ----------------Views -------------- */ + + /** + * Base class for views. + */ + private abstract static class CollectionView implements Collection { + /** + * The largest possible (non-power of two) array size. + */ + static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + final DictionaryMap map; + CollectionView(final DictionaryMap map) { + this.map = map; + } + + /** + * Removes all elements from this view by removing all the mappings from the map + * backing this view. + */ + @Override + public final void clear() { map.clear(); } + @Override + public final int size() { return map.size(); } + @Override + public final boolean isEmpty() { return map.isEmpty(); } + + // implementations below rely on concrete classes supplying these + // abstract methods + /** + * Returns an iterator over the elements in this collection. + * + *

The returned iterator is weakly consistent. + * + * @return an iterator over the elements in this collection + */ + @Override + public abstract Iterator iterator(); + @Override + public abstract boolean contains(Object o); + @Override + public abstract boolean remove(Object o); + + private static final String oomeMsg = "Required array size too large"; + + @Override + public final Object[] toArray() { + long sz = map.sizeAsLong(); + if (sz > MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + int n = (int)sz; + Object[] r = new Object[n]; + int i = 0; + for (E e : this) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = e; + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + @SuppressWarnings("unchecked") + @Override + public final T[] toArray(final T[] a) { + long sz = map.sizeAsLong(); + if (sz > MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + int m = (int)sz; + T[] r = (a.length >= m) ? a : + (T[])java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), m); + int n = r.length; + int i = 0; + for (E e : this) { + if (i == n) { + if (n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = (T)e; + } + if (a == r && i < n) { + r[i] = null; // null-terminate + return r; + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + /** + * Returns a string representation of this collection. + * The string representation consists of the string representations + * of the collection's elements in the order they are returned by + * its iterator, enclosed in square brackets ({@code "[]"}). + * Adjacent elements are separated by the characters {@code ", "} + * (comma and space). Elements are converted to strings as by + * {@link String#valueOf(Object)}. + * + * @return a string representation of this collection + */ + @Override + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + Iterator it = iterator(); + if (it.hasNext()) { + for (;;) { + Object e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if (!it.hasNext()) + break; + sb.append(',').append(' '); + } + } + return sb.append(']').toString(); + } + + @Override + public final boolean containsAll(final Collection c) { + if (c != this) { + for (Object e : c) { + if (!contains(e)) { + return false; + } + } + } + return true; + } + + @Override + public final boolean removeAll(final Collection c) { + if (c == null) throw new NullPointerException(); + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + @Override + public final boolean retainAll(final Collection c) { + if (c == null) throw new NullPointerException(); + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if (!c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + } + + /** + * A view of a DictionaryMap as a {@link Set} of keys, in which additions are disabled. + * This class cannot be directly instantiated. See {@link #keySet() keySet()}. + */ + private static class KeySetView extends CollectionView implements Set { + KeySetView(final DictionaryMap map) { // non-public + super(map); + } + + @Override + public boolean contains(final Object o) { + //noinspection SuspiciousMethodCalls + return map.containsKey(o); + } + + /** + * Removes the key from this map view, by removing the key (and its + * corresponding value) from the backing map. This method does + * nothing if the key is not in the map. + * + * @param o the key to be removed from the backing map + * @return {@code true} if the backing map contained the specified key + */ + @Override + public boolean remove(final Object o) { + if (!map.keyType.isInstance(o) && o != null) { + return false; + } + return map.remove(map.keyType.cast(o), (map, result) -> result.isDeleted()); + } + + /** + * @return an iterator over the keys of the backing map + */ + @Override + public Iterator iterator() { + return new KeyIterator<>(map); + } + + @Override + public boolean add(final K K) { + throw new UnsupportedOperationException(); + } + @Override + public boolean addAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + int h = 0; + for (final K k : this) { + h += Objects.hashCode(k); + } + return h; + } + + @Override + public boolean equals(final Object o) { + Set c; + //noinspection SuspiciousMethodCalls + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(this, Spliterator.CONCURRENT | Spliterator.DISTINCT); + } + + @Override + public void forEach(final Consumer action) { + requireNonNull(action); + try (final IteratingContext.KeyValuePairs pairs = map.iterator.get().iteratingKeyValuePairs()) { + for (final KeyValuePair kv : pairs) { + final K key = map.keyMarshaller.unmarshal(kv.key()); + action.accept(key); + } + } + } + } + + /** + * A view of a DictionaryMap as a {@link Collection} of values, in which additions are disabled. + * This class cannot be directly instantiated. See {@link #values()}. + */ + static final class ValuesView extends CollectionView implements Collection { + ValuesView(final DictionaryMap map) { + super(map); + } + + @Override + public boolean contains(final Object o) { + //noinspection SuspiciousMethodCalls + return map.containsValue(o); + } + + @Override + public boolean remove(final Object o) { + for (final Iterator it = iterator(); it.hasNext();) { + if (Objects.equals(o, it.next())) { + it.remove(); + return true; + } + } + return false; + } + + @Override + public Iterator iterator() { + return new ValueIterator<>(map); + } + + @Override + public boolean add(final V e) { + throw new UnsupportedOperationException(); + } + @Override + public boolean addAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(this, Spliterator.CONCURRENT); + } + + @Override + public void forEach(final Consumer action) { + requireNonNull(action); + try (final IteratingContext.KeyValuePairs pairs = map.iterator.get().iteratingKeyValuePairs()) { + for (final KeyValuePair kv : pairs) { + final V value = map.valueMarshaller.unmarshal(kv.value()); + action.accept(value); + } + } + } + } + + /** + * A view of a DictionaryMap as a {@link Set} of (key, value) + * entries. This class cannot be directly instantiated. See + * {@link #entrySet()}. + */ + private static final class EntrySetView extends CollectionView> implements Set> { + EntrySetView(final DictionaryMap map) { + super(map); + } + + @Override + public boolean contains(final Object o) { + if (o instanceof Map.Entry) { + final Map.Entry e = (Map.Entry) o; + final Object key = e.getKey(); + if (!map.keyType.isInstance(key) && key != null) { + return false; + } + final Object value = e.getValue(); + final Object mapped = value != Boolean.FALSE ? + map.get(map.keyType.cast(key), (map, result) -> result.isPresent() ? + map.valueMarshaller.unmarshal(result.value()) : Boolean.FALSE) + : + map.get(map.keyType.cast(key), (map, result) -> result.isPresent() ? + map.valueMarshaller.unmarshal(result.value()) : Boolean.TRUE); + return Objects.equals(value, mapped); + } + return false; + } + + @Override + public boolean remove(final Object o) { + if (o instanceof Map.Entry) { + final Map.Entry e = (Map.Entry) o; + //noinspection SuspiciousMethodCalls + return map.remove(e.getKey(), e.getValue()); + } + return false; + } + + /** + * @return an iterator over the entries of the backing map + */ + public Iterator> iterator() { + return new EntryIterator<>(map); + } + + @Override + public boolean add(final Entry e) { + return map.put(e.getKey(), e.getValue(), (map, result) -> result.isUpdated()); + } + + @Override + public boolean addAll(final Collection> c) { + boolean added = false; + for (final Entry e : c) { + if (add(e)) { + added = true; + } + } + return added; + } + + @Override + public int hashCode() { + int h = 0; + try (final IteratingContext.KeyValuePairs pairs = map.iterator.get().iteratingKeyValuePairs()) { + for (final KeyValuePair kv : pairs) { + final K key = map.keyMarshaller.unmarshal(kv.key()); + final V value = map.valueMarshaller.unmarshal(kv.value()); + h += MapEntry.hashCode(key, value); + } + } + return h; + } + + @Override + public boolean equals(final Object o) { + final Set c; + //noinspection SuspiciousMethodCalls + return ((o instanceof Set) && + ((c = (Set)o) == this || + (containsAll(c) && c.containsAll(this)))); + } + + public Spliterator> spliterator() { + return Spliterators.spliterator(this, Spliterator.CONCURRENT | Spliterator.DISTINCT); + } + + public void forEach(final Consumer> action) { + requireNonNull(action); + try (final IteratingContext.KeyValuePairs pairs = map.iterator.get().iteratingKeyValuePairs()) { + for (final KeyValuePair kv : pairs) { + final K key = map.keyMarshaller.unmarshal(kv.key()); + final V value = map.valueMarshaller.unmarshal(kv.value()); + action.accept(new MapEntry<>(map, key, value)); + } + } + } + } + + private abstract static class BaseIterator implements Iterator { + final DictionaryMap map; + DictionaryIterator dictionaryIterator; + IteratingContext.KeyValuePairs iterableContext; + Iterator iterator; + DirectBuffer lastReturnedKey; + + BaseIterator(final DictionaryMap map) { + this.map = requireNonNull(map); + this.dictionaryIterator = map.dictionary.createIterator(); + this.iterableContext = dictionaryIterator.iteratingKeyValuePairs(); + this.iterator = iterableContext.iterator(); + } + + @Override + public boolean hasNext() { + if (iterator != null) { + if (iterator.hasNext()) { + return true; + } + if (lastReturnedKey != null) { + //NOTE: buffers get unwrapped below, so we need to copy the key to still support removal + final MutableDirectBuffer copy = new ExpandableArrayBuffer(lastReturnedKey.capacity()); + copy.putBytes(0, lastReturnedKey, 0, lastReturnedKey.capacity()); + lastReturnedKey = copy; + } + iterableContext.close(); + iterableContext = null; + dictionaryIterator.close(); + dictionaryIterator = null; + iterator = null; + } + return false; + } + + @Override + public E next() { + if (iterator == null || !hasNext()) { + throw new NoSuchElementException(); + } + final KeyValuePair pair = iterator.next(); + lastReturnedKey = pair.key(); + return unmarshal(pair); + } + + @Override + public void remove() { + final DirectBuffer key = lastReturnedKey; + if (key == null) { + throw new IllegalStateException(); + } + lastReturnedKey = null; + map.updater.get().delete(key); + } + + abstract protected E unmarshal(final KeyValuePair pair); + } + + private static final class KeyIterator extends BaseIterator { + KeyIterator(final DictionaryMap map) { + super(map); + } + + @Override + protected K unmarshal(final KeyValuePair pair) { + return map.keyMarshaller.unmarshal(pair.key()); + } + } + + private static final class ValueIterator extends BaseIterator { + ValueIterator(final DictionaryMap map) { + super(map); + } + + @Override + protected V unmarshal(final KeyValuePair pair) { + return map.valueMarshaller.unmarshal(pair.value()); + } + } + + private static final class EntryIterator extends BaseIterator> { + EntryIterator(final DictionaryMap map) { + super(map); + } + + @Override + protected Entry unmarshal(final KeyValuePair pair) { + final DictionaryMap m = map; + return new MapEntry<>( + m, + m.keyMarshaller.unmarshal(pair.key()), + m.valueMarshaller.unmarshal(pair.value()) + ); + } + } + + private static final class MapEntry implements Map.Entry { + final DictionaryMap map; + final K key; + V val; + + MapEntry(final DictionaryMap map, final K key, final V val) { + this.map = requireNonNull(map); + this.key = key; + this.val = val; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return val; + } + + @Override + public int hashCode() { + return hashCode(key, val); + } + + static int hashCode(final Object key, final Object val) { + return Objects.hashCode(key) ^ Objects.hashCode(val); + } + + @Override + public String toString() { + return key + "=" + val; + } + + @Override + public boolean equals(final Object o) { + if (o instanceof Map.Entry) { + final Map.Entry e = (Map.Entry)o; + return Objects.equals(key, e.getKey()) && Objects.equals(val, e.getValue()); + } + return false; + } + + /** + * Sets our entry's value and writes through to the map. The + * value to return is somewhat arbitrary here. Since we do not + * necessarily track asynchronous changes, the most recent + * "previous" value could be different from what we return (or + * could even have been removed, in which case the put will + * re-establish). We do not and cannot guarantee more. + */ + public V setValue(final V value) { + final V v = val; + val = value; + map.put(key, value, (map, result) -> null); + return v; + } + } +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/DoubleMarshaller.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/DoubleMarshaller.java new file mode 100644 index 0000000..8d2ab2e --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/DoubleMarshaller.java @@ -0,0 +1,49 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.marshal; + +import org.agrona.DirectBuffer; +import org.agrona.MutableDirectBuffer; + +public interface DoubleMarshaller extends Marshaller { + @Override + default int maxByteCapacity() { + return Double.BYTES; + } + + @Override + default int marshal(final Double value, final MutableDirectBuffer buffer) { + marshal(value.doubleValue(), buffer); + return Double.BYTES; + } + + @Override + default Double unmarshal(final DirectBuffer buffer) { + return unmarshalAsDouble(buffer); + } + + void marshal(double value, MutableDirectBuffer buffer); + + double unmarshalAsDouble(DirectBuffer buffer); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/IntMarshaller.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/IntMarshaller.java new file mode 100644 index 0000000..adbf75d --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/IntMarshaller.java @@ -0,0 +1,49 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.marshal; + +import org.agrona.DirectBuffer; +import org.agrona.MutableDirectBuffer; + +public interface IntMarshaller extends Marshaller { + @Override + default int maxByteCapacity() { + return Integer.BYTES; + } + + @Override + default int marshal(final Integer value, final MutableDirectBuffer buffer) { + marshal(value.intValue(), buffer); + return Integer.BYTES; + } + + @Override + default Integer unmarshal(final DirectBuffer buffer) { + return unmarshalAsInt(buffer); + } + + void marshal(int value, MutableDirectBuffer buffer); + + int unmarshalAsInt(DirectBuffer buffer); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/LongMarshaller.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/LongMarshaller.java new file mode 100644 index 0000000..fbf4cec --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/LongMarshaller.java @@ -0,0 +1,48 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.marshal; + +import org.agrona.DirectBuffer; +import org.agrona.MutableDirectBuffer; + +public interface LongMarshaller extends Marshaller { + @Override + default int maxByteCapacity() { + return Long.BYTES; + } + + @Override + default int marshal(final Long value, final MutableDirectBuffer buffer) { + marshal(value.longValue(), buffer); + return Long.BYTES; + } + + @Override + default Long unmarshal(final DirectBuffer buffer) { + return unmarshalAsLong(buffer); + } + + void marshal(long value, MutableDirectBuffer buffer); + long unmarshalAsLong(DirectBuffer buffer); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/Marshaller.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/Marshaller.java new file mode 100644 index 0000000..34d89ce --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/Marshaller.java @@ -0,0 +1,33 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.marshal; + +import org.agrona.DirectBuffer; +import org.agrona.MutableDirectBuffer; + +public interface Marshaller { + int maxByteCapacity(); + int marshal(T value, MutableDirectBuffer buffer); + T unmarshal(DirectBuffer buffer); +} diff --git a/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/Marshallers.java b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/Marshallers.java new file mode 100644 index 0000000..762d6f1 --- /dev/null +++ b/mmap-dictionary/src/main/java/org/tools4j/mmap/dictionary/marshal/Marshallers.java @@ -0,0 +1,153 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.marshal; + +import org.agrona.DirectBuffer; +import org.agrona.MutableDirectBuffer; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static java.util.Objects.requireNonNull; + +public enum Marshallers { + ; + public static final IntMarshaller INT = new IntMarshaller() { + @Override + public void marshal(final int value, final MutableDirectBuffer buffer) { + buffer.putInt(0, value, LITTLE_ENDIAN); + } + + @Override + public int unmarshalAsInt(final DirectBuffer buffer) { + return buffer.getInt(0, LITTLE_ENDIAN); + } + + @Override + public String toString() { + return "INT"; + } + }; + + public static final LongMarshaller LONG = new LongMarshaller() { + @Override + public void marshal(final long value, final MutableDirectBuffer buffer) { + buffer.putLong(0, value, LITTLE_ENDIAN); + } + + @Override + public long unmarshalAsLong(final DirectBuffer buffer) { + return buffer.getLong(0, LITTLE_ENDIAN); + } + + @Override + public String toString() { + return "LONG"; + } + }; + public static final DoubleMarshaller DOUBLE = new DoubleMarshaller() { + @Override + public void marshal(final double value, final MutableDirectBuffer buffer) { + buffer.putDouble(0, value, LITTLE_ENDIAN); + } + + @Override + public double unmarshalAsDouble(final DirectBuffer buffer) { + return buffer.getDouble(0, LITTLE_ENDIAN); + } + + @Override + public String toString() { + return "DOUBLE"; + } + }; + + public static > Marshaller enumMarshaller(final Class enumClass) { + requireNonNull(enumClass); + return new Marshaller() { + final E[] constants = enumClass.getEnumConstants(); + final String name = "ENUM<" + enumClass.getSimpleName() + ">"; + @Override + public int maxByteCapacity() { + return Integer.BYTES; + } + + @Override + public int marshal(final E value, final MutableDirectBuffer buffer) { + final int ordinal = value == null ? -1 : value.ordinal(); + buffer.putInt(0, ordinal, LITTLE_ENDIAN); + return Integer.BYTES; + } + + @Override + public E unmarshal(final DirectBuffer buffer) { + final int ordinal = buffer.getInt(0, LITTLE_ENDIAN); + return ordinal >= 0 && ordinal <= constants.length ? constants[ordinal] : null; + } + + @Override + public String toString() { + return name; + } + }; + } + + public static Marshaller asciiStringMarshaller(final int maxBytes) { + if (maxBytes < 0) { + throw new IllegalArgumentException("Max bytes cannot be negative: " + maxBytes); + } + return new Marshaller() { + final String name = "ASCII<" + maxBytes + ">"; + @Override + public int maxByteCapacity() { + return maxBytes + Integer.BYTES; + } + + @Override + public int marshal(final String value, final MutableDirectBuffer buffer) { + if (value == null) { + buffer.putInt(0, -1, LITTLE_ENDIAN); + return Integer.BYTES; + } + final int len = value.length(); + if (len > maxBytes) { + throw new IllegalArgumentException("String length exceeds max of " + maxBytes + ": " + value); + } + buffer.putInt(0, len, LITTLE_ENDIAN); + buffer.putStringWithoutLengthAscii(Integer.BYTES, value); + return Integer.BYTES + len; + } + + @Override + public String unmarshal(final DirectBuffer buffer) { + final int len = buffer.getInt(0, LITTLE_ENDIAN); + return len >= 0 ? buffer.getStringWithoutLengthAscii(0, len) : null; + } + + @Override + public String toString() { + return name; + } + }; + } + +} diff --git a/mmap-dictionary/src/test/java/org/tools4j/mmap/dictionary/impl/HashTest.java b/mmap-dictionary/src/test/java/org/tools4j/mmap/dictionary/impl/HashTest.java new file mode 100644 index 0000000..74792e0 --- /dev/null +++ b/mmap-dictionary/src/test/java/org/tools4j/mmap/dictionary/impl/HashTest.java @@ -0,0 +1,2183 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 tools4j.org (Marco Terzer, Anton Anufriev) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.tools4j.mmap.dictionary.impl; + +import org.agrona.DirectBuffer; +import org.agrona.ExpandableArrayBuffer; +import org.agrona.MutableDirectBuffer; +import org.agrona.concurrent.UnsafeBuffer; +import org.junit.jupiter.api.Test; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Unit test for {@link Hash} + */ +class HashTest { + @SuppressWarnings("NumericOverflow") + @Test + public void xxHashCollisionTest() { + final MutableDirectBuffer sequence = new ExpandableArrayBuffer(128); + sequence.putLong(0, 1); + sequence.putLong(16, 42); + sequence.putLong(32, 2); + final long h1 = Hash.xxHash64(sequence, 0, 128); + + sequence.putLong(0, 1 + 0xBA79078168D4BAFL); + sequence.putLong(32, 2 + 0x9C90005B80000000L); + final long h2 = Hash.xxHash64(sequence, 0, 128); + assertEquals(h1, h2); + + sequence.putLong(0, 1 + 0xBA79078168D4BAFL * 2); + sequence.putLong(32, 2 + 0x9C90005B80000000L * 2); + + final long h3 = Hash.xxHash64(sequence, 0, 128); + assertEquals(h2, h3); + } + + @Test + public void xxHashWithoutSeeds() { + for (int i = 0; i < HASHES_OF_LOOPING_BYTES_WITHOUT_SEED.length; i++) { + xxHash(0, i, HASHES_OF_LOOPING_BYTES_WITHOUT_SEED); + } + } + + @Test + public void xxHashWithSeed() { + for (int i = 0; i < HASHES_OF_LOOPING_BYTES_WITH_SEED_42.length; i++) { + xxHash(42, i, HASHES_OF_LOOPING_BYTES_WITH_SEED_42); + } + } + + public void xxHash(final long seed, final int len, final long[] hashesOfLoopingBytes) { + final byte[] data = new byte[len]; + for (int j = 0; j < data.length; j++) { + data[j] = (byte) j; + } + xxHash(seed, data, hashesOfLoopingBytes[len]); + } + + private static void xxHash(final long seed, final byte[] data, final long expected) { + xxHash(seed, new UnsafeBuffer(data), expected); + xxHash(seed, ByteBuffer.allocate(data.length).put(data), expected); + xxHash(seed, ByteBuffer.allocateDirect(data.length).put(data), expected); + } + + private static void xxHash(final long seed, final ByteBuffer buffer, final long expected) { + xxHash(seed, new UnsafeBuffer(buffer), expected); + xxHash(seed, new UnsafeBuffer(buffer.order(ByteOrder.BIG_ENDIAN)), expected); + xxHash(seed, new UnsafeBuffer(buffer.order(ByteOrder.LITTLE_ENDIAN)), expected); + } + + private static void xxHash(final long seed, final DirectBuffer data, final long expected) { + assertEquals(expected, Hash.xxHash64(seed, data, 0, data.capacity())); + } + + /** + * Test data is output of the following program with xxHash implementation + * from https://github.com/Cyan4973/xxHash. + * + *

+     * #include "xxhash.c"
+     * #include 
+     * #include 
+     * int main()
+     * {
+     *     char* src = (char*) malloc(1024);
+     *     const int N = 1024;
+     *     for (int i = 0; i < N; i++) {
+     *         src[i] = (char) i;
+     *     }
+     *
+     *     printf("without seed\n");
+     *     for (int i = 0; i <= N; i++) {
+     *        printf("%lldL,\n", (long long) XXH64(src, i, 0));
+     *     }
+     *
+     *     printf("with seed 42\n");
+     *     for (int i = 0; i <= N; i++) {
+     *        printf("%lldL,\n", (long long) XXH64(src, i, 42));
+     *     }
+     * }
+     * 
+ */ + public static final long[] HASHES_OF_LOOPING_BYTES_WITHOUT_SEED = { + -1205034819632174695L, + -1642502924627794072L, + 5216751715308240086L, + -1889335612763511331L, + -13835840860730338L, + -2521325055659080948L, + 4867868962443297827L, + 1498682999415010002L, + -8626056615231480947L, + 7482827008138251355L, + -617731006306969209L, + 7289733825183505098L, + 4776896707697368229L, + 1428059224718910376L, + 6690813482653982021L, + -6248474067697161171L, + 4951407828574235127L, + 6198050452789369270L, + 5776283192552877204L, + -626480755095427154L, + -6637184445929957204L, + 8370873622748562952L, + -1705978583731280501L, + -7898818752540221055L, + -2516210193198301541L, + 8356900479849653862L, + -4413748141896466000L, + -6040072975510680789L, + 1451490609699316991L, + -7948005844616396060L, + 8567048088357095527L, + -4375578310507393311L, + -3749919242623962444L, + 888155921178136237L, + -228195649085979072L, + -521095004075279741L, + -2458702038214709156L, + -2792334161285995319L, + 7509323632532862410L, + 46046374822258777L, + -731200582691896855L, + 933917387460394992L, + 5623144551929396680L, + 6456984547425914359L, + -6398540474588876142L, + 1224372500617079775L, + -931727396974525131L, + 979677643219401656L, + -8078270932489049756L, + -92767506898879473L, + 2379112167176776082L, + 2065719310945572007L, + -4972682801816081667L, + -7346559332994187462L, + 4674729779638751546L, + 5844780159702313017L, + 925606237565008152L, + 8164325403643669774L, + 5124005065773312983L, + -4646462236086916483L, + 4733593776494364101L, + -6408850806317360L, + 7405089268865026700L, + -2131704682637193649L, + -592659849139514384L, + -4386868621773355429L, + -2216833672566288862L, + 4022619316305276641L, + -60464713570988944L, + 2416749694506796597L, + 3576590985110933976L, + 3368688771415645536L, + -357157638897078259L, + 3484358739758473117L, + 2078888409435083535L, + 8053093288416703076L, + -4934736471585554038L, + -7784370683223414061L, + -4109284735634941390L, + 5982490102027564625L, + -4991107002810882893L, + 8664747912276562373L, + 8536879438728327651L, + 2358675440174594061L, + 5352236919104495867L, + 6340852522718110192L, + 5075606340464035668L, + -6313168920073458239L, + -6428599582591385786L, + -7278654800402467208L, + -6630626099856243581L, + -7548742438664634646L, + 5514383762309532642L, + -5996126265702944431L, + 4011116741319319261L, + -7289240093981845088L, + 4975257207486779926L, + -3945500877932691916L, + 1973955144068521079L, + 3884425912161913184L, + 7692681977284421015L, + -1616730378439673826L, + 4799493270916844476L, + -6107310582897997679L, + 3643294092300179537L, + 5406040598516899149L, + -3032420409304067208L, + 5044227119457305622L, + 9165032773225506149L, + 7553488247682850248L, + 2247298339072845043L, + 7380491470304042584L, + -456791943260357427L, + -1906500292613319324L, + -4025157985304129897L, + 6167829983725509611L, + -8678196943431064825L, + -636391087313417831L, + 5757999497725839182L, + 8999325347316115948L, + -6042339776328081249L, + 7988836354190359013L, + 2818448030979902104L, + -8484201484113382447L, + -1140175406473847155L, + 3042776987426497381L, + 3147338037480432386L, + 5065714330193756569L, + 8827021486636772242L, + 838335823706922959L, + 481844220820054909L, + 5333474685474667077L, + -3722898251196013565L, + 7909417627390150381L, + 7116148225996109646L, + 7520381989775811302L, + 6045444672904719015L, + 169039646730338133L, + -2144629916252757106L, + -3752608501798118554L, + 8374704774878780935L, + -5830926781667225570L, + 3202139393110256022L, + 4400219135677717216L, + -5663710220155589201L, + -2589002340345751622L, + -8240133511464343390L, + -4036798392879835146L, + 501599054729008501L, + -4851415719238782188L, + 7565157933617774080L, + -6428091359957700043L, + 4081845077806300175L, + -9016659258880122392L, + 7811786097015457596L, + 1357606791019752376L, + 6522211979684949668L, + -3462397075047559451L, + 3075504459164148117L, + 3055992297861390732L, + -7230492327399411047L, + -1128103378253532506L, + 1834607408788151585L, + 7065978976369231860L, + 6566122632438908362L, + -3440855531356735824L, + 6271453770746181891L, + 413365468403580071L, + -8342682158827061522L, + -3713303136987568731L, + -8959326895824091541L, + -2793862582117663595L, + -184756427409317729L, + -7052502019782453427L, + 3666196071825438258L, + 170204095295428634L, + -1880693509859077843L, + 5179169206996749826L, + 2866097700453114958L, + 1859104195026275510L, + 3782323564639128125L, + -6485194456269981193L, + 6761934873296236857L, + 5764605515941066448L, + 597754945258033208L, + -4888986062036739232L, + -6490228233091577705L, + 3234089784845854336L, + -5506883591180767430L, + 1491493862343818933L, + 3232293217886687768L, + -4079803366160739972L, + 4884134040093556099L, + -7274733680156962461L, + 5265680254123454403L, + 1036855740788018258L, + 423439784169709263L, + -3627743032115866622L, + -6311378083791982305L, + -3058076915688265687L, + 5826550132901840796L, + 8049712006832885455L, + 1707844692241288946L, + -3293048440386932248L, + -2458638193238955307L, + 943059295184967928L, + 3899561579431348819L, + -1516862862245909493L, + 4448476568037673976L, + 8738531437146688925L, + -1033913449611929894L, + 733668166271378558L, + 438686375775205249L, + -4325889118346169305L, + -238178883117433622L, + -7972205050662019794L, + -1263398103237492853L, + -8333197763892905802L, + 7796341294364809534L, + -1381767618016537445L, + 2892579485651013970L, + -3376209887503828920L, + -8575120126045607817L, + -1609355362031172055L, + -386138918275547508L, + 4598874691849543747L, + -2961781601824749597L, + -3032925351997820092L, + -4256249198066449735L, + 6712291718681474012L, + -4281614253751277086L, + 3727487933918100016L, + -2744649548868700294L, + 8662377383917584333L, + -9154398439761221404L, + -6895275824272461794L, + 3394857180017540444L, + 2010825527298793302L, + 4894417464710366872L, + -6879244364314087051L, + 83677167865178033L, + -8258406393927169823L, + 5042126978317943321L, + 6485279223034053259L, + 4442956705009100620L, + 316801800427881731L, + 1381431847939703076L, + 5172932759041399062L, + -69656533526213521L, + -5302643413630076306L, + -3956089084400440856L, + 372087412941022771L, + 4711314482928419386L, + 3255220726505012060L, + 8917854303046844847L, + 1116214654602499731L, + 2282408585429094475L, + -9207590323584417562L, + 8881688165595519866L, + 1731908113181957442L, + 3847295165012256987L, + 4457829016858233661L, + 4944046822375522396L, + 3445091217248591320L, + -5055680960069278553L, + -399195423199498362L, + -8109174165388156886L, + 4967185977968814820L, + -5911973391056763118L, + 2239508324487797550L, + -954783563382788523L, + 8523699184200726144L, + 932575865292832326L, + -7491448407022023047L, + 1809887519026638446L, + -8610524715250756725L, + 6158809695983348998L, + 4948400960714316843L, + -4513370424175692831L, + -3955280856263842959L, + 6440233015885550592L, + 8756942107256956958L, + 7895095834297147376L, + 370033091003609904L, + 948078545203432448L, + -8523229038380945151L, + 100794871657160943L, + -2186420796072284323L, + -9221115378196347951L, + 8102537654803861332L, + 5857339063191690550L, + -4554257374958739421L, + 6607496554818971053L, + -778402196622557070L, + -3817535277727878318L, + 3564122000469288769L, + -44446230828995950L, + 1322708749649533240L, + 6150374672341998205L, + -3300275952549095391L, + 5700833512536085850L, + -8559358370491270937L, + 5434443260519512697L, + -8031025173259990945L, + 7117462129248544172L, + 5425177419943569451L, + -7215427371174054838L, + -5728669976971194528L, + -2096361446095323077L, + -4247416835972286805L, + 4912769047482466787L, + 7755341152739082452L, + 6797061233443658471L, + 4089361562209715474L, + 5830701413838808929L, + 5514515889578551370L, + 609334005368729318L, + 177310574483850759L, + -820431153866372784L, + 7188454041446661654L, + 7480194911613035473L, + 4564607884390103056L, + 888496928954372093L, + -5480535802290619117L, + 9100964700413324707L, + 510523132632789099L, + 8249362675875046694L, + 5340321809639671537L, + -4633081050124361874L, + -839915092967986193L, + -7377542419053401928L, + 1820485955145562839L, + 8517645770425584256L, + -1877318739474090786L, + 7674371564231889244L, + -3311130470964498678L, + -880090321525066135L, + -5670998531776225745L, + -8828737503035152589L, + -6029750416835830307L, + -6535608738168818581L, + -550872341393232043L, + 2831504667559924912L, + -4613341433216920241L, + 502960879991989691L, + 576723875877375776L, + -2575765564594953903L, + -4642144349520453953L, + 7939746291681241029L, + 6486356905694539404L, + -9086235573768687853L, + 5369903658359590823L, + 3199947475395774092L, + 8384948078622146995L, + -3365598033653273878L, + -2525526479099052030L, + 2648498634302427751L, + 3715448294999624219L, + -4734466095330028983L, + -8440427851760401644L, + -371198022355334589L, + 8864079431738600817L, + -4205600060099565684L, + 6617166152874298882L, + -6515522971156180292L, + 7254251246745292298L, + -420587237082849417L, + 1190495815435763349L, + -474540026828753709L, + -8150622114536376016L, + -5790621848044235275L, + -2780522220219318167L, + -2991155855957250848L, + 1692932912262846366L, + 8814949734565782733L, + -8746818869495012552L, + 7931250816026891600L, + -7434629709560596700L, + 4388261932396122996L, + 7154847153195510802L, + -2810154398655124882L, + 2601892684639182965L, + 7781574423676509607L, + -6647000723020388462L, + -8679132292226137672L, + -2447013202020963672L, + 3658855631326217196L, + 2176620921764007759L, + 3654402165357492705L, + 4511989090021652156L, + -3254638803798424003L, + 9050506214967102331L, + 922579360317805810L, + 609820949221381248L, + 5723875594772949290L, + 4637721466210023638L, + 6195303339320487374L, + -38202587086649325L, + -2142927092331878341L, + 5355751314914287101L, + -7170892783575760055L, + -7506612729078573199L, + 8645580445823695595L, + 3221950179890871958L, + 1638211443525398634L, + 7356718304253861777L, + -296260062751271549L, + -1790105985391377345L, + -7004118620405119098L, + 7056012094479909462L, + -7673357898031223798L, + -8929502135696203556L, + 7527161467311997998L, + 6182865571027510002L, + -2163310275402596869L, + 6285112477695252864L, + 3703909999924067987L, + 962491298117560533L, + 138936592567072793L, + 6094857527471100960L, + 5914305068838335718L, + -8896724991235492552L, + -2667562314507789198L, + -7456492499188304500L, + -3422709784851063201L, + -1511644999824238281L, + -7130158069449057322L, + 6243266426571961929L, + 2713895636371672711L, + 5765589573821453640L, + 2624585483746388367L, + 3933828437519859601L, + -5664404238108533781L, + 7086393398544811684L, + 1322058227068490376L, + -8232508114671021371L, + -5963804389649678229L, + -3318229976491806899L, + -6261789542948241754L, + 199130260709663583L, + 7521707465510595039L, + 507353862067534334L, + -7737968456769005928L, + -8964687882992257099L, + -7735003539801528311L, + 6989812739838460574L, + -6986289777499051441L, + 1881562796144865699L, + -6077719780113966592L, + -5427071388091979746L, + 1660707436425817310L, + -4338189980197421104L, + 5330934977599207307L, + 4461280425701571033L, + -7426107478263746863L, + 4258305289832328199L, + -8003283151332860979L, + -2500604212764835216L, + -8883941775298564436L, + -5059709834257638733L, + -4582947579039913741L, + 1371959565630689983L, + -1925163414161391371L, + -1180269729544278896L, + -6603171789097590304L, + 8985062706306079731L, + -3588748723254272836L, + -6052032019910018725L, + 6200960040430493088L, + 2146343936795524980L, + 7785948646708747443L, + 4524411768393719400L, + 749211414228926779L, + -163844243342465015L, + 1066801203344117463L, + -3687825939602944988L, + -4873811917429870500L, + -3765115783578949524L, + 3344884226049804020L, + -22793631121165636L, + -5636541624133159076L, + -6201449576244177151L, + -4533734412127714050L, + -2064657727206266594L, + -1325853623186040989L, + -2651306529045029511L, + 903264360879626406L, + 6082283797495873520L, + 6185446819995987847L, + -5727850940826115079L, + 8356646143516726527L, + -7705915341280821272L, + 9137633133909463406L, + 6613483969797411894L, + 8598514961735984460L, + 6805925079991408361L, + 6009403222422527608L, + 2216303622650116705L, + -3736062178532154638L, + -7139008962939637477L, + -1537711200058404375L, + 8896755073380580322L, + -6063426810787442347L, + -3472064301690015285L, + -4568131486464952371L, + -8141256104294687045L, + 5627435360893599536L, + 1136003802967708029L, + 2730027518034735037L, + 1985287040172139729L, + -3643431491383365431L, + -9042919736106376701L, + 8879968900590373568L, + 8504486139877409399L, + 5832665747670146536L, + 4202923651402292496L, + 1738511892080946286L, + 4512683881549777042L, + 9200194457599870145L, + -1948301178705617139L, + 8655715314401162523L, + 412698981651521600L, + -1479274044808688580L, + 2688302549664693359L, + -3059920027366623178L, + -4275753325231806565L, + -8321791698013769889L, + -3678119714812414102L, + -2500922551770832553L, + 9018541633115002061L, + 5713301371152396803L, + 4180584812840471799L, + 3062416401091271879L, + -8125716681035757962L, + -2076056159878596225L, + 8855540523533374738L, + 2402007906402689092L, + 2020584786288649542L, + 1707405964421070701L, + -3681994462249973122L, + -3982567775984742012L, + 7133200226358561844L, + -5270514263562558963L, + 9060760368219219429L, + -6967162372382490281L, + -9094664463528453384L, + -3968518633408880046L, + 8618660189330281694L, + -4668946581954397558L, + -8596433172676363407L, + -1264942061713169049L, + -5309493221793643795L, + -1099320768477039529L, + 8925041285873295227L, + -6809278181760513499L, + -7039439984223885585L, + 6188209901527865226L, + 1487353394192637059L, + 2402097349430126337L, + -3818359601525025681L, + 4123217079279439249L, + -1424515143377220376L, + 1742298536803356877L, + -2836832784751148874L, + -4838603242771410698L, + 2383745618623084414L, + -2790832243316548423L, + -1176683649587660160L, + 1862928178605117401L, + 5208694030074527671L, + 4339841406618876548L, + -7704801448691668472L, + 500068664415229033L, + -2111184635274274347L, + -1387769336519960517L, + -2368660677263980293L, + -4980481392402938776L, + -6856361166068680884L, + 1708658704968066797L, + -9013068514618931938L, + -2616479975851677179L, + 7121103440247327570L, + -7094192881960646061L, + -4042342930006488618L, + 5294323611741266775L, + 5235545113690922502L, + -2562011392475214878L, + -4613304566070234734L, + -3784386310583029381L, + -4526148219816534267L, + -8643470129031767968L, + -4573761335510927866L, + -8255399593563317902L, + -1925488377092111963L, + -1747797357090594237L, + 7292772921748919564L, + 3951718848780851600L, + 5339305877764077075L, + 7889570407201305102L, + -8935437555550449315L, + -1858205318388884024L, + 381779657795494278L, + -3769854251228686168L, + -7957724087073627355L, + 4349540075286824743L, + -2476434494603040708L, + -4506107235113109706L, + -7120863144673563848L, + -8534342596639587598L, + 2205658724629050493L, + 604438195864305027L, + 4530331938860561927L, + -2074141653226683751L, + -1114378227875974007L, + 3377301950002508302L, + 5369356700690664306L, + -1747063224581819445L, + -6320380781966280801L, + -2075443262555773155L, + 1028541493355576591L, + -4694402890123574860L, + -5250660999767019003L, + 3847087895315315136L, + -4448050214964317066L, + -4591316307978008151L, + 4894820902772635901L, + 3088847887353411593L, + -6699208183127463352L, + 4636731998354510780L, + 9095126525233209263L, + 4135373626035182291L, + 3835688804093949701L, + -3490782692819028324L, + -561453348486424761L, + -3329283619698366365L, + 3251154327320814221L, + -8006986328190314286L, + 5856651505286251345L, + -8871425101391073L, + 7806993676637210959L, + 7586479850833664643L, + -7091216108599847229L, + -3410137297792125447L, + -8251963871271100526L, + -8849730915506517177L, + 8400334327557485676L, + 1676125861848906502L, + -8480324002538122254L, + -1402216371589796114L, + 5951911012328622382L, + 8596811512609928773L, + -2266336480397111285L, + -8840962712683931463L, + 4301675602445909557L, + 1843369157327547440L, + 2169755460218905712L, + -1592865257954325910L, + -8763867324602133653L, + -4283855559993550994L, + -7577702976577664015L, + -5152834259238990784L, + 4596243922610406362L, + -4326545138850544854L, + 1480440096894990716L, + 8548031958586152418L, + 6705615952497668303L, + -2915454802887967935L, + -6137002913510169520L, + 2908515186908319288L, + 5834242853393037250L, + -6721431559266056630L, + -7810820823419696676L, + 1954209413716096740L, + 6657013078387802473L, + 2214178984740031680L, + 8789512881373922013L, + 1240231669311237626L, + 8694612319028097761L, + 492180561068515854L, + -6047127535609489112L, + 7436686740711762797L, + -4520261623507558716L, + 938282189116272147L, + 3232025564608101134L, + -5425498066931840551L, + 932123105892452494L, + 9054941090932531526L, + 8066693670021084601L, + 764877609198828864L, + -489112437588815338L, + 4827691353685521957L, + 1948321254606741278L, + 6117773063719937712L, + 4645962658121906639L, + -7846887104148029590L, + 4210795945791252618L, + -8879516722990993098L, + -2621063563373927241L, + 2094675051444850863L, + -8681225697045319537L, + 6072534474938492189L, + 6181923696407824226L, + 5463607676777614919L, + 3708342890820711111L, + 8844501223821777366L, + -1459359143442302680L, + 2225439088478089068L, + -3866259492807347627L, + 5715020051188773955L, + 3922300588924895992L, + -9142841818158905228L, + 2234845285375211931L, + 2466598091809457099L, + -5086614780930363190L, + -59740786891006359L, + 3484340182077240897L, + 5684798394905475931L, + 8492255409537329167L, + 5276601975076232447L, + -723955912320185993L, + 9032937149732310432L, + 2226206333274026280L, + 5631303328800272036L, + 3943832708526382713L, + -3756282686478033644L, + -5407377327559185078L, + 2025162219823732106L, + -8802502232162774782L, + 9039368856081455195L, + 663058667658971174L, + 3624269418844967319L, + 1835338408542062149L, + 6821836507221295281L, + 6273547355770435776L, + -3104373869480308814L, + 1150888014781722836L, + 7638478751521711777L, + -6407096352658729423L, + -2242514077180426481L, + -3181824045541296523L, + -4562287221569080073L, + -5550768647534615669L, + -5786611484859469238L, + -6147722345444149090L, + 3737249616177808079L, + 3401215612108618403L, + -713522925214097648L, + 7938558781452631257L, + -2822931074351003413L, + -6484774850345918944L, + 3384659068511379086L, + 6976459554734427695L, + 4254162229878558339L, + -3312164339867139602L, + 7263045146222903358L, + 4561625003713187235L, + -3350421200373539593L, + -6329267008823047447L, + -6889593333717619051L, + -6470291206680780949L, + -1925391510610223335L, + 4955720513801530785L, + -6515999401129420095L, + -5146900596178823847L, + 2572121582663686783L, + -4958678197003031937L, + -1295912792184970105L, + -8320363273488883198L, + -8213501149234986129L, + -3883775881968950160L, + -8925953418077243474L, + 3199784299548492168L, + -6836506744583692202L, + -5007347279129330642L, + 7387675960164975441L, + -5841389805259238070L, + 6263589037534776610L, + 3327727201189139791L, + 3673450414312153409L, + -1563909967243907088L, + -3758518049401683145L, + 6368282934319908146L, + -6025191831649813215L, + 1223512633484628943L, + -8540335264335924099L, + -8569704496403127098L, + -5712355262561236939L, + -6468621715016340600L, + 7015005898276272746L, + -1037164971883038884L, + -6108649908647520338L, + -6781540054819591698L, + -2762739023866345855L, + -270930832663123436L, + -2387080926579956105L, + -3984603512651136889L, + 2367015942733558542L, + 2997123688964990405L, + -424413420483149165L, + 2906467516125124288L, + 7979917630945955701L, + 2879736983084566817L, + 558436267366797870L, + 6471658168855475843L, + -3453803644372811678L, + 95470628886709014L, + 5666911245054448862L, + 1594133734978640945L, + 3790246368687946045L, + 8636400206261643605L, + 5901994795106283147L, + -6774812279971490610L, + -4622588246534854941L, + 5395884908872287278L, + 7381412950348018556L, + 5461775216423433041L, + 2851500852422732203L, + 1153428834012773824L, + 2567326223464897798L, + 6290362916558214218L, + 6095765709335097474L, + -3526424734043456674L, + -8411274175041022530L, + 7565408328520233290L, + -1318636864706103626L, + 1261242784453012654L, + -472643963000448611L, + -7126293899612852456L, + 5072187962931197668L, + 4775251504230927816L, + -1624676500499667689L, + 2252385971292411863L, + 7908437759266752884L, + -8948829914565397845L, + 5258787823809553293L, + 3885696202809019506L, + -4551784314460062669L, + 5315762970089305011L, + 7218180419200466576L, + 109471822471146966L, + 3901499100759315793L, + -5613018173558603696L, + 5782419706003468119L, + 8285176821902721729L, + -2944182278904878473L, + 8089487615165958290L, + 6934039118340963316L, + 8481603619533191729L, + -6321491167299496492L, + 6441589800192421521L, + 6436057639713571196L, + 6819921695214365155L, + 1185928916708893611L, + 2597068862418243401L, + -7637601550649263782L, + 9129303862479379164L, + 4047905726243458335L, + 6672087858539795207L, + -4841432774404255351L, + 5501215987763227677L, + -5300305896512100453L, + 1635946349436492617L, + -5017459781050596604L, + -7313558338536196566L, + 4625509831332846264L, + -1241826701278444028L, + 2916178164108211239L, + -6947453283344846915L, + 5520544791845620925L, + 5009241392834567026L, + -630825152277572403L, + 6246654103747517292L, + -5632205909016659384L, + -5099826214945383802L, + 2466330894206710401L, + -1463559257726812272L, + 4922422449110036517L, + -4940410396057186660L, + 8835766963654337957L, + -1984334093384497740L, + 5616151800825184227L, + -8442970605804311782L, + -5396399970392474268L, + 2711274356126287353L, + -5090439840321959043L, + 6638617029380445409L, + -6424875729377006548L, + -7243574969986334324L, + -904268348341193502L, + -6196811069886893217L, + -7742123331454617135L, + 1449632469607275832L, + 3212140938119717436L, + 8676942774083692265L, + -6625590425417154859L, + 8720904664575676760L, + 9151723732605931383L, + 7642401923610349184L, + -3454390566366389884L, + -232373658792783206L, + -8933620623437682010L, + 2514068248201398743L, + 6757007617821370359L, + -2870340646674679163L, + 416331333845426881L, + -5319172016123138702L, + 3294412564645954555L, + 2812538484970453169L, + -9128349093860081905L, + 6784456254618976198L, + -2861881330654872638L, + 3912429093271518508L, + -2562542119887175820L, + 4835616088583228965L, + 427639171891209425L, + 2590582080178010045L, + -6288067880951692635L, + -3204510905067065501L, + 9008426291442999873L, + -4085962609397876083L, + -3786041297813905157L, + -6006475053574578261L, + -6174022276199807178L, + 7958957647277035097L, + 2915785807118517755L, + 2139592530283433011L, + -8562048562533248017L, + -4991735207930685025L, + 393144860250454082L, + -5852177196425420458L, + -2652303154023739579L, + 2079679586901234739L, + -1386526064824772584L, + 1574420554361329695L, + -855542130447493508L, + 8291940350733154044L, + -5330200233059892402L, + 5140782607921164290L, + -977254437067235218L, + -261520846651909307L, + -7369143208070837455L, + -4728766390712852111L, + -8572213434879266955L, + -6754813768712497692L, + 7946121307356573089L, + 504268959085012646L, + -5536654029698676818L, + -6021520522792328781L, + 6968613512520500871L, + 4029920623217569312L, + 2738878342460920492L, + 4562432005481165726L, + -1279037845195368028L, + 1746645308450474697L, + 2538150989161378915L, + 2012299649948738944L, + -3997559675475377347L, + -5939431505669672858L, + 2077103722387383456L, + -6188261335534632204L, + 8772504603740967633L, + -1653698997940568281L, + 1676948989756529271L, + 2377579815165102226L, + -2667481192445387240L, + -5498860615033631762L, + -2490865541169744469L, + -1233441883399707566L, + 5445263795307566596L, + 2288458809413275798L, + -5908274826918996877L, + 2909363406069168415L, + 2376032171261335687L, + -5215189045919902574L, + -6083327007632847329L, + 2462785604224107327L, + -6684045035730714275L, + 2409356208468676804L, + 2814747114160772803L, + -4529204412661254980L, + -8437511853472556883L, + 1819323657613892915L, + 6862685309651627151L, + -9210337863564319258L, + -3641041551811494963L, + -6791020794026796740L, + -5261661996953805298L, + -1953516254626596632L, + -5901591005960707793L, + -7413695905040596911L, + 2952256922297384020L, + -8427771021447591769L, + -6920139339436245233L, + 2967149838604559395L, + -3253499104068010353L, + -8473804925120692039L, + -3561285603521886085L, + -4453849179065102447L, + 2050092642498054323L, + -5626434133619314199L, + 7995075368278704248L, + 7685996432951370136L, + -8037783900933102779L, + 4601459625295412851L, + -4491938778497306775L, + -9089886217821142309L, + -3947191644612298897L, + 1364225714229764884L, + 2580394324892542249L, + -3765315378396862242L, + 6023794482194323576L, + -662753714084561214L, + 3080495347149127717L, + 911710215008202776L, + -803705685664586056L, + -6101059689379533503L, + -2122356322512227634L, + 8012110874513406695L, + -4158551223425336367L, + 8282080141813519654L, + 4172879384244246799L, + 708522065347490110L, + -6997269001146828181L, + 1887955086977822594L, + 8014460039616323415L + }; + + public static final long[] HASHES_OF_LOOPING_BYTES_WITH_SEED_42 = { + -7444071767201028348L, + -8959994473701255385L, + 7116559933691734543L, + 6019482000716350659L, + -6625277557348586272L, + -5507563483608914162L, + 1540412690865189709L, + 4522324563441226749L, + -7143238906056518746L, + -7989831429045113014L, + -7103973673268129917L, + -2319060423616348937L, + -7576144055863289344L, + -8903544572546912743L, + 6376815151655939880L, + 5913754614426879871L, + 6466567997237536608L, + -869838547529805462L, + -2416009472486582019L, + -3059673981515537339L, + 4211239092494362041L, + 1414635639471257331L, + 166863084165354636L, + -3761330575439628223L, + 3524931906845391329L, + 6070229753198168844L, + -3740381894759773016L, + -1268276809699008557L, + 1518581707938531581L, + 7988048690914090770L, + -4510281763783422346L, + -8988936099728967847L, + -8644129751861931918L, + 2046936095001747419L, + 339737284852751748L, + -8493525091666023417L, + -3962890767051635164L, + -5799948707353228709L, + -6503577434416464161L, + 7718729912902936653L, + 191197390694726650L, + -2677870679247057207L, + 20411540801847004L, + 2738354376741059902L, + -3754251900675510347L, + -3208495075154651980L, + 5505877218642938179L, + 6710910171520780908L, + -9060809096139575515L, + 6936438027860748388L, + -6675099569841255629L, + -5358120966884144380L, + -4970515091611332076L, + -1810965683604454696L, + -516197887510505242L, + 1240864593087756274L, + 6033499571835033332L, + 7223146028771530185L, + 909128106589125206L, + 1567720774747329341L, + -1867353301780159863L, + 4655107429511759333L, + 5356891185236995950L, + 182631115370802890L, + -3582744155969569138L, + 595148673029792797L, + 495183136068540256L, + 5536689004903505647L, + -8472683670935785889L, + -4335021702965928166L, + 7306662983232020244L, + 4285260837125010956L, + 8288813008819191181L, + -3442351913745287612L, + 4883297703151707194L, + 9135546183059994964L, + 123663780425483012L, + 509606241253238381L, + 5940344208569311369L, + -2650142344608291176L, + 3232776678942440459L, + -922581627593772181L, + 7617977317085633049L, + 7154902266379028518L, + -5806388675416795571L, + 4368003766009575737L, + -2922716024457242064L, + 4771160713173250118L, + 3275897444752647349L, + -297220751499763878L, + 5095659287766176401L, + 1181843887132908826L, + 9058283605301070357L, + 3984713963471276643L, + 6050484112980480005L, + 1551535065359244224L, + 565337293533335618L, + 7412521035272884309L, + -4735469481351389369L, + 6998597101178745656L, + -9107075101236275961L, + 5879828914430779796L, + 6034964979406620806L, + 5666406915264701514L, + -4666218379625258428L, + 2749972203764815656L, + -782986256139071446L, + 6830581400521008570L, + 2588852022632995043L, + -5484725487363818922L, + -3319556935687817112L, + 6481961252981840893L, + 2204492445852963006L, + -5301091763401031066L, + -2615065677047206256L, + -6769817545131782460L, + -8421640685322953142L, + -3669062629317949176L, + -9167016978640750490L, + 2783671191687959562L, + -7599469568522039782L, + -7589134103255480011L, + -5932706841188717592L, + -8689756354284562694L, + -3934347391198581249L, + -1344748563236040701L, + 2172701592984478834L, + -5322052340624064417L, + -8493945390573620511L, + 3349021988137788403L, + -1806262525300459538L, + -8091524448239736618L, + 4022306289903960690L, + -8346915997379834224L, + -2106001381993805461L, + -5784123934724688161L, + 6775158099649720388L, + -3869682756870293568L, + 4356490186652082006L, + 8469371446702290916L, + -2972961082318458602L, + -7188106622222784561L, + -4961006366631572412L, + 3199991182014172900L, + 2917435868590434179L, + 8385845305547872127L, + 7706824402560674655L, + -1587379863634865277L, + -4212156212298809650L, + -1305209322000720233L, + -7866728337506665880L, + 8195089740529247049L, + -4876930125798534239L, + 798222697981617129L, + -2441020897729372845L, + -3926158482651178666L, + -1254795122048514130L, + 5192463866522217407L, + -5426289318796042964L, + -3267454004443530826L, + 471043133625225785L, + -660956397365869974L, + -6149209189144999161L, + -2630977660039166559L, + 8512219789663151219L, + -3309844068134074620L, + -6211275327487847132L, + -2130171729366885995L, + 6569302074205462321L, + 4855778342281619706L, + 3867211421508653033L, + -3002480002418725542L, + -8297543107467502696L, + 8049642289208775831L, + -5439825716055425635L, + 7251760070798756432L, + -4774526021749797528L, + -3892389575184442548L, + 5162451061244344424L, + 6000530226398686578L, + -5713092252241819676L, + 8740913206879606081L, + -8693282419677309723L, + 1576205127972543824L, + 5760354502610401246L, + 3173225529903529385L, + 1785166236732849743L, + -1024443476832068882L, + -7389053248306187459L, + 1171021620017782166L, + 1471572212217428724L, + 7720766400407679932L, + -8844781213239282804L, + -7030159830170200877L, + 2195066352895261150L, + 1343620937208608634L, + 9178233160016731645L, + -757883447602665223L, + 3303032934975960867L, + -3685775162104101116L, + -4454903657585596656L, + -5721532367620482629L, + 8453227136542829644L, + 5397498317904798888L, + 7820279586106842836L, + -2369852356421022546L, + 3910437403657116169L, + 6072677490463894877L, + -2651044781586183960L, + 5173762670440434510L, + -2970017317595590978L, + -1024698859439768763L, + -3098335260967738522L, + -1983156467650050768L, + -8132353894276010246L, + -1088647368768943835L, + -3942884234250555927L, + 7169967005748210436L, + 2870913702735953746L, + -2207022373847083021L, + 1104181306093040609L, + 5026420573696578749L, + -5874879996794598513L, + -4777071762424874671L, + -7506667858329720470L, + -2926679936584725232L, + -5530649174168373609L, + 5282408526788020384L, + 3589529249264153135L, + -6220724706210580398L, + -7141769650716479812L, + 5142537361821482047L, + -7029808662366864423L, + -6593520217660744466L, + 1454581737122410695L, + -139542971769349865L, + 1727752089112067235L, + -775001449688420017L, + -5011311035350652032L, + -8671171179275033159L, + -2850915129917664667L, + -5258897903906998781L, + -6954153088230718761L, + -4070351752166223959L, + -6902592976462171099L, + -7850366369290661391L, + -4562443925864904705L, + 3186922928616271015L, + 2208521081203400591L, + -2727824999830592777L, + -3817861137262331295L, + 2236720618756809066L, + -4888946967413746075L, + -446884183491477687L, + -43021963625359034L, + -5857689226703189898L, + -2156533592262354883L, + -2027655907961967077L, + 7151844076490292500L, + -5029149124756905464L, + 526404452686156976L, + 8741076980297445408L, + 7962851518384256467L, + -105985852299572102L, + -2614605270539434398L, + -8265006689379110448L, + 8158561071761524496L, + -6923530157382047308L, + 5551949335037580397L, + 565709346370307061L, + -4780869469938333359L, + 6931895917517004830L, + 565234767538051407L, + -8663136372880869656L, + 1427340323685448983L, + 6492705666640232290L, + 1481585578088475369L, + -1712711110946325531L, + 3281685342714380741L, + 6441384790483098576L, + -1073539554682358394L, + 5704050067194788964L, + -5495724689443043319L, + -5425043165837577535L, + 8349736730194941321L, + -4123620508872850061L, + 4687874980541143573L, + -468891940172550975L, + -3212254545038049829L, + -6830802881920725628L, + 9033050533972480988L, + 4204031879107709260L, + -677513987701096310L, + -3286978557209370155L, + 1644111582609113135L, + 2040089403280131741L, + 3323690950628902653L, + -7686964480987925756L, + -4664519769497402737L, + 3358384147145476542L, + -4699919744264452277L, + -4795197464927839170L, + 5051607253379734527L, + -8987703459734976898L, + 8993686795574431834L, + -2688919474688811047L, + 375938183536293311L, + 1049459889197081920L, + -1213022037395838295L, + 4932989235110984138L, + -6647247877090282452L, + -7698817539128166242L, + -3264029336002462659L, + 6487828018122309795L, + -2660821091484592878L, + 7104391069028909121L, + -1765840012354703384L, + 85428166783788931L, + -6732726318028261938L, + 7566202549055682933L, + 229664898114413280L, + -1474237851782211353L, + -1571058880058007603L, + -7926453582850712144L, + 2487148368914275243L, + 8740031015380673473L, + 1908345726881363169L, + -2510061320536523178L, + 7854780026906019630L, + -6023415596650016493L, + -6264841978089051107L, + 4024998278016087488L, + -4266288992025826072L, + -3222176619422665563L, + -1999258726038299316L, + 1715270077442385636L, + 6764658837948099754L, + -8646962299105812577L, + -51484064212171546L, + -1482515279051057493L, + -8663965522608868414L, + -256555202123523670L, + 1973279596140303801L, + -7280796173024508575L, + -5691760367231354704L, + -5915786562256300861L, + -3697715074906156565L, + 3710290115318541949L, + 6796151623958134374L, + -935299482515386356L, + -7078378973978660385L, + 5379481350768846927L, + -9011221735308556302L, + 5936568631579608418L, + -6060732654964511813L, + -4243141607840017809L, + 3198488845875349355L, + -7809288876010447646L, + 4371587872421472389L, + -1304197371105522943L, + 7389861473143460103L, + -1892352887992004024L, + 2214828764044713398L, + 6347546952883613388L, + 1275694314105480954L, + -5262663163358903733L, + 1524757505892047607L, + 1474285098416162746L, + -7976447341881911786L, + 4014100291977623265L, + 8994982266451461043L, + -7737118961020539453L, + -2303955536994331092L, + 1383016539349937136L, + 1771516393548245271L, + -5441914919967503849L, + 5449813464890411403L, + -3321280356474552496L, + 4084073849712624363L, + 4290039323210935932L, + 2449523715173349652L, + 7494827882138362156L, + 9035007221503623051L, + 5722056230130603177L, + -5443061851556843748L, + -7554957764207092109L, + 447883090204372074L, + 533916651576859197L, + -3104765246501904165L, + -4002281505194601516L, + -8402008431255610992L, + -408273018037005304L, + 214196458752109430L, + 6458513309998070914L, + 2665048360156607904L, + 96698248584467992L, + -3238403026096269033L, + 6759639479763272920L, + -4231971627796170796L, + -2149574977639731179L, + -1437035755788460036L, + -6000005629185669767L, + 145244292800946348L, + -3056352941404947199L, + 3748284277779018970L, + 7328354565489106580L, + -2176895260373660284L, + 3077983936372755601L, + 1215485830019410079L, + 683050801367331140L, + -3173237622987755212L, + -1951990779107873701L, + -4714366021269652421L, + 4934690664256059008L, + 1674823104333774474L, + -3974408282362828040L, + 2001478896492417760L, + -4115105568354384199L, + -2039694725495941666L, + -587763432329933431L, + -391276713546911316L, + -5543400904809469053L, + 1882564440421402418L, + -4991793588968693036L, + 3454088185914578321L, + 2290855447126188424L, + 3027910585026909453L, + 2136873580213167431L, + -6243562989966916730L, + 5887939953208193029L, + -3491821629467655741L, + -3138303216306660662L, + 8572629205737718669L, + 4154439973110146459L, + 5542921963475106759L, + -2025215496720103521L, + -4047933760493641640L, + -169455456138383823L, + -1164572689128024473L, + -8551078127234162906L, + -7247713218016599028L, + 8725299775220778242L, + 6263466461599623132L, + 7931568057263751768L, + 7365493014712655238L, + -7343740914722477108L, + 8294118602089088477L, + 7677867223984211483L, + -7052188421655969232L, + -3739992520633991431L, + 772835781531324307L, + 881441588914692737L, + 6321450879891466401L, + 5682516032668315027L, + 8493068269270840662L, + -3895212467022280567L, + -3241911302335746277L, + -7199586338775635848L, + -4606922569968527974L, + -806850906331637768L, + 2433670352784844513L, + -5787982146811444512L, + 7852193425348711165L, + 8669396209073850051L, + -6898875695148963118L, + 6523939610287206782L, + -8084962379210153174L, + 8159432443823995836L, + -2631068535470883494L, + -338649779993793113L, + 6514650029997052016L, + 3926259678521802094L, + 5443275905907218528L, + 7312187582713433551L, + -2993773587362997676L, + -1068335949405953411L, + 4499730398606216151L, + 8538015793827433712L, + -4057209365270423575L, + -1504284818438273559L, + -6460688570035010846L, + 1765077117408991117L, + 8278320303525164177L, + 8510128922449361533L, + 1305722765578569816L, + 7250861238779078656L, + -576624504295396147L, + -4363714566147521011L, + -5932111494795524073L, + 1837387625936544674L, + -4186755953373944712L, + -7657073597826358867L, + 140408487263951108L, + 5578463635002659628L, + 3400326044813475885L, + -6092804808386714986L, + -2410324417287268694L, + 3222007930183458970L, + 4932471983280850419L, + 3554114546976144528L, + -7216067928362857082L, + -6115289896923351748L, + -6769646077108881947L, + 4263895947722578066L, + 2939136721007694271L, + 1426030606447416658L, + -1316192446807442076L, + 5366182640480055129L, + 6527003877470258527L, + 5849680119000207603L, + 5263993237214222328L, + -6936533648789185663L, + -9063642143790846605L, + 3795892210758087672L, + 4987213125282940176L, + 2505500970421590750L, + -1014022559552365387L, + -3574736245968367770L, + 1180676507127340259L, + -2261908445207512503L, + -8416682633172243509L, + 1114990703652673283L, + 7753746660364401380L, + 1874908722469707905L, + 2033421444403047677L, + 21412168602505589L, + 385957952615286205L, + 2053171460074727107L, + 1915131899400103774L, + 6680879515029368390L, + 568807208929724162L, + -6211541450459087674L, + -5026690733412145448L, + 1384781941404886235L, + -98027820852587266L, + 1806580495924249669L, + 6322077317403503963L, + 9078162931419569939L, + -2809061215428363978L, + 7697867577577415733L, + -5270063855897737274L, + 5649864555290587388L, + -6970990547695444247L, + 579684606137331754L, + 3871931565451195154L, + 2030008578322050218L, + -5012357307111799829L, + -2271365921756144065L, + 4551962665158074190L, + -3385474923040271312L, + -7647625164191633577L, + 6634635380316963029L, + -5201190933687061585L, + 8864818738548593973L, + 2855828214210882907L, + 9154512990734024165L, + -6945306719789457786L, + 1200243352799481087L, + 875998327415853787L, + 1275313054449881011L, + -6105772045375948736L, + -2926927684328291437L, + 9200050852144954779L, + 5188726645765880663L, + 5197037323312705176L, + 3434926231010121611L, + -5054013669361906544L, + 2582959199749224670L, + -6053757512723474059L, + -5016308176846054473L, + -2509827316698626133L, + 7700343644503853204L, + -1997627249894596731L, + 3993168688325352290L, + -8181743677541277704L, + 3719056119682565597L, + -7264411659282947790L, + 7177028972346484464L, + -5460831176884283278L, + 1799904662416293978L, + -6549616005092764514L, + 5472403994001122052L, + 8683463751708388502L, + -7873363037838316398L, + 689134758256487260L, + -1287443614028696450L, + 4452712919702709507L, + 762909374167538893L, + 6594302592326281411L, + 1183786629674781984L, + 5021847859620133476L, + -2490098069181538915L, + 5105145136026716679L, + 4437836948098585718L, + 1987270426215858862L, + 6170312798826946249L, + 634297557126003407L, + -1672811625495999581L, + 6282971595586218191L, + 4549149305727581687L, + -5652165370435317782L, + 1064501550023753890L, + -5334885527127139723L, + -6904378001629481237L, + -1807576691784201230L, + -205688432992053911L, + 7621619053293393289L, + 6258649161313982470L, + -1111634238359342096L, + -8044260779481691987L, + 400270655839010807L, + -7806833581382890725L, + -2970563349459508036L, + -7392591524816802798L, + 2918924613160219805L, + -6444161627929149002L, + 6096497501321778876L, + -1477975665655830038L, + 1690651307597306138L, + -2364076888826085362L, + -6521987420014905821L, + -4419193480146960582L, + 3538587780233092477L, + 8374665961716940404L, + 7492412312405424500L, + 6311662249091276767L, + -1240235198282023566L, + 5478559631401166447L, + 3476714419313462133L, + 377427285984503784L, + 2570472638778991109L, + -2741381313777447835L, + -7123472905503039596L, + 2493658686946955193L, + 1024677789035847585L, + -2916713904339582981L, + -4532003852004642304L, + -2202143560366234111L, + 5832267856442755135L, + -261740607772957384L, + 239435959690278014L, + 5755548341947719409L, + 6138795458221887696L, + -7709506987360146385L, + -6657487758065140444L, + -7006376793203657499L, + 6544409861846502033L, + 3171929352014159247L, + 1051041925048792869L, + 2617300158375649749L, + 952652799620095175L, + -576661730162168147L, + -1634191369221345988L, + 4833656816115993519L, + 647566759700005786L, + 2473810683785291822L, + 3005977181064745326L, + -3321881966853149523L, + 7595337666427588699L, + 6004093624251057224L, + -563917505657690279L, + 6117428527147449302L, + -6287297509522976113L, + -4527219334756214406L, + 742626429298092489L, + 3057351806086972041L, + 645967551210272605L, + -4428701157828864227L, + 3236379103879435414L, + -8477089892132066300L, + -6127365537275859058L, + -4052490484706946358L, + -8004854976625046469L, + -3679456917426613424L, + -8212793762082595299L, + -818288739465424130L, + 1358812099481667095L, + 7835987612195254310L, + -3663247409614323059L, + -2931105150130396604L, + 7296136776835614792L, + -2014557408985889628L, + 7267662411237959788L, + 3699280615819277743L, + -212010675469091396L, + -6518374332458360120L, + 145026010541628849L, + 1879297324213501001L, + -7146296067751816833L, + -5002958800391379931L, + 6060682439924517608L, + -432234782921170964L, + -6669688947353256956L, + 7728943532792041267L, + 830911367341171721L, + 3396934884314289432L, + -779464156662780749L, + 2330041851883352285L, + -4783350380736276693L, + -5758476056890049254L, + -7551552301614791791L, + 1253334187723911710L, + -2685018208308798978L, + 5379636036360946454L, + 6154668487114681217L, + -8641287462255458898L, + 4676087643800649558L, + -2405142641398691475L, + 1088685126864246881L, + 6431149082338374041L, + -607357695335069155L, + -720970692129524140L, + 2648766932394044468L, + 8408344790179354573L, + -6193808387735667350L, + 7722524628524697419L, + -6975433852560238120L, + -2925851029234475295L, + -4274458387165211028L, + -8355836377702147319L, + 5278146397877332061L, + 8502098812383680707L, + 2292836642336580326L, + -6127608082651070062L, + 2222301962240611208L, + -1930887695854799378L, + 7640503480494894592L, + 1162652186586436094L, + -1918002592943761683L, + 7648998601717261840L, + -8472603250832757057L, + -988877663117552456L, + 2368458128168026494L, + -6480813811998475245L, + -5896967824416018967L, + -2593783161701820446L, + 6950098417530252598L, + 6362589545555771236L, + 7981389665448567125L, + 3954017080198558850L, + 1626078615050230622L, + 6650159066527969109L, + 697345338922935394L, + -1226816215461768626L, + 8740408765973837440L, + -4194155864629568323L, + 7016680023232424746L, + 6043281358142429469L, + -4201005667174376809L, + 1216727117859013155L, + 6367202436544203935L, + 35414869396444636L, + 3715622794033998412L, + 488654435687670554L, + -2503747297224687460L, + 3147101919441470388L, + -8248611218693190922L, + 970697264481229955L, + 3411465763826851418L, + 9117405004661599969L, + -5204346498331519734L, + -19637460819385174L, + -5039124225167977219L, + 2990108874601696668L, + -2623857460235459202L, + 4256291692861397446L, + 6724147860870760443L, + 3558616688507246537L, + 6487680097936412800L, + -6470792832935928161L, + 4314814550912237614L, + -1292878983006062345L, + 6791915152630414174L, + 5971652079925815310L, + 2557529546662864312L, + 466175054322801580L, + -585216717310746872L, + -2486640422147349036L, + 7212029603994220134L, + 3958995069888972500L, + 4950471855791412790L, + -3721948842035712763L, + -6184503487488243051L, + 4079570444585775332L, + -3952156172546996872L, + 4543894231118208322L, + -1739995588466209963L, + 9155948355455935530L, + 5821980345462207860L, + -2431287667309520417L, + -3890108130519441316L, + -558124689277030490L, + 6079823537335801717L, + 5409742395192364262L, + -2329885777717160453L, + -7332804342513677651L, + 1466490574975950555L, + -420549419907427929L, + -5249909814389692516L, + -5145692168206210661L, + 5934113980649113921L, + 3241618428555359661L, + -6622110266160980250L, + 5048250878669516223L, + 5747219637359976174L, + 2975906212588223728L, + 5730216838646273215L, + -176713127129024690L, + 6734624279336671146L, + 5127866734316017180L, + 7111761230887705595L, + 3457811808274317235L, + 3362961434604932375L, + -1877869936854991246L, + 7171428594877765665L, + -8252167178400462374L, + -6306888185035821047L, + -6684702191247683887L, + -7754928454824190529L, + -1902605599135704386L, + -4037319846689421239L, + 8493746058123583457L, + -8156648963857047193L, + 2051510355149839497L, + -1256416624177218909L, + -3344927996254072010L, + -1838853051925943568L, + 316927471680974556L, + -1502257066700798003L, + -5836095610125837606L, + -1594125583615895424L, + 1442211486559637962L, + -144295071206619569L, + 5159850900959273410L, + 4589139881166423678L, + -7038726987463097509L, + 2886082400772974595L, + 2780759114707171916L, + 5694649587906297495L, + 1260349041268169667L, + 4921517488271434890L, + 644696475796073018L, + 6262811963753436289L, + -6128198676595868773L, + -3625352083004760261L, + -8751453332943236675L, + 8749249479868749221L, + -2450808199545048250L, + -6517435817046180917L, + -3433321727429234998L, + -2591586258908763451L, + 3847750870868804507L, + 6603614438546398643L, + -7598682191291031287L, + 8710261565627204971L, + 4753389483755344355L, + -4645333069458786881L, + -6742695046613492214L, + 643070478568866643L, + -7543096104151965610L, + 7171495384655926161L, + 595063872610714431L, + 3292310150781130424L, + 4326847806055440904L, + -4580020566072794152L, + 3142286571820373678L, + 5530356537440155930L, + 546372639737516181L, + 7401214477400367500L, + 7406531960402873109L, + 3287639667219172570L, + 4977301681213633671L, + 5253257820925174498L, + 2906216636104297878L, + 6142955758238347523L, + -3498651268741727235L, + -5875053958265588593L, + 3896719087169993883L, + -910904726885775073L, + 380107493197368177L, + -4993591912695447004L, + 2970487257212582761L, + 2551762717569548774L, + 953061649962736812L, + 8949739538606589463L, + -2962839167079475801L, + -1375673191272573835L, + 3761793818361866390L, + -389577789190726878L, + 5661262051502180269L, + -6558556411143987683L, + -702798336372315031L, + -336662820551371779L, + 998576401126580155L, + -5945021269112582755L, + 6108533925730179871L, + 2207095297001999618L, + -9042779159998880435L, + -6177868444342118372L, + 6775965402605895077L, + -3788428885163306576L, + 7790055010527190387L, + 3581587652196995358L, + -6176354155561607694L, + -5859381340906321207L, + 395898765763528395L, + 8132967590863909348L, + -3329092504090544483L, + -6785855381158040247L, + 1497218517051796750L, + -5352392845588925911L, + -6271364901230559194L, + 2314830370653350118L, + -7617588269001325450L, + 1423166885758213795L, + 8538612578307869519L, + -61918791718295474L, + -8177103503192338593L, + -4740086042584326695L, + 3677931948215558698L, + 6558856291580149558L, + 2674975452453336335L, + 5133796555646930522L, + 5139252693299337100L, + 7949476871295347205L, + 4407815324662880678L, + -3758305875280581215L, + 6066309507576587415L, + -7368508486398350973L, + -3181640264332856492L, + 6905100869343314145L, + 3677177673848733417L, + 8862933624870506941L, + -8575223195813810568L, + 9178470351355678144L, + 4677809017145408358L, + -1194833416287894989L, + 3436364743255571183L, + -5204770725795363579L, + 560599448536335263L, + -3192077522964776200L, + -751575299648803575L, + 6334581746534596579L, + -8358187891202563300L, + -1462480609823525055L, + 5605961062646987941L, + 4968399805931440889L, + 7968693270782626653L, + -5868205923557518188L, + 1830234928743560617L, + -8435261076693154407L, + 2138416970728681332L, + 8088740745199685138L, + 806532400344230520L, + 1800590379902909333L, + -8909128842071238901L, + -7357495566969170860L, + 3679766664126940553L, + 2060050474865839094L, + 2363972840121763414L, + 525695004292982714L, + -1224842191746529593L, + 7011317848855545003L, + -6337167558180299938L, + -5184688833363785939L, + -8426673387248359061L, + -5035438815930785229L, + 3521810320608058994L, + 4803742557254962242L, + 6623527039545786598L, + -1221475882122634738L, + -3344794405518401087L, + 6510298498414053658L, + 2844753907937720338L, + 90502309714994895L, + -750403235344282494L, + -4825474181021465833L, + -3405519947983849510L, + 3503875590944089793L, + 7286294700691822468L, + 7828126881500292486L, + 8437899353709338096L, + 136052254470293480L, + 1113259077339995086L, + -8244887265606191121L, + 8089569503800461649L, + -1429698194850157567L, + 1575595674002364989L, + 3576095286627428675L, + -7653655285807569222L, + -6053506977362539111L, + -3923855345805787169L, + -8001149080454232377L, + -4382867706931832271L, + 4212860258835896297L, + 4207674254247034014L, + 5519424058779519159L, + -754483042161434654L, + 1434113479814210082L, + -6416645032698336896L, + 5624329676066514819L, + -8229557208322175959L, + 3922640911653270376L, + 7826932478782081910L, + -4862787164488635842L, + 1449234668827944573L, + -1781657689570106327L, + 5442827552725289699L, + 3589862161007644641L, + 4787115581650652778L, + -3512152721942525726L, + -6750103117958685206L, + 5012970446659949261L, + 6797752795961689017L, + 5086454597639943700L, + -7616068364979994076L, + 1492846825433110217L, + 2967476304433704510L, + -8413824338284112078L, + -1319049442043273974L, + -1756090916806844109L, + -9061091728950139525L, + -6864767830358160810L, + 4879532090226251157L, + 5528644708740739488L + }; +} \ No newline at end of file diff --git a/mmap-queue/src/test/java/org/tools4j/mmap/queue/impl/AppenderIdPoolTest.java b/mmap-region/src/test/java/org/tools4j/mmap/region/impl/IdPoolTest.java similarity index 100% rename from mmap-queue/src/test/java/org/tools4j/mmap/queue/impl/AppenderIdPoolTest.java rename to mmap-region/src/test/java/org/tools4j/mmap/region/impl/IdPoolTest.java diff --git a/settings.gradle b/settings.gradle index d4019aa..f9c92c7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ include ( 'mmap-region', - 'mmap-queue') \ No newline at end of file + 'mmap-queue', + 'mmap-dictionary') \ No newline at end of file